ابرووا
شروع رایگان
☰ فهرست مستندات · بررسی توکن روی سرور

بررسی توکن کاربر روی سرور شما

نمایش Markdown

توکنی را که اپ به سرور خودتان می‌فرستد بررسی کنید: امضای ES256 با کلیدهای عمومی ابرووا (JWKS در /.well-known/jwks.json)، صادرکننده، مخاطب، زمان انقضا و پروژه‌ی شما. همان مدل verifyIdToken در Firebase، با هر کتابخانه‌ی استاندارد JWT در Node.js، PHP، پایتون یا Go.

کی به این نیاز دارید.

اپ شما API خودتان را صدا می‌زند، مثلاً برای ثبت سفارش. سرور شما باید بداند چه کسی درخواست داده. اپ توکن کاربر واردشده را می‌فرستد و سرور شما آن را با کلیدهای عمومی ابرووا بررسی می‌کند. نه برای هر درخواست تماسی با ابرووا لازم است، نه رمز مشترکی: همان مدل verifyIdToken در Firebase.

چطور کار می‌کند#

هر توکن کاربر ابرووا یک JWT است که با ES256 امضا شده. ابرووا کلیدهای عمومی بررسی امضا را به شکل استاندارد JWKS (RFC 7517) منتشر می‌کند:

text
https://auth.abrova.ir/.well-known/jwks.json

آن‌جا فقط کلید عمومی هست. کلید خصوصی هیچ‌وقت از ابرووا بیرون نمی‌رود، پس کسی نمی‌تواند توکن بسازد، ولی همه می‌توانند توکن را بررسی کنند. هر کلید یک شناسه (kid) دارد و هر توکن kid کلیدی را که امضایش کرده می‌گوید. وقتی ابرووا کلیدش را عوض می‌کند، کلید تازه پیش از آن‌که توکنی با آن امضا شود منتشر می‌شود و کلید قبلی دست‌کم یک روز بعد هم منتشر می‌ماند؛ پس سروری که کلیدها را ۱۰ دقیقه نگه می‌دارد هیچ کلیدی را از دست نمی‌دهد.

کلیدها از داخل ایران ارائه می‌شوند، پس سرور شما هنگام قطع اینترنت بین‌الملل هم می‌تواند توکن‌ها را بررسی کند.

مرحله ۱: فرستادن توکن از اپ#

توکن فعلی را در اپ بخوانید و در سرآیند Authorization بفرستید:

پلتفرمخواندن توکن
Flutterawait auth.getToken()
iOStry await auth.getAccessToken()
Androidauth.getAccessToken()
Web، React Nativeawait auth.getToken()
http
GET /api/orders
Authorization: Bearer eyJhbGciOiJFUzI1NiIs...

SDK توکن را پیش از انقضا تمدید می‌کند، پس آن را برای هر درخواست بخوانید و نسخه‌ای نگه ندارید. API خودتان را همیشه با HTTPS صدا بزنید.

مرحله ۲: بررسی روی سرور#

پروژه را بررسی کنید، نه فقط امضا را.

یک کلید توکن‌های همه‌ی پروژه‌های ابرووا را امضا می‌کند. توکنی با امضای درست هم ممکن است مال کاربر اپ دیگری باشد. همیشه بررسی کنید که pid شناسه‌ی پروژه‌ی شما باشد (کنسول › تنظیمات پروژه › عمومی).

Node.js: SDK مدیریتی#

با @abrova/auth-admin نسخه‌ی ۰.۲.۰ یا بالاتر، یک فراخوانی همه‌ی بررسی‌های این صفحه را انجام می‌دهد:

ts
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 (روش ورود)

برای کارهای حساس، مثل پرداخت یا پاک کردن داده، این را هم بررسی کنید که کاربر بعد از صدور توکن غیرفعال نشده یا از همه‌ی دستگاه‌ها خارج نشده باشد. این یک درخواست به ابرووا احراز هویت هزینه دارد:

ts
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) از خود توکن.
issabrova
audabrova_user
expدر آینده. حداکثر ۳۰ ثانیه اختلاف ساعت را بپذیرید.
pidشناسه‌ی پروژه‌ی شما.
subشناسه‌ی کاربر: ru_... برای کاربر ابرووا احراز هویت، یا شناسه‌ای که سرور شما هنگام درخواست توکن داده.

Node.js با jose:

ts
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 نسخه‌ی ۶ یا بالاتر:

php
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]"):

python
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 نسخه‌ی ۳:

go
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، audabrova و 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() می‌خواند تا صفحه‌ها را انتخاب کند. فقط بررسی روی سرور شما قابل اعتماد است.
  • توکن‌ها را لاگ نکنید. هر کسی که توکن را داشته باشد تا زمان انقضا می‌تواند جای کاربر کار کند.
این صفحه مفید بود؟
گزارش مشکل

به‌روزرسانی: ۲۳ آبان ۱۴۰۵