Files
id/src/Id/Providers/AnikeenIdUserProvider.php
T
Maurice PreußandClaude Opus 5.5 dc29eb2844 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
2026-11-03 23:07:58 +01:00

137 lines
3.6 KiB
PHP

<?php
namespace Anikeen\Id\Providers;
use Anikeen\Id\AnikeenId;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
class AnikeenIdUserProvider implements UserProvider
{
private ?string $accessTokenField;
public function __construct(
private AnikeenId $anikeenId,
private Request $request,
private string $model,
private array $fields = [],
private ?string $column = null,
) {
$this->accessTokenField = AnikeenId::getAccessTokenField();
}
/**
* {@inheritDoc}
*/
public function retrieveByToken($identifier, $token): ?Authenticatable
{
// Token from request (if not already pass from $token):
$token = $token ?: $this->request->bearerToken();
if (! $token) {
return null;
}
// Set token in SSO client and request user info
$this->anikeenId->setToken($token);
$result = $this->anikeenId->getAuthedUser();
if (! $result->success()) {
return null;
}
// Only the desired fields
$data = Arr::only((array)$result->data(), $this->fields);
// Primary key (e.g. $user->id)
$pk = $this->createModel()->getAuthIdentifierName();
$data[$pk] = $result->data->id;
// Fill in access token field, if available
if ($this->accessTokenField) {
$data[$this->accessTokenField] = $token;
}
// Local eloquent model: either find or create a new one
/** @var Model $modelInstance */
$modelInstance = $this->newModelQuery()
->firstOrNew([$pk => $data[$pk]]);
$modelInstance->fill($data);
$modelInstance->save();
return $modelInstance;
}
/**
* {@inheritDoc}
*/
public function updateRememberToken(Authenticatable $user, $token): void
{
// no-op
}
/**
* {@inheritDoc}
*/
public function retrieveByCredentials(array $credentials): ?Authenticatable
{
return null;
}
/**
* {@inheritDoc}
*/
public function validateCredentials(Authenticatable $user, array $credentials): bool
{
return true;
}
/**
* {@inheritDoc}
*/
public function retrieveById($identifier): ?Authenticatable
{
return $this->newModelQuery()
->where($this->createModel()->getAuthIdentifierName(), $identifier)
->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}
*/
public function rehashPasswordIfRequired(Authenticatable $user, #[\SensitiveParameter] array $credentials, bool $force = false): void
{
// no-op
}
/**
* @return Model
*/
protected function createModel(): Model
{
$class = '\\' . ltrim($this->model, '\\');
return new $class;
}
/**
* @return Builder
*/
protected function newModelQuery(): Builder
{
return $this->createModel()->newQuery();
}
}