oauth2-bundle icon indicating copy to clipboard operation
oauth2-bundle copied to clipboard

Password Grant Flow

Open cybperic opened this issue 4 years ago • 0 comments

Hi,

I need to implement the password grant flow between an angular SPA and symfony api back end. My understanding is that I should use the password grant flow. The SPA will serve 3 different brand sites and we will have 3 different OAuth Clients. The first thing I don't get is since the SPA can't contain the client secret which oauth end point I'll be hitting to get the access token.

Right now we have a two steps registration process and in second step I need to return an access token for the user in order to complete profile. I'm restricting this end point with 'registration' scope and the user will have the role ROLE_USER_INACTIVE + obviously ROLE_OAUTH2_REGISTRATION. So the user can't access anything else. I'm providing the access token like this:

public function getAccessToken(Request $request, Player $player)
    {
        //Auto wired in constructor
        //Nyholm\Psr7\Factory\Psr17Factory $psrHttpFactory,
        //League\OAuth2\Server\AuthorizationServer $authorizationServer,
        //App\Repository\ClientBrandRepository $clientBrandRepository,

        $clientBrand = $this->clientBrandRepository->getOAuthClientByBrand($player->getBrand()->getName());

        $request->request->add([
            'grant_type' => 'password',
            'scope' => 'registration',
            'username' => $player->getUsername(),
            'password' => 'password',
            'client_id' => $clientBrand->getClient()->getIdentifier(),
            'client_secret' => $clientBrand->getClient()->getSecret(),
        ]);

        $psrRequest = $this->psrHttpFactory->createRequest($request);
        $psr17Factory = new Psr17Factory();
        $serverResponse = $psr17Factory->createResponse();

        try {
            $response = $this->authorizationServer->respondToAccessTokenRequest($psrRequest, $serverResponse);
            $responseAsArray = json_decode($response->getBody(), true);

            return $responseAsArray;

        } catch (OAuthServerException $e) {
            return $e->generateHttpResponse($serverResponse);
        }
    }

Here the password sin't taken into account, here is my UserResolveListener

public function onUserResolve(UserResolveEvent $event): void
    {
        $user = $this->userBrandProvider->loadPlayerByUsernameClient($event->getUsername(), $event->getClient()->getIdentifier());

        if (null === $user) {
            return;
        }

        if ($user->isActive()) {
            if (!$this->userPasswordEncoder->isPasswordValid($user, $event->getPassword())) {
                return;
            }
        } else {
            if (!$user->isRegistrationOngoing()) {
                return;
            }
        }

        $event->setUser($user);
    }

Basically in case the user is inactive, I'm checking if the registration is ongoing and without checking the password (since I don't have it) I'm setting the user to the event.

But I'm not using the 2 oauth end points defined in routes/trikoder_oauth2.yaml

oauth2_authorize:
    path: /oauth/v2/authorize
    defaults: { _controller: Trikoder\Bundle\OAuth2Bundle\Controller\AuthorizationController::indexAction, _method: GET }

oauth2_token:
    path: /oauth/v2/token
    defaults: { _controller: Trikoder\Bundle\OAuth2Bundle\Controller\TokenController::indexAction, _method: POST }

My question is am I missing something and how I would implement the login? Also since the SPA can't contain the client secret how it would use the refresh token as well?

Thanks

cybperic avatar Dec 18 '21 10:12 cybperic