Scopes & claims
Scope catalog
Section titled “Scope catalog”The provider understands the OIDC standard scopes — openid, profile, email, address,
phone — merged with your configured catalog (passport.scopes, below) and any scopes a
third party registered directly via Passport::tokensCan(). On a conflict the configured
catalog wins, then tokensCan()-registered scopes, then the built-in OIDC scopes — so you can
override the description of a standard scope simply by defining it in your catalog.
Wildcard (*) parity
Section titled “Wildcard (*) parity”The package mirrors Passport’s wildcard behavior exactly. Passport treats * as always valid and
grants it for the password, personal_access, and client_credentials grants (e.g.
$user->createToken('cli', ['*'])). Here * resolves as a scope and survives finalization for
those grant types, and is stripped for authorization_code (interactive) flows.
Registering API scopes
Section titled “Registering API scopes”Feed your API scope catalog to the provider through config/oidc.php’s passport.scopes
option instead of calling Passport::tokensCan() yourself:
'passport' => [ 'token_model' => App\Models\ApiToken::class, // optional Passport token model (a `Laravel\Passport\Token` subclass) 'scopes' => App\Auth\ApiScopes::class, // or an inline [scope => description] map],A class-string must implement Bambamboole\LaravelOidc\Server\Contracts\ScopeCatalog
(scopes(): array<string, string>). The scope repository consults it lazily —
resolved from the container the first time scopes are actually enumerated (the
consent screen, the discovery document, token issuance), so a database-backed
catalog costs nothing on unrelated requests, keeping key- and db-less artisan
runs working, and the result is memoized for the life of the repository.
Exceptions thrown by scopes() fall back to an empty catalog; an invalid
class-string still fails loudly, at first enumeration rather than at boot.
Scopes registered directly via Passport::tokensCan() (by a third-party
package, for instance) are still honored — the repository merges them in,
with the configured catalog winning on conflict. Passport::scopes() /
scopeIds() no longer reflect oidc.passport.scopes; enumerate the full
catalog through the ScopeRepository contract instead.
The scope catalog is provided by the ScopeRepository contract — see
Extension contracts to swap it.
Claims
Section titled “Claims”Bambamboole\LaravelOidc\Server\Contracts\ClaimsResolver maps an authenticated user to a ClaimSet. A
ClaimSet is constructed from a scope => [claim => value] map. Both the id_token builder and
the userinfo endpoint call forScopes() on it with the token’s granted scopes, so a claim is only
emitted when its scope was granted — and null values are dropped.
use Bambamboole\LaravelOidc\Server\Claims\ClaimSet;use Bambamboole\LaravelOidc\Server\Contracts\ClaimsResolver;use Illuminate\Contracts\Auth\Authenticatable;
class AppClaimsResolver implements ClaimsResolver{ public function resolve(Authenticatable $user): ClaimSet { return new ClaimSet([ 'profile' => ['name' => $user->name], 'email' => [ 'email' => $user->email, 'email_verified' => $user->hasVerifiedEmail(), ], ]); }}Bind your resolver so the provider uses it:
$this->app->singleton( \Bambamboole\LaravelOidc\Server\Contracts\ClaimsResolver::class, AppClaimsResolver::class,);The bundled DefaultClaimsResolver also maps two conventional user attributes
under the profile scope, when present: locale (from $user->locale) and
zoneinfo (from $user->timezone). A custom resolver like the one above
replaces it entirely, so re-add that mapping yourself if you want to keep it.
The ClaimsResolver and ScopeRepository are the two seams described in full under
Extension contracts.