You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pixelfed/app/Auth/AppRegisterGrant.php

114 lines
4.1 KiB
PHP

<?php
namespace App\Auth;
use DateInterval;
use Laravel\Passport\Passport;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\AbstractGrant;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\RequestAccessTokenEvent;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\RequestRefreshTokenEvent;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use Psr\Http\Message\ServerRequestInterface;
/**
* Issues an access token + refresh token pair for a given user against a
* client the app registered through /api/v1/apps. Only ever enabled on the
* private authorization server built by AppRegisterTokenFactory, so it is
* never reachable through /oauth/token.
*/
class AppRegisterGrant extends AbstractGrant
{
public const IDENTIFIER = 'app_register';
public function __construct(RefreshTokenRepositoryInterface $refreshTokenRepository)
{
$this->setRefreshTokenRepository($refreshTokenRepository);
$this->refreshTokenTTL = new DateInterval('P1M');
}
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
): ResponseTypeInterface {
$client = $this->validateRegisteredClient($request);
$userIdentifier = $this->getRequestParameter('user_id', $request);
if ($userIdentifier === null || $userIdentifier === '') {
throw OAuthServerException::invalidRequest('user_id');
}
$userIdentifier = (string) $userIdentifier;
$scopes = $this->scopeRepository->finalizeScopes(
$this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)),
$this->getIdentifier(),
$client,
$userIdentifier
);
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $userIdentifier, $scopes);
$this->getEmitter()->emit(
new RequestAccessTokenEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request, $accessToken)
);
Passport::token()->newQuery()->whereKey($accessToken->getIdentifier())->update([
'name' => $this->getRequestParameter('name', $request) ?: $client->getName(),
]);
$responseType->setAccessToken($accessToken);
$refreshToken = $this->issueRefreshToken($accessToken);
if ($refreshToken instanceof \League\OAuth2\Server\Entities\RefreshTokenEntityInterface) {
$this->getEmitter()->emit(
new RequestRefreshTokenEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request, $refreshToken)
);
$responseType->setRefreshToken($refreshToken);
}
return $responseType;
}
/**
* Same checks as AbstractGrant::validateClient minus the grant_types
* gate. Clients created by /api/v1/apps have no explicit grant_types
* column, so Passport computes the list and "app_register" is never in
* it. We only need: client exists, is confidential, secret matches.
*/
protected function validateRegisteredClient(ServerRequestInterface $request): ClientEntityInterface
{
[$clientId, $clientSecret] = $this->getClientCredentials($request);
$client = $this->clientRepository->getClientEntity($clientId);
if (! $client instanceof ClientEntityInterface || ! $client->isConfidential()) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
if (
$clientSecret === '' ||
! $this->clientRepository->validateClient($clientId, $clientSecret, $this->getIdentifier())
) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
return $client;
}
public function getIdentifier(): string
{
return self::IDENTIFIER;
}
}