thechaoticpanda
thechaoticpanda

Reputation: 416

What is the proper way to implement a custom Laravel passport grant?

I have been tinkering with the laravel passport and i can not seem to implement a custom grant type. I am using laravel 5.6 with passport 6.0 . After research, i created a CustomRequestGrantProvider and a CustomRequestGrant likewise in this CustomGrant library but i had no luck and every time i would make a POST request to localhost:8000/oauth/token with grant_type, client_id and client_secret

{"error": "unsupported_grant_type",
"message": "The authorization grant type is not supported by the authorization server.",
"hint": "Check that all required parameters have been provided"}

And as it seems, my request is not even passing through. I made sure to add the provider to app.php

This is my CustomRequestGrantProvider

class CustomRequestGrantProvider extends PassportServiceProvider{


public function boot()
{
    $this->loadViewsFrom(__DIR__.'/../resources/views', 'passport');
    $this->deleteCookieOnLogout();


}

public function register()
{
    $this->registerAuthorizationServer();
}

protected function registerAuthorizationServer()
{
    $this->app->singleton(AuthorizationServer::class, function () {
        return tap($this->makeAuthorizationServer(), function ($server) {
            $server->enableGrantType(
                $this->makeCustomRequestGrant(), Passport::tokensExpireIn()
            );
        });
    });
}

protected function makeCustomRequestGrant()
{
    $grant = new CustomRequestGrant(
        $this->app->make(UserRepository::class),
        $this->app->make(RefreshTokenRepository::class)
    );
    $grant->setRefreshTokenTTL(Passport::refreshTokensExpireIn());
    return $grant;
}}

and this is my CustomRequestGrant

class CustomRequestGrant extends AbstractGrant{

public function __construct(
    UserRepositoryInterface $userRepository,
    RefreshTokenRepositoryInterface $refreshTokenRepository
)
{
    $this->setUserRepository($userRepository);
    $this->setRefreshTokenRepository($refreshTokenRepository);
    $this->refreshTokenTTL = new \DateInterval('P1M');
}

public function respondToAccessTokenRequest(
    ServerRequestInterface $request,
    ResponseTypeInterface $responseType,
    \DateInterval $accessTokenTTL
)
{
    // Validate request
    $client = $this->validateClient($request);
    $scopes = $this->validateScopes($this->getRequestParameter('scope', $request));
    $user = $this->validateUser($request);
    // Finalize the requested scopes
    $scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
    // Issue and persist new tokens
    $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes);
    $refreshToken = $this->issueRefreshToken($accessToken);
    // Inject tokens into response
    $responseType->setAccessToken($accessToken);
    $responseType->setRefreshToken($refreshToken);
    return $responseType;
}

public function getIdentifier()
{
    return 'custom_request';
}

protected function validateUser(ServerRequestInterface $request)
{
    $laravelRequest = new Request($request->getParsedBody());
    $user = $this->getUserEntityByRequest($laravelRequest);
    if ($user instanceof UserEntityInterface === false) {
        $this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
        throw OAuthServerException::invalidCredentials();
    }
    return $user;
}

protected function getUserEntityByRequest(Request $request)
{
    if (is_null($model = config('auth.providers.users.model'))) {
        throw OAuthServerException::serverError('Unable to determine user model from configuration.');
    }
    if (method_exists($model, 'byPassportCustomRequest')) {
        $user = (new $model)->byPassportCustomRequest($request);
    } else {
        throw OAuthServerException::serverError('Unable to find byPassportCustomRequest method on user model.');
    }
    return ($user) ? new User($user->id) : null;
}} 

Note: All imports and namespace is correct, i just removed them for the sake of this post.

I even thought about editing the passport library, but i am not sure how sustainable it would be in the future.

Any help is really appreciated.

A few references:

Custom Grants?

Outdated custom grant example

Github discussion

Upvotes: 1

Views: 1575

Answers (1)

thechaoticpanda
thechaoticpanda

Reputation: 416

So i ended up creating custom classes that implement the original classes (Take a look at the picture below).

The classes needed from the laravel/passport trait are the classes in the picture below with out the Custom in the beginning.

For the CustomUserRepositoryInterface, you only need to modify UserRepositoryInterface, if for example you need to send an extra parameter to the laravel post request.

This gave me the ability to customize laravel passport heavily, like for example passing multiple account types that have different ways of accessing the app(like sign in with phone number, email, facebook_token and id).

I am sorry that i did not go thoroughly into the answer, but i was thinking of creating a library and sharing my work on github and off course update the answer and share the link, but non the less, these are the only classes you need to alter to achieve such result.

enter image description here

Have a splendid day :)

Upvotes: 2

Related Questions