FusionAuth / fusionauth-issues

FusionAuth issue submission project
https://fusionauth.io
91 stars 12 forks source link

API endpoint "Complete an OpenID Connect Login" is not working as expected #2191

Open pkoz opened 1 year ago

pkoz commented 1 year ago

API endpoint "Complete an OpenID Connect Login" is not working as expected

Description

Trying to set up a 3rd party Identity Provider using Generic OIDC for a custom login page.

The process fails at the last step, where I try to exchange the code I received via callback to FusionAuth's access token using the Complete an OpenID Connect Login. The 3rd party Identity Provider receives a code (43 characters long) that does not look like the code Ory created but rather like a code created by FusionAuth.

Affects versions

Verified the problem for versions:

Steps to reproduce

Identity Provider: ory.sh (Ory Hydra)

Settings, the Identity Provider (Ory):

Steps to reproduce the behavior:

  1. Request to https://xxx.fusionauth.io/oauth2/authorize with query parameters: ​
    • client_id= application ID enabled in the IdP config
    • redirect_uri= URL encoded, own callback
    • response_type=code
    • tenantId= the tenant ID that has the application enabled in the IdP config
    • scope= URL encoded openid offline_access
    • idp_hint= ID of the Identity Provider configuration ​
  2. Have been redirected to Ory, got the authorization page, and confirmed authorization for scope: email, name, openid. ​
  3. Got redirection to the callback provided in point 1 as redirect_uri, with query parameters: ​
    • code=*** 43 characters long code, looks like Base64 plus - and _
    • userState=Authenticated
    • locale=en_GB ​ Confirmed in Ory web console that FusionAuth is listed as an authorized OAuth2 application with scope: email, name, openid. ​ All looks good so far. ​
  4. As the next step, I tried to exchange the 43 characters long code for FusionAuth's access token (JWT): ​ Request POST to https://xxx.fusionauth.io/api/identity-provider/login with Content-Type: application/json and body: ​ ​
    {
    "applicationId": "<FusionAuth's application ID>",
    "data": {
        "code": "43 characters-long code",
        "redirect_uri": "same as redirect_uri from point 1"
    },
    "identityProviderId": "FusonAuth's ID of the IdP configuration"
    }

  5. Response: HTTP 401: ​
    {"generalErrors":[
    {"code":"[ExternalAuthenticationException]OpenIDConnectToken","message":"A request to the OpenID Connect Token API has failed. Unable to complete this login request."}
    ]}

  6. Event Log: ​
    Request to the [https://xxx.projects.oryapis.com/oauth2/token] endpoint failed. Status code [400].
    ​
    Error response is 
    {
    "error" : "invalid_grant",
    "error_description" : "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. not_found"
    }

    Expected behavior

The request to https://xxx.fusionauth.io/api/identity-provider/login should return FusionAuth's JWT and a refresh token, the same as for any other FusionAuth's login flow.

Additional context

  1. I've implemented a simple OIDC flow with the same exact client configuration in Ory, without FusionAuth, and it worked as expected. Conclusion: problems described above are not caused by Ory. ​
  2. For investigation purposes, I've created my own public endpoint where I was able to see request details. ​ When replaced Ory's /oauth2/token with my endpoint, I discovered that FusionAuth does one extra and unexpected call to /oauth2/token between point 2 and point 3 above, i.e., after receiving successful redirection from Ory (with code apparently) and before sending the result to my callback. FusionAuth calls /oauth2/token with Ory's code but does not keep the access token or refresh token in the internal state, or at least does not expose access/refresh tokens through API or in the callback. Instead, FusionAuth returns to the callback a new 43-character code. ​ The problem is, when the 43-character code is provided to http://xxx.fusionauth.io/api/identity-provider/login in data.code, it seems to be sent to Ory. Obviously, Ory does not know what it is and returns an error. ​

    Additional questions:

    ​1. Why FusionAuth calls IdP's /oauth2/token before it calls the callback?

  3. What is this 43-character code FusionAuth sends to the callback if I can't use it for anything? Most importantly, I cannot use it for `http://xxx.fusionauth.io/api/identity-provider/login.
robotdan commented 1 year ago

The /api/identity-provider/login API is intended to complete a login with a 3rd party IdP. If you are only attempting to integrate FusionAuth with Ory, I don't think this is the API you want to use.

The correct OIDC integration will use /oauth2/token to exchange the auth code for an access token.

The summary of the integration will be:

  1. GET /oauth2/authorize
  2. Consume the callback which includes the auth code. code=<code>
  3. Exchange the auth code for an access token. POST /oauth2/token

I would expect that Ory will do all of this for you if you just point it at the FusionAuth well-known OIDC configuration endpoint /.well-known/openid-configuration.