Can I exchange a Session Token retrieved via Primary Authentication for an Authorization Code

Hello, my team is developing a client application using Okta as the OpenID Provider. One of our authentication flows has the following requirements:

  1. Flow must be performed by an end user without access to a web browser
  2. MFA sign on policies must be enforced (no Resource Owner Credentials)

Our application is set up to execute a code => token exchange using the Authentication Code Flow, but we need a way to generate an authorization code without the user having access to a web browser.

We are attempting to implement the steps outlined here but have hit a roadblock attempting to generate an authorization code. The steps of the Flow are:

  1. The user generates a sessionToken using curl or some other CLI utility via Primary authentication
curl -s -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
  "username": "${username}",
  "password": "${password}"
}' "https://${oktaDomain}/api/v1/authn" | jq .sessionToken
  1. The user forwards the sessionToken value from the Primary authentication response to our client app.

  2. The client application generates an authorization URL to execute the Authorization Code grant. The sessionToken is used for authentication.

https://{{oktaDomain}}/oauth2/v1/authorize?response_type=code&response_mode=form_post&client_id={{client_id}}&redirect_uri=https://localhost:6050/oauth2/v1/test/code_callback&state={{state}}&scope=openid%20email%20profile%20groups%20offline_access&sessionToken={{sessionToken}}
  1. Using a libcurl wrapper, the client application executes a HTTP GET request at the the authorization url generated in the previous step. The expectation is that Okta will immediately redirect the client app back to itself with the authorization code.

  2. The client application exchanges the authorization code for id/access/refresh tokens.

In theory, this seems like a viable approach - following the steps outlined in this tutorial. However, Okta returns the following error response to the client in step 4 (no redirect).

{"request_id":"2e0738eb-f221-425f-abcb-00826b10b432","timestamp":"1655304888","database":"test","error_code":"GENERAL_ERROR","state":"eyJvY19yZXFfaWQiOiIyZTA3MzhlYi1mMjIxLTQyNWYtYWJjYi0wMDgyNmIxMGI0MzIiLCJvY19kYl9pZCI6ImQ5NjFlOTM5LTE5ZmUtNGNmOS1iYmZiLWUyMGIxYWYwZjMwNCIsIm9jX2Zsb3ciOjJ9","error_description":"the client specified not to prompt, but the user is not logged in."}

Is the linked How-To guide out of date? Is supplying the sessionToken insufficient? Are there additional headers that need to be added to the HTTP GET request in step (4)?

Any additional help is greatly appreciated!!

supplying the sessionToken should be enough. Keep in mind that this is an ephemeral token and has a finite lifetime. Did you try walking through the steps in that article exactly how its written? Are you able to include a sessionToken in an /authorize call made in a browser without needing to login?

No, that’s not exactly how it works. You have the first part right, because you use /authn to authenticate through the API, and that gives you the session token. The error you are getting back is that /authorize does not expect a session token, /authorize is only to initiate an OAuth or OIDC flow.

So, to your first and second requirements out of the way, the MFA has to be handled via YOUR client app when the call to /authn is made. If MFA is required, then the initial response from that call be MFA_REQUIRED and a list of MFA choices the user is enrolled in. You have to make a call to /verify picking which MFA to use, then Okta will return MFA_CHALLENGE and do something like send the SMS one-time-passcode, and you have to call /verify again with the value to finish authentication. Don’t forget to pass the state token from the previous reply back with each subsequent call, that is how Okta remembers what you are doing. Look for “MFA_REQUIRED” in the /authn responses in this doc: Authentication | Okta Developer.

So once all that is done, the user is authenticated and you get a session token back in the final response. I’m not sure what you want to do next. Because you are using /authn, the only reason to use Auth Code is to get an access token to authorize the client application to make calls against the Okta or your own APIs. In that case you can build a client that exchanges the session token for a session cookie using a redirect, see the three ways to do that in sessions documentation at Sessions | Okta Developer. After you do that, then you can initiate your first Auth Code flow passing along that session cookie, and Okta will see the user as authenticated and pass you back the tokens you request. Or to be precise, it passes you back an auth code which you use in another call to retrieve the tokens. :slight_smile: