knpuniversity / oauth2-client-bundle

Easily talk to an OAuth2 server for social functionality in Symfony
https://symfonycasts.com
MIT License
787 stars 145 forks source link

Usage with self hosted login #375

Closed bbarrick-lawson closed 2 years ago

bbarrick-lawson commented 2 years ago

I successfully set this up with Okta hosted authentication but I'm looking at switching to the self hosted Okta JavaScript plugin. So the login form is provided by the Okta sdk and called via JavaScript, the library makes the call out to Okta with the username/password and the response is sent back to the redirect URI. I'm trying to understand how to continue the authentication with the app once I have the response, is it possible to utilize this library with the self hosted plugin to handle the authentication response?

weaverryan commented 2 years ago

Hey!

After you submit the Okta login form, are you eventually redirected back to some URL on your site that looks like /check?authorization_code=.......? Or does it all happen in JavaScript? As long as your user DOES end up on a URL with ...?authorization_code=, then you can use this library. You would basically just be using "half" of it. For example, you would not need to implement connect() in this example, but you would need to implement connectCheckAction (or implement that "side" of things with an authenticator).

Cheers!

bbarrick-lawson commented 2 years ago

@weaverryan thank you - It does redirect back to a URL. It seems everything is working up till I get back to authenticate where it throws a "Invalid state parameter passed in a callback URL."

This is the url response https://localhost:8000/connect/okta/check?code=tzUukstate=vVnS

So state is there I'm just not sure if the parameter it's looking for is different but I'm working through that at the moment.

bbarrick-lawson commented 2 years ago

It seems the only issue is in the authenticate function where it sets $accessToken = $this->fetchAccessToken($client);

Do I need to somehow integrate data from the $request object which contains the code and state values that is passed into the authenticate() function in with that client object?

public function authenticate(Request $request): SelfValidatingPassport
    {
        $client = $this->clientRegistry->getClient('okta');
        $accessToken = $this->fetchAccessToken($client);

        return new SelfValidatingPassport(
            new UserBadge($accessToken->getToken(), function () use ($accessToken, $client) {

                $oktaUser = $client->fetchUserFromToken($accessToken);
                $email = $oktaUser->getEmail();

                $existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $email]);

                if ($existingUser) {
                    return $existingUser;
                }

                return $this->entityManager->getRepository(User::class)->findOneBy(['email' => $email]);

            })
        );
    }
bbarrick-lawson commented 2 years ago

It looks like in order to get the access token I have to use that code in the response to go back to Okta and get it. I'm not sure if I can use fetchAccessToken or getAccessToken and pass along that code/state to get the access token I need.

weaverryan commented 2 years ago

Sorry for the slow reply. Based on the URL - https://localhost:8000/connect/okta/check?code=tzUukstate=vVnS - it looks like the OAuth server is not passing back any state. That is not idea - state is an extra layer of security - but that's ok. In this bundle, you can set use_state: false under your client in the YAML configuration to disable this check.

Cheers!

bbarrick-lawson commented 2 years ago

@weaverryan thank you, I was just about to reply to this just in case anyone else runs in to this issue.

The response had the state but for some reason wasn't passing it to the client object.

I had to create a manual API call to the /token endpoint to get the access code. Then I imported the League\OAuth2\Client\Token\AccessToken class from the library and create a new AccessToken object = to the response back from the API call that contained the access code. At this point I was able to authenticate with Okta and the app just as I was before when using the Okta hosted login.