Skip to content

Token Context, Scopes, and Claims

TokenContext describes what should be embedded in the token pair and persisted in token rows.

php
use Sopheak\JwtAuth\DTO\TokenContext;

$context = TokenContext::make()
    ->tenantId(42)
    ->scopes(['invoices.read', 'invoices.write'])
    ->claim('tenant_role', 'owner');

Scopes

Scopes are stored on access and refresh rows and embedded in the JWT payload as scopes.

php
Route::middleware(['auth:api', 'sp.jwt.scope:invoices.read'])
    ->get('/invoices', Controller::class);

Use sp.jwt.any_scope when any one scope is enough.

php
Route::middleware(['auth:api', 'sp.jwt.any_scope:admin,support'])
    ->get('/support', Controller::class);

Claims

Claims are app-defined JSON-safe values.

Access token claims are stored in the sp_jwt_access_tokens.claims JSON column and cast to an array on JwtAccessToken. The package does not store the raw JWT string on the token model.

After the sp-jwt guard authenticates a request, read claims from the attached token:

php
$token = $request->user()?->token();

$tenantId = $token?->claim('tenant_id');
$companyId = $token?->claim('company_id');
$companyIds = $token?->companyIds();
$isImpersonated = $token?->isImpersonated();
$claims = $token?->claims ?? [];

Reserved JWT claim names cannot be used as custom claims:

  • iss
  • sub
  • aud
  • exp
  • nbf
  • iat
  • jti
  • sid
  • scopes
  • subject

Subject Context

Subject context is useful for tenant, company, workspace, or account selection.

php
TokenContext::make()
    ->companyId(1001)
    ->companyIds([1001, 1002])
    ->impersonated(false);

companyId() sets subject('company', ...) and claims['company_id']. companyIds() stores app-readable company access in claims['company_ids']. Use subject_type / subject_id for the primary active tenant/company/workspace context because those columns are indexed on the access-token table. Use claims for app-readable request context. The package does not add hardcoded company_id columns because tenant models vary by application.

The package stores subject and claim data but does not enforce tenant policy. The consuming application owns tenant authorization rules.

Token Responses

TokenResponse::passportCompatible() accepts extra fields for app-owned response data:

php
return TokenResponse::passportCompatible($pair, [
    'company_id' => $pair->accessTokenRecord->companyId(),
    'impersonated' => $pair->accessTokenRecord->isImpersonated(),
]);

Register a global token response transformer when every token response needs the same app-owned fields:

php
TokenResponse::extend(function (array $response, TokenPair $pair): array {
    $response['company_id'] = $pair->accessTokenRecord->companyId();
    $response['impersonated'] = $pair->accessTokenRecord->isImpersonated();

    return $response;
});

Device and Session

TokenContext supports device and session metadata. If no session id is supplied, the package creates one when issuing tokens.

Use session ids to revoke all tokens for a single logged-in device/session.