mirror of
https://github.com/anikeen-com/id.git
synced 2026-11-04 22:59:41 +00:00
Fix the anikeen API guard and register its user provider
- Register the guard driver and the anikeen user provider with static closures: Laravel binds extend() closures to the AuthManager, so $this->makeGuard() failed. - Look the token's sub up in the user's Anikeen ID column (anikeen_id by default; provider 'column', services.anikeen.user_id_column or useUserIdField()), not in the primary key; works with the anikeen, eloquent and database providers. - JwtParser: key from services.anikeen.public_key(_path) or the bundled key, read once per process; misconfiguration is an error, not a 401. Passport tokens have no client claim, so it is optional now. - Optional revocation check against Anikeen ID (verify_revocation), cached per token. - anikeenTokenCan() without a token, anikeenTokenCanAny(), scope middleware accept JSON responses. - Update the bundled staging public key to the current staging key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VbpgPaLqhBzo1AXHNs9Cx5
This commit is contained in:
1 parent
b074ec3c0c
commit
dc29eb2844
12 files changed
+554
-163
No files matched your search
+110
-61
@@ -9,38 +9,39 @@ use Exception;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Auth\GuardHelpers;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Illuminate\Cookie\CookieValuePrefix;
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use stdClass;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Resolves the user of a request from an Anikeen ID access token (JWT, "Authorization: Bearer …")
|
||||
* or from the encrypted token cookie of CreateFreshApiToken.
|
||||
*
|
||||
* A bearer token is verified with the JwtParser (signature, expiry). Its `sub` is the user's
|
||||
* Anikeen ID, looked up by the provider (AnikeenIdUserProvider::retrieveByAnikeenId(), column
|
||||
* `anikeen_id` by default). The token is attached to the user: `$user->anikeenTokenCan('scope')`.
|
||||
*
|
||||
* With `services.anikeen.verify_revocation` the token is also checked against Anikeen ID
|
||||
* (GET v1/user, cached per token for `services.anikeen.revocation_cache_ttl` seconds), so
|
||||
* revoked tokens stop working before they expire.
|
||||
*/
|
||||
class TokenGuard
|
||||
{
|
||||
use GuardHelpers;
|
||||
|
||||
private Encrypter $encrypter;
|
||||
|
||||
private JwtParser $jwtParser;
|
||||
|
||||
public function __construct(UserProvider $provider, Encrypter $encrypter, JwtParser $jwtParser)
|
||||
{
|
||||
$this->provider = $provider;
|
||||
$this->encrypter = $encrypter;
|
||||
$this->jwtParser = $jwtParser;
|
||||
public function __construct(
|
||||
protected UserProvider $provider,
|
||||
private Encrypter $encrypter,
|
||||
private JwtParser $jwtParser
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user for the incoming request.
|
||||
*
|
||||
* @throws BindingResolutionException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function user(Request $request): ?Authenticatable
|
||||
{
|
||||
@@ -55,9 +56,6 @@ class TokenGuard
|
||||
|
||||
/**
|
||||
* Authenticate the incoming request via the Bearer token.
|
||||
*
|
||||
* @throws BindingResolutionException
|
||||
* @throws Throwable
|
||||
*/
|
||||
protected function authenticateViaBearerToken(Request $request): ?Authenticatable
|
||||
{
|
||||
@@ -65,51 +63,97 @@ class TokenGuard
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the access token is valid we will retrieve the user according to the user ID
|
||||
// associated with the token. We will use the provider implementation which may
|
||||
// be used to retrieve users from Eloquent. Next, we'll be ready to continue.
|
||||
/** @var Authenticatable|HasAnikeenTokens $user */
|
||||
$user = $this->provider->retrieveById(
|
||||
$request->attributes->get('oauth_user_id') ?: null
|
||||
);
|
||||
// client credentials tokens have no user
|
||||
if (!is_scalar($token->sub ?? null) || (string) $token->sub === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $user?->withAnikeenAccessToken($token);
|
||||
if (!$this->tokenIsActive($token, (string) $request->bearerToken())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var Authenticatable|HasAnikeenTokens|null $user */
|
||||
$user = $this->retrieveUser((string) $token->sub);
|
||||
|
||||
return $user && method_exists($user, 'withAnikeenAccessToken')
|
||||
? $user->withAnikeenAccessToken($token)
|
||||
: $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate and get the incoming request via the Bearer token.
|
||||
*
|
||||
* @throws BindingResolutionException
|
||||
* @throws Throwable
|
||||
* Verify the Bearer token and expose its claims as request attributes.
|
||||
*/
|
||||
protected function validateRequestViaBearerToken(Request $request): ?stdClass
|
||||
{
|
||||
try {
|
||||
$decoded = $this->jwtParser->decode($request);
|
||||
|
||||
$request->attributes->set('oauth_access_token_id', $decoded->jti);
|
||||
$request->attributes->set('oauth_client_id', $decoded->aud);
|
||||
$request->attributes->set('oauth_client_trusted', $decoded->client->trusted);
|
||||
$request->attributes->set('oauth_user_id', $decoded->sub);
|
||||
$request->attributes->set('oauth_scopes', $decoded->scopes);
|
||||
|
||||
return $decoded;
|
||||
} catch (AuthenticationException $e) {
|
||||
$request->headers->set('Authorization', '', true);
|
||||
|
||||
Container::getInstance()->make(
|
||||
ExceptionHandler::class
|
||||
)->report($e);
|
||||
|
||||
} catch (AuthenticationException) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded->scopes = array_values(array_filter((array) ($decoded->scopes ?? []), 'is_string'));
|
||||
|
||||
$request->attributes->set('oauth_access_token_id', $decoded->jti ?? null);
|
||||
$request->attributes->set('oauth_client_id', $decoded->aud ?? null);
|
||||
$request->attributes->set('oauth_client_trusted', $decoded->client->trusted ?? null);
|
||||
$request->attributes->set('oauth_user_id', $decoded->sub ?? null);
|
||||
$request->attributes->set('oauth_scopes', $decoded->scopes);
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether Anikeen ID still accepts the token (only with services.anikeen.verify_revocation).
|
||||
*/
|
||||
protected function tokenIsActive(stdClass $token, string $jwt): bool
|
||||
{
|
||||
if (!config('services.anikeen.verify_revocation')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$key = 'anikeen-id:token-active:' . hash('sha256', $jwt);
|
||||
|
||||
if (($cached = Cache::get($key)) !== null) {
|
||||
return (bool) $cached;
|
||||
}
|
||||
|
||||
try {
|
||||
$result = (clone app(AnikeenId::class))->withToken($jwt)->getAuthedUser();
|
||||
} catch (Throwable) {
|
||||
return false; // Anikeen ID unreachable: fail closed, ask again next time
|
||||
}
|
||||
|
||||
$status = $result->response?->getStatusCode();
|
||||
|
||||
if ($result->success()) {
|
||||
$active = true;
|
||||
} elseif (in_array($status, [401, 403], true)) {
|
||||
$active = false;
|
||||
} else {
|
||||
return false; // server error: fail closed without caching
|
||||
}
|
||||
|
||||
$ttl = (int) config('services.anikeen.revocation_cache_ttl', 300);
|
||||
if (isset($token->exp)) {
|
||||
$ttl = max(1, min($ttl, (int) $token->exp - time()));
|
||||
}
|
||||
Cache::put($key, $active, $ttl);
|
||||
|
||||
return $active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the local user of an Anikeen ID.
|
||||
*/
|
||||
protected function retrieveUser(string $anikeenId): ?Authenticatable
|
||||
{
|
||||
return $this->provider->retrieveByAnikeenId($anikeenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the incoming request via the token cookie.
|
||||
*/
|
||||
protected function authenticateViaCookie(Request $request): mixed
|
||||
protected function authenticateViaCookie(Request $request): ?Authenticatable
|
||||
{
|
||||
if (!$token = $this->getTokenViaCookie($request)) {
|
||||
return null;
|
||||
@@ -118,9 +162,11 @@ class TokenGuard
|
||||
// If this user exists, we will return this user and attach a "transient" token to
|
||||
// the user model. The transient token assumes it has all scopes since the user
|
||||
// is physically logged into the application via the application's interface.
|
||||
/** @var Authenticatable|HasAnikeenTokens $user */
|
||||
if ($user = $this->provider->retrieveById($token['sub'])) {
|
||||
return $user->withAnikeenAccessToken((object)['scopes' => ['*']]);
|
||||
/** @var Authenticatable|HasAnikeenTokens|null $user */
|
||||
if ($user = $this->retrieveUser((string) $token['sub'])) {
|
||||
return method_exists($user, 'withAnikeenAccessToken')
|
||||
? $user->withAnikeenAccessToken((object) ['scopes' => ['*']])
|
||||
: $user;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -136,15 +182,18 @@ class TokenGuard
|
||||
// database. If we can't decrypt the value we'll bail out with a null return.
|
||||
try {
|
||||
$token = $this->decodeJwtTokenCookie($request);
|
||||
} catch (Exception $e) {
|
||||
} catch (Exception) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($token['sub'], $token['expiry'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// We will compare the CSRF token in the decoded API token against the CSRF header
|
||||
// sent with the request. If they don't match then this request isn't sent from
|
||||
// a valid source and we won't authenticate the request for further handling.
|
||||
if (!AnikeenId::$ignoreCsrfToken && (!$this->validCsrf($token, $request) ||
|
||||
time() >= $token['expiry'])) {
|
||||
if (time() >= $token['expiry'] || (!AnikeenId::$ignoreCsrfToken && !$this->validCsrf($token, $request))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -156,7 +205,7 @@ class TokenGuard
|
||||
*/
|
||||
protected function decodeJwtTokenCookie(Request $request): array
|
||||
{
|
||||
return (array)JWT::decode(
|
||||
return (array) JWT::decode(
|
||||
CookieValuePrefix::remove($this->encrypter->decrypt($request->cookie(AnikeenId::cookie()), AnikeenId::$unserializesCookies)),
|
||||
new Key(
|
||||
$this->encrypter->getKey(),
|
||||
@@ -171,8 +220,8 @@ class TokenGuard
|
||||
protected function validCsrf(array $token, Request $request): bool
|
||||
{
|
||||
return isset($token['csrf']) && hash_equals(
|
||||
$token['csrf'], $this->getTokenFromRequest($request)
|
||||
);
|
||||
$token['csrf'], $this->getTokenFromRequest($request)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +235,7 @@ class TokenGuard
|
||||
$token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized()));
|
||||
}
|
||||
|
||||
return $token;
|
||||
return (string) $token;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,4 +245,4 @@ class TokenGuard
|
||||
{
|
||||
return EncryptCookies::serialized('XSRF-TOKEN');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Anikeen\Id\Auth;
|
||||
|
||||
use Anikeen\Id\AnikeenId;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Auth\UserProvider as Base;
|
||||
|
||||
@@ -10,11 +11,33 @@ class UserProvider implements Base
|
||||
/**
|
||||
* Create a new AnikeenId user provider.
|
||||
*/
|
||||
public function __construct(protected Base $provider, protected string $providerName)
|
||||
public function __construct(protected Base $provider, protected string $providerName, protected ?string $column = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* The column that holds a user's Anikeen ID: the provider's `column`, else
|
||||
* `services.anikeen.user_id_column`, else AnikeenId::getUserIdField() (`anikeen_id`).
|
||||
*/
|
||||
public function column(): string
|
||||
{
|
||||
return $this->column ?: (config('services.anikeen.user_id_column') ?: AnikeenId::getUserIdField());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the user of an Anikeen ID (the `sub` of an access token).
|
||||
*/
|
||||
public function retrieveByAnikeenId(string $anikeenId): ?Authenticatable
|
||||
{
|
||||
if (method_exists($this->provider, 'retrieveByAnikeenId')) {
|
||||
return $this->provider->retrieveByAnikeenId($anikeenId);
|
||||
}
|
||||
|
||||
// eloquent / database providers: a plain where on the column
|
||||
return $this->provider->retrieveByCredentials([$this->column() => $anikeenId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -65,6 +88,6 @@ class UserProvider implements Base
|
||||
|
||||
public function rehashPasswordIfRequired(Authenticatable $user, #[\SensitiveParameter] array $credentials, bool $force = false)
|
||||
{
|
||||
// TODO: Implement rehashPasswordIfRequired() method.
|
||||
$this->provider->rehashPasswordIfRequired($user, $credentials, $force);
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,23 @@ trait HasAnikeenTokens
|
||||
*/
|
||||
public function anikeenTokenCan(string $scope): bool
|
||||
{
|
||||
$scopes = $this->accessToken ? $this->accessToken->scopes : [];
|
||||
$scopes = (array) ($this->accessToken->scopes ?? []);
|
||||
|
||||
return in_array('*', $scopes) || in_array($scope, $this->accessToken->scopes);
|
||||
return in_array('*', $scopes, true) || in_array($scope, $scopes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current API token has any of the given scopes.
|
||||
*/
|
||||
public function anikeenTokenCanAny(array $scopes): bool
|
||||
{
|
||||
foreach ($scopes as $scope) {
|
||||
if ($this->anikeenTokenCan($scope)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,32 +7,74 @@ use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Http\Request;
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Verifies access tokens issued by Anikeen ID (Passport, RS256): signature against
|
||||
* Anikeen ID's public key plus the time claims (exp, nbf, iat; 60 seconds leeway).
|
||||
*
|
||||
* The key is `services.anikeen.public_key` (PEM, "\n" escapes allowed) or the file at
|
||||
* `services.anikeen.public_key_path`, else the key shipped with this package for the
|
||||
* configured mode (production / staging). It is read once per process.
|
||||
*/
|
||||
class JwtParser
|
||||
{
|
||||
/**
|
||||
* Public keys already read, by source.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private static array $keys = [];
|
||||
|
||||
/**
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
public function decode(Request $request): stdClass
|
||||
{
|
||||
return $this->decodeToken((string) $request->bearerToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
public function decodeToken(string $token): stdClass
|
||||
{
|
||||
if ($token === '') {
|
||||
throw new AuthenticationException('No access token given.');
|
||||
}
|
||||
|
||||
JWT::$leeway = 60;
|
||||
|
||||
// outside the try: a missing key is a configuration error, not an invalid token
|
||||
$key = new Key($this->getOauthPublicKey(), 'RS256');
|
||||
|
||||
try {
|
||||
return JWT::decode(
|
||||
$request->bearerToken(),
|
||||
new Key($this->getOauthPublicKey(), 'RS256')
|
||||
);
|
||||
} catch (Throwable $exception) {
|
||||
throw (new AuthenticationException());
|
||||
return JWT::decode($token, $key);
|
||||
} catch (Throwable) {
|
||||
throw new AuthenticationException('The access token is invalid or expired.');
|
||||
}
|
||||
}
|
||||
|
||||
private function getOauthPublicKey(): bool|string
|
||||
/**
|
||||
* Forget the keys read so far (e.g. after changing the configuration in tests).
|
||||
*/
|
||||
public static function flushKeys(): void
|
||||
{
|
||||
return AnikeenId::getMode() === 'staging'
|
||||
? file_get_contents(dirname(__DIR__, 3) . '/oauth-public.staging.key')
|
||||
: file_get_contents(dirname(__DIR__, 3) . '/oauth-public.key');
|
||||
self::$keys = [];
|
||||
}
|
||||
|
||||
protected function getOauthPublicKey(): string
|
||||
{
|
||||
if ($key = config('services.anikeen.public_key')) {
|
||||
return self::$keys['config:' . md5($key)] ??= str_replace('\\n', "\n", $key);
|
||||
}
|
||||
|
||||
$path = config('services.anikeen.public_key_path') ?: (AnikeenId::getMode() === 'staging'
|
||||
? dirname(__DIR__, 3) . '/oauth-public.staging.key'
|
||||
: dirname(__DIR__, 3) . '/oauth-public.key');
|
||||
|
||||
return self::$keys['file:' . $path] ??= (is_readable($path) ? (string) file_get_contents($path) : throw new RuntimeException("Anikeen ID public key [$path] is not readable."));
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ use Anikeen\Id\Exceptions\MissingScopeException;
|
||||
use Closure;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CheckForAnyScope extends UseParameters
|
||||
{
|
||||
@@ -17,7 +17,7 @@ class CheckForAnyScope extends UseParameters
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, ...$scopes): Response
|
||||
{
|
||||
if (!$request->user() || !$request->user()->anikeenToken()) {
|
||||
if (!$request->user() || !method_exists($request->user(), 'anikeenToken') || !$request->user()->anikeenToken()) {
|
||||
throw new AuthenticationException;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use Anikeen\Id\Exceptions\MissingScopeException;
|
||||
use Closure;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CheckScopes extends UseParameters
|
||||
{
|
||||
@@ -17,7 +17,7 @@ class CheckScopes extends UseParameters
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, ...$scopes): Response
|
||||
{
|
||||
if (!$request->user() || !$request->user()->anikeenToken()) {
|
||||
if (!$request->user() || !method_exists($request->user(), 'anikeenToken') || !$request->user()->anikeenToken()) {
|
||||
throw new AuthenticationException;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,11 @@ use Anikeen\Id\Auth\UserProvider;
|
||||
use Anikeen\Id\Contracts;
|
||||
use Anikeen\Id\Helpers\JwtParser;
|
||||
use Anikeen\Id\Repository;
|
||||
use Illuminate\Auth\AuthManager;
|
||||
use Illuminate\Auth\RequestGuard;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
use InvalidArgumentException;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AnikeenIdServiceProvider extends ServiceProvider
|
||||
@@ -38,15 +41,28 @@ class AnikeenIdServiceProvider extends ServiceProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the token guard.
|
||||
* Register the `anikeen` guard driver and user provider.
|
||||
*
|
||||
* Laravel binds non-static extend() closures to the AuthManager, so these closures are
|
||||
* static and use nothing but their arguments.
|
||||
*/
|
||||
protected function registerGuard(): void
|
||||
{
|
||||
Auth::resolved(function ($auth) {
|
||||
$auth->extend('anikeen', function ($app, $name, array $config) {
|
||||
return tap($this->makeGuard($config), function ($guard) {
|
||||
$this->app->refresh('request', $guard, 'setRequest');
|
||||
});
|
||||
$this->callAfterResolving('auth', static function (AuthManager $auth): void {
|
||||
$auth->provider('anikeen', static fn (Application $app, array $config) => new AnikeenIdUserProvider(
|
||||
$app->make(AnikeenId::class),
|
||||
$app->make('request'),
|
||||
$config['model'] ?? $app['config']->get('auth.providers.users.model'),
|
||||
$config['fields'] ?? [],
|
||||
$config['column'] ?? null,
|
||||
));
|
||||
|
||||
$auth->extend('anikeen', static function (Application $app, string $name, array $config): RequestGuard {
|
||||
$guard = AnikeenIdServiceProvider::makeGuard($app, $config);
|
||||
|
||||
$app->refresh('request', $guard, 'setRequest');
|
||||
|
||||
return $guard;
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -54,15 +70,20 @@ class AnikeenIdServiceProvider extends ServiceProvider
|
||||
/**
|
||||
* Make an instance of the token guard.
|
||||
*/
|
||||
protected function makeGuard(array $config): RequestGuard
|
||||
public static function makeGuard(Application $app, array $config): RequestGuard
|
||||
{
|
||||
return new RequestGuard(function ($request) use ($config) {
|
||||
$providerName = $config['provider'] ?? $app['config']->get('auth.defaults.provider', 'users');
|
||||
$provider = $app['auth']->createUserProvider($providerName)
|
||||
?? throw new InvalidArgumentException("Authentication user provider [$providerName] is not configured.");
|
||||
$providerConfig = $app['config']->get("auth.providers.$providerName", []);
|
||||
|
||||
return new RequestGuard(static function (Request $request) use ($app, $provider, $providerName, $providerConfig, $config) {
|
||||
return (new TokenGuard(
|
||||
new UserProvider(Auth::createUserProvider($config['provider']), $config['provider']),
|
||||
$this->app->make('encrypter'),
|
||||
$this->app->make(JwtParser::class)
|
||||
new UserProvider($provider, $providerName, $config['column'] ?? $providerConfig['column'] ?? null),
|
||||
$app->make('encrypter'),
|
||||
$app->make(JwtParser::class)
|
||||
))->user($request);
|
||||
}, $this->app['request']);
|
||||
}, $app['request'], $provider);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,8 @@ class AnikeenIdUserProvider implements UserProvider
|
||||
private AnikeenId $anikeenId,
|
||||
private Request $request,
|
||||
private string $model,
|
||||
private array $fields = []
|
||||
private array $fields = [],
|
||||
private ?string $column = null,
|
||||
) {
|
||||
$this->accessTokenField = AnikeenId::getAccessTokenField();
|
||||
}
|
||||
@@ -97,6 +98,17 @@ class AnikeenIdUserProvider implements UserProvider
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the user of an Anikeen ID (the `sub` of an access token) by the provider's `column`,
|
||||
* else `services.anikeen.user_id_column`, else AnikeenId::getUserIdField() (`anikeen_id`).
|
||||
*/
|
||||
public function retrieveByAnikeenId(string $anikeenId): ?Authenticatable
|
||||
{
|
||||
$column = $this->column ?: (config('services.anikeen.user_id_column') ?: AnikeenId::getUserIdField());
|
||||
|
||||
return $this->newModelQuery()->where($column, $anikeenId)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user