Access Token Returned as JWE Instead of JWT – How to Force JWT?

I’m working on a Rust-based full-stack app that uses Auth0 + PKCE flow to authenticate users via a Yew SPA frontend.
The project is open-source and available on github
organization: opendrafts-rs-os
repository: openapi-axum-server
branch: [auth0-part2-gui]
dir: gui

:wrench: Project Overview

  • Frontend:
    • Framework: [Yew] (Rust/WASM SPA)
    • Directory: gui/ (separate frontend)
  • Backend:
    • Framework: [Axum]
  • Auth Flow: Authorization Code Flow with PKCE
  • Auth0 app config (via .env file):

env

You add .env

CLIENT_ID="xxx"
DOMAIN="xxx.eu.auth0.com"
REDIRECT_URI="http://localhost:8080/callback"

:red_exclamation_mark:Problem
After the user logs in, I receive an access_token in JWE format, which I cannot decrypt (no private key).
I would like to receive the access_token as a signed JWT (RS256), so I can validate it from the Axum backend using public JWKs.

What I’ve Tried
Used the example app in gui/.

Set up .env with the client config (see above).

Exchanged code using the function below (Yew/wasm code):

pub async fn exchange_code_for_token(
    code: &str,
    client_id: &str,
    domain: &str,
    redirect_uri: &str,
) -> Result<TokenResponse, String> {
    let verifier = web_sys::window()
        .and_then(|w| w.session_storage().ok().flatten())
        .and_then(|s| s.get_item("code_verifier").ok().flatten())
        .ok_or("missing code_verifier in sessionStorage")?;

    let body = format!(
        "grant_type=authorization_code&client_id={client_id}&code={code}\
        &redirect_uri={redirect_uri}&code_verifier={verifier}"
    );

    let url = format!("https://{domain}/oauth/token");

    let builder = Request::post(&url)
        .header("Content-Type", "application/x-www-form-urlencoded")
        .body(body)
        .map_err(|e| format!("{e}"))?;

    let response = builder.send().await.map_err(|e| format!("{e}"))?;

    if !response.ok() {
        let status = response.status();
        let text = response.text().await.unwrap_or_else(|_| "empty".into());
        return Err(format!("status: {status}: {text}"));
    }

    let token: TokenResponse = response.json().await.map_err(|e| format!("JSON: {e}"))?;
    Ok(token)
}

:red_question_mark:My Questions

  1. How do I configure Auth0 (app or API settings) so that the returned access_token is a JWT, not a JWE?
  2. Is it a matter of:
  • changing the API signing algorithm from “RS256” to something else?
  • setting a proper audience during token request?
  1. Can SPAs using PKCE ever receive JWT access tokens, or is JWE the default for security reasons?
  2. Should I enable/disable any “OIDC Conformant” or “Allow Skipping User Consent” options?

:test_tube: Extra Info

  • The access_token returned starts like:
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ...
  • The id_token is a readable JWT – only the access token is JWE.

:folded_hands: All suggestions are welcome!

To receive a signed JWT access_token (RS256) instead of a JWE or opaque token from Auth0 in your Yew SPA, you must define an API in your Auth0 dashboard and then explicitly include that API’s Identifier as the audience parameter in your frontend’s token exchange request to Auth0’s /oauth/token endpoint. This tells Auth0 the access_token is for a specific API, prompting it to issue a self-contained JWT that your Axum backend can validate using Auth0’s public JWKs. Setting the API’s signing algorithm to RS256 is also crucial for this validation. SPAs using PKCE can indeed receive JWT access tokens when an audience is properly specified.

2 Likes

Unfortunately, adding the audience and setting up the API still seems insufficient — I’m still receiving a JWE in return.

It wouldn’t be a problem if I could verify it on the backend — in fact, I’d even say it would be safer that way.

Here’s the updated code in the exchange_code_for_token method:

let body = format!(
    "grant_type=authorization_code\
    &client_id={client_id}\
    &code={code}\
    &redirect_uri={redirect_url_url_encoding}\
    &code_verifier={verifier}\
    &audience={audience_url_encoding}",
);

I also see this issue, after configuring my application in auth0 dashboard to authorize my api. I pass the api identifier as the audience in my app which is set up as “Native” and still see a JWE, not a JWT

1 Like

Hi team,

Thank you for reaching out to us!

To solve the issue of the access token being returned as a JWE instead of an expected JWT, I would recommend looking through the following documentations : Get Access Tokens and Opaque Versus JWT Access Token . You will need to include the audience parameter in the token request, which should return the access token in JWT format.

You can view this following topic that goes over this as well for more information : JWE received instead of standard JWS

Hope this helped!
Gerald