Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDX Issue Action sends JSON but /token expects x-www-form-urlencoded #1403

Open
cvharris opened this issue Apr 19, 2023 · 4 comments
Open

IDX Issue Action sends JSON but /token expects x-www-form-urlencoded #1403

cvharris opened this issue Apr 19, 2023 · 4 comments
Labels

Comments

@cvharris
Copy link

cvharris commented Apr 19, 2023

Describe the bug?

When using the IDX/OIDC module to authenticate a user with responseType: 'interaction_code', say in an SPA/PKCE setup, the IDXResponse after successfully answering all challenges will return an interactionCode and include the issue() action for the /token endpoint. Calling the actions.issue({codeVerifier}) method fails with a 400 Bad Request, because the /token endpoint expects Content-Type: 'x-www-form-urlencoded' but this sends JSON.

Instead, calling authClient.token.exchangeCodeForTokens({interactionCode, codeVerifier }) works as expected because it passes the right Content-Type.

I'm guessing that the IDX module does not use the headings described by the successWithInteractionCode response, which clearly indicates how to talk to /token:

"successWithInteractionCode": {
  "rel": ["create-form"],
  "name": "issue",
  "href": "https://dev-123.okta.com/oauth2/default/v1/token",
  "method": "POST",
  "value": [
    { "name": "grant_type", "required": true, "value": "interaction_code" },
    {
      "name": "interaction_code",
      "required": true,
      "value": "interactionCode"
    },
    {
      "name": "client_id",
      "required": true,
      "value": "clientId"
    },
    { "name": "code_verifier", "required": true }
  ],
  "accepts": "application/x-www-form-urlencoded"
}

What is expected to happen?

curl 'https://dev-123.okta.com/oauth2/default/v1/token' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-raw 'client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Flogin%2Fcallback&grant_type=interaction_code&code_verifier=codeVerifier&interaction_code=interactionCode'

What is the actual behavior?

curl 'https://dev-123.okta.com/oauth2/default/v1/token' \
  -H 'Accept: application/x-www-form-urlencoded' \
  -H 'Content-Type: application/json' \
  --data-raw '{"grant_type":"interaction_code","interaction_code":"InteractionCode","client_id":"clientId","codeVerifier":"codeVerifier","redirectUri":"http://localhost:8080/login/callback"}'

Reproduction Steps?

In a sandbox/developer Okta tenant, setup an application that requires PKCE and is used in an SPA, then allow Interaction Code enabled for the Grant Type. It shouldn't matter what authentication policies are used.

In any of the SPA sample apps, configure the authClient as normal:

const authClient = new OktaAuth({
  clientId: 'clientId',
  redirectUri: 'http://localhost:8080/callback', // or whatever
  issuer: 'yourtenantissuer'
})

then authenticate using IDX. I don't use authClient.idx.authenticate since our app incorporates a number of flows, instead doing this:

const currentTransaction = await authClient.idx.start()

and then chaining proceed depending on the next steps:

let response = await currentTransaction.proceed('identify', {identifier: 'email@gmail.com'})
 // my app happens to use email verification/OTP, but a password should produce the same response after the next two steps
response = await response.proceed('authenticator-verification-data', {authenticator: { id: AuthenticatorKey.OKTA_EMAIL, methodType: 'email' }})
// ...after prompting the user to input the passcode...
response = await response.proceed('challenge-authenticator', {credentials: { passcode: '123456' }})

const { meta: { codeVerifier }} = authClient.transactionManager.load()
// The call below throws the 400 Bad Request
const { tokens } = await response.actions.issue({ codeVerifier })
// This works instead
const { tokens } = await authClient.token.exchangeCodeForTokens({
  codeVerifier,
  interactionCode: response.interactionCode
})

SDK Versions

@okta/okta-auth-js: 7.0.1

Execution Environment

Modern browsers, Chrome

Additional Information?

No response

@cvharris cvharris added the bug label Apr 19, 2023
@cvharris cvharris changed the title IDX Issue Action Sends JSON but /token expects x-www-form-urlencoded IDX Issue Action sends JSON but /token expects x-www-form-urlencoded Apr 19, 2023
@denysoblohin-okta
Copy link
Contributor

Thanks for submitting this issue with detailed information and steps to reproduce.
Internal ref: OKTA-602355
For now please use authClient.token.exchangeCodeForTokens as a workaround

@cvharris
Copy link
Author

cvharris commented Apr 19, 2023

Awesome! I will also add that I had to configure withCredentials: false as well for some reason, otherwise I ran into a CORS issue. Not sure if that's expected or not on the /token endpoint

@shuowu-okta
Copy link
Contributor

Glad the workaround helps! We will fix the issue shortly.

@matijagrcic
Copy link

Had the same issue, this works ok but there's no meta property anymore

So instead of

const { meta: { codeVerifier }} = authClient.transactionManager.load()

used

const { codeVerifier } = authClient.transactionManager.load()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants