☰ فهرست مستندات · بررسی توکن روی سرور
بررسی توکن کاربر روی سرور شما
توکنی را که اپ به سرور خودتان میفرستد بررسی کنید: امضای ES256 با کلیدهای عمومی ابرووا (JWKS در /.well-known/jwks.json)، صادرکننده، مخاطب، زمان انقضا و پروژهی شما. همان مدل verifyIdToken در Firebase، با هر کتابخانهی استاندارد JWT در Node.js، PHP، پایتون یا Go.
اپ شما API خودتان را صدا میزند، مثلاً برای ثبت سفارش. سرور شما باید بداند چه کسی درخواست داده. اپ توکن کاربر واردشده را میفرستد و سرور شما آن را با کلیدهای عمومی ابرووا بررسی میکند. نه برای هر درخواست تماسی با ابرووا لازم است، نه رمز مشترکی: همان مدل verifyIdToken در Firebase.
چطور کار میکند#
هر توکن کاربر ابرووا یک JWT است که با ES256 امضا شده. ابرووا کلیدهای عمومی بررسی امضا را به شکل استاندارد JWKS (RFC 7517) منتشر میکند:
https://auth.abrova.ir/.well-known/jwks.json
آنجا فقط کلید عمومی هست. کلید خصوصی هیچوقت از ابرووا بیرون نمیرود، پس کسی نمیتواند توکن بسازد، ولی همه میتوانند توکن را بررسی کنند. هر کلید یک شناسه (kid) دارد و هر توکن kid کلیدی را که امضایش کرده میگوید. وقتی ابرووا کلیدش را عوض میکند، کلید تازه پیش از آنکه توکنی با آن امضا شود منتشر میشود و کلید قبلی دستکم یک روز بعد هم منتشر میماند؛ پس سروری که کلیدها را ۱۰ دقیقه نگه میدارد هیچ کلیدی را از دست نمیدهد.
کلیدها از داخل ایران ارائه میشوند، پس سرور شما هنگام قطع اینترنت بینالملل هم میتواند توکنها را بررسی کند.
مرحله ۱: فرستادن توکن از اپ#
توکن فعلی را در اپ بخوانید و در سرآیند Authorization بفرستید:
| پلتفرم | خواندن توکن |
|---|---|
| Flutter | await auth.getToken() |
| iOS | try await auth.getAccessToken() |
| Android | auth.getAccessToken() |
| Web، React Native | await auth.getToken() |
GET /api/orders Authorization: Bearer eyJhbGciOiJFUzI1NiIs...
SDK توکن را پیش از انقضا تمدید میکند، پس آن را برای هر درخواست بخوانید و نسخهای نگه ندارید. API خودتان را همیشه با HTTPS صدا بزنید.
مرحله ۲: بررسی روی سرور#
یک کلید توکنهای همهی پروژههای ابرووا را امضا میکند. توکنی با امضای درست هم ممکن است مال کاربر اپ دیگری باشد. همیشه بررسی کنید که pid شناسهی پروژهی شما باشد (کنسول › تنظیمات پروژه › عمومی).
Node.js: SDK مدیریتی#
با @abrova/auth-admin نسخهی ۰.۲.۰ یا بالاتر، یک فراخوانی همهی بررسیهای این صفحه را انجام میدهد:
import { initAdmin } from '@abrova/auth-admin';
const auth = initAdmin(); // ABROVA_PROJECT_ID، ABROVA_API_KEY و ABROVA_SERVER_SECRET را میخواند
app.use(async (req, res, next) => {
const token = req.headers.authorization?.replace(/^Bearer /, '');
try {
req.user = await auth.verifyIdToken(token);
next();
} catch {
res.status(401).json({ error: 'Sign in again' });
}
});
// req.user.uid، req.user.role (یک claim سفارشی)، req.user.amr (روش ورود)برای کارهای حساس، مثل پرداخت یا پاک کردن داده، این را هم بررسی کنید که کاربر بعد از صدور توکن غیرفعال نشده یا از همهی دستگاهها خارج نشده باشد. این یک درخواست به ابرووا احراز هویت هزینه دارد:
const user = await auth.verifyIdToken(token, true); // checkRevoked: بررسی باطل شدن
| کد خطا | معنی |
|---|---|
| INVALID_ID_TOKEN | توکن معتبر نیست: امضای نادرست، پروژهی دیگر، صادرکننده یا مخاطب نادرست. پیام خطا میگوید کدام. |
| ID_TOKEN_EXPIRED | توکن منقضی شده. اپ در فراخوانی بعدی توکن تازه میگیرد. |
| ID_TOKEN_REVOKED | با checkRevoked: کاربر بعد از صدور این توکن از همهی دستگاهها خارج شده. |
| USER_DISABLED | با checkRevoked: کاربر در کنسول غیرفعال شده. |
| NETWORK | کلیدهای عمومی خوانده نشد. کلیدهایی که قبلاً خوانده شدهاند کار میکنند. |
هر زبانی: یک کتابخانهی استاندارد JWT#
هر کتابخانهی JWT که JWKS میخواند کار میکند. همهی این بررسیها را انجام دهید:
| بررسی | مقدار |
|---|---|
| الگوریتم | فقط ES256. هر alg دیگری، از جمله "none"، را رد کنید. |
| کلید | از https://auth.abrova.ir/.well-known/jwks.json با kid توکن. هرگز کلید یا نشانی کلید (jwk، jku، x5u) از خود توکن. |
| iss | abrova |
| aud | abrova_user |
| exp | در آینده. حداکثر ۳۰ ثانیه اختلاف ساعت را بپذیرید. |
| pid | شناسهی پروژهی شما. |
| sub | شناسهی کاربر: ru_... برای کاربر ابرووا احراز هویت، یا شناسهای که سرور شما هنگام درخواست توکن داده. |
Node.js با jose:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet(new URL('https://auth.abrova.ir/.well-known/jwks.json'));
export async function verifyUser(token: string) {
const { payload } = await jwtVerify(token, JWKS, {
algorithms: ['ES256'],
issuer: 'abrova',
audience: 'abrova_user',
clockTolerance: 30,
});
if (payload.pid !== process.env.ABROVA_PROJECT_ID) throw new Error('Token of another project');
return payload; // payload.sub شناسهی کاربر است
}PHP (لاراول) با firebase/php-jwt نسخهی ۶ یا بالاتر:
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
function verifyUser(string $token): object
{
$jwks = Cache::remember('abrova_jwks', 600, fn () =>
Http::timeout(5)->get('https://auth.abrova.ir/.well-known/jwks.json')->throw()->json());
JWT::$leeway = 30;
$claims = JWT::decode($token, JWK::parseKeySet($jwks, 'ES256'));
if ($claims->iss !== 'abrova' || $claims->aud !== 'abrova_user' || $claims->pid !== env('ABROVA_PROJECT_ID')) {
throw new UnexpectedValueException('Not a token of this project');
}
return $claims; // $claims->sub شناسهی کاربر است
}JWT::decode امضا را با کلیدی که kid توکن میگوید، و زمان انقضا را بررسی میکند. وقتی ابرووا کلید تازهای اضافه میکند، توکنی با kid ناشناخته تا تازه شدن کش (حداکثر ۱۰ دقیقه) رد میشود؛ در این حالت کش را پاک کنید و یک بار دوباره امتحان کنید.
پایتون با PyJWT (pip install "pyjwt[crypto]"):
import os
import jwt
jwks = jwt.PyJWKClient("https://auth.abrova.ir/.well-known/jwks.json", lifespan=600)
def verify_user(token: str) -> dict:
key = jwks.get_signing_key_from_jwt(token).key
claims = jwt.decode(token, key, algorithms=["ES256"], issuer="abrova", audience="abrova_user", leeway=30)
if claims.get("pid") != os.environ["ABROVA_PROJECT_ID"]:
raise jwt.InvalidTokenError("Token of another project")
return claims # claims["sub"] شناسهی کاربر استGo با golang-jwt نسخهی ۵ و keyfunc نسخهی ۳:
var jwks keyfunc.Keyfunc
func init() {
var err error
jwks, err = keyfunc.NewDefault([]string{"https://auth.abrova.ir/.well-known/jwks.json"})
if err != nil {
log.Fatal(err)
}
}
func verifyUser(token string) (jwt.MapClaims, error) {
claims := jwt.MapClaims{}
_, err := jwt.ParseWithClaims(token, claims, jwks.Keyfunc,
jwt.WithValidMethods([]string{"ES256"}),
jwt.WithIssuer("abrova"),
jwt.WithAudience("abrova_user"),
jwt.WithExpirationRequired(),
jwt.WithLeeway(30*time.Second))
if err != nil {
return nil, err
}
if claims["pid"] != os.Getenv("ABROVA_PROJECT_ID") {
return nil, errors.New("token of another project")
}
return claims, nil // claims["sub"] شناسهی کاربر است
}مرحله ۳: استفاده از claimها#
| Claim | چیست |
|---|---|
| sub | شناسهی کاربر. |
| pid | پروژهای که توکن برای آن است. |
| iss، aud | abrova و abrova_user. |
| iat، exp | زمان صدور و زمان انقضای توکن (به ثانیه). هر توکن یک ساعت عمر دارد. |
| auth_time | زمان ورود کاربر. |
| amr | روش ورود کاربر، مثلاً ["google"]. فقط در توکنهای ابرووا احراز هویت. |
| claimهای سفارشی شما | در سطح اول توکن، مثلاً role یا plan. ببینید: claimهای سفارشی. |
دسترسی را روی سرور خودتان از روی sub و claimهای سفارشی تصمیم بگیرید، هرگز از چیز دیگری که اپ میفرستد.
نکتهها#
- باطل کردن. توکن تا زمان انقضا، حداکثر یک ساعت، معتبر میماند، حتی بعد از خروج کاربر از همهی دستگاهها. در Firebase هم همینطور است. هرجا مهم است، باطل شدن را بررسی کنید (
checkRevokedدر SDK مدیریتی، یاtokensValidAfterTimeکاربر ازgetUser). - توکنهایی که سرور شما درخواست کرده (
POST /users/tokenبا کلید سرور) هم همینطور بررسی میشوند.subآنها همان شناسهای است که دادهاید. - هرگز در اپ بررسی نکنید. اپ claimهای خودش را با
getTokenResult()میخواند تا صفحهها را انتخاب کند. فقط بررسی روی سرور شما قابل اعتماد است. - توکنها را لاگ نکنید. هر کسی که توکن را داشته باشد تا زمان انقضا میتواند جای کاربر کار کند.
بهروزرسانی: ۲۳ آبان ۱۴۰۵