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

invalid grant type when trying with google #126

Open
chris-kruining opened this issue Aug 17, 2023 · 8 comments
Open

invalid grant type when trying with google #126

chris-kruining opened this issue Aug 17, 2023 · 8 comments

Comments

@chris-kruining
Copy link

I am trying to implement SSO for a leptos app I am making but I am stuck at the code exchange phase. I've tried a lot of things but am basically about to give up.

I have the following code:

async fn get_client() -> Result<MyClient, anyhow::Error> {
    let provider_metadata = CoreProviderMetadata::discover_async(
        IssuerUrl::new("https://accounts.google.com".to_string())?,
        async_http_client,
    ).await?;

    Ok(
        CoreClient::from_provider_metadata(
            provider_metadata,
            ClientId::new("***".to_string()),
            Some(ClientSecret::new("***".to_string())),
        )
        .set_redirect_uri(RedirectUrl::new("http://localhost:3000/auth/redirect".to_string())?)
    )
}

pub async fn start() -> Result<(url::Url, openidconnect::Nonce, openidconnect::PkceCodeVerifier), anyhow::Error> {
    let client: MyClient = get_client().await?;

    let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();

    let (auth_url, _, nonce) = client
        .authorize_url(
            CoreAuthenticationFlow::AuthorizationCode,
            CsrfToken::new_random,
            Nonce::new_random,
        )
        // Set the desired scopes.
        .add_scope(Scope::new("email".to_string()))
        // .add_scope(Scope::new("read".to_string()))
        // .add_scope(Scope::new("write".to_string()))
        // Set the PKCE code challenge.
        .set_pkce_challenge(pkce_challenge)
        .url();

    Ok((auth_url, nonce, pkce_verifier))
}

pub async fn exchange(code: String, nonce: String, pkce_verifier: String) -> Result<(), anyhow::Error> {
    use openidconnect::{ TokenResponse, PkceCodeVerifier };

    log!("{{ code: {},\nnonce: {},\npkce: {} }}", code, nonce, pkce_verifier);

    let client = get_client().await?;

    let token_response = client
        .exchange_code(AuthorizationCode::new(code))
        .add_extra_param("client_id", "***")
        .add_extra_param("client_secret", "***")
        .set_pkce_verifier(PkceCodeVerifier::new(pkce_verifier))
        .request_async(async_http_client)
        .await?;

    let _id_token = token_response.id_token().ok_or_else(|| anyhow!("Server did not return an ID token"))?;

    Ok(())
}

but whenever I call exchange I get a 400 response from google
I have notices a couple of things. According to google I need the client id and secret as fields in the post data, those are missing (I believe those are encoded into the authorization header?), so I added those in the dirty way with the extra params. another thing I noticed is that the code I have is a lot longer than the one shown in the google documentation, although I reckon this is not a problem.

image
image
image

p.s. I redacted the client id and secret for hopefully obvious reasons

@ramosbugs
Copy link
Owner

Hmm... the Google example seems to work with the client credentials in the Authorization header rather than the body, but you can change this behavior by calling client.set_auth_type(AuthType::RequestBody).

The other main difference I see with the Google example is PKCE. I would try temporarily disabling PKCE and making sure it works in case you're somehow passing in a mismatched PKCE verifier.

Since you included a browser devtools screenshot, does this mean you're using the crate via WASM? It should work using WASM, but I haven't specifically tested WASM against Google, so it's possible there's a browser interoperability quirk I'm not aware of. Although, if this happening inside a browser, it probably doesn't make sense to use a client secret since a browser app isn't able to keep a secret away from end users. I believe Google lets you specify the app type during registration so that it doesn't assign a client secret for public apps.

@chris-kruining
Copy link
Author

I am indeed running in WASM. I've also tried to run it without the PKCE, but I have the same result

@ramosbugs
Copy link
Owner

Did you try set_auth_type? The 400 error text should at least change in that scenario since there should no longer be an Authorization header. The updated error will hopefully point us in the right direction.

@chris-kruining
Copy link
Author

sorry for the late reaction.

I did try that yes, but it didn't make a difference as I had already managed to move those values by setting custom values. Which I now no longer need to do.

the thing I find most weird though is that the request seems fine. but I don't get the error, like there isn't a Authorization header, so there are no credentials in the headers as far as I can tell

@ramosbugs
Copy link
Owner

the missing Authorization header could be a CORS issue that might be solved by calling fetch_credentials_include on the reqwest client passed into this crate. See also https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#sending_a_request_with_credentials_included

@beckend
Copy link

beckend commented Sep 28, 2023

What would an example code look like to pass a customized reqwest module? The docs says impl this trait but it's very vague.

@beckend
Copy link

beckend commented Sep 28, 2023

Actually never mind, I found: https://github.com/ramosbugs/oauth2-rs/blob/main/src/reqwest.rs

@dakom
Copy link

dakom commented Apr 12, 2024

I created a fullstack Rust/WASM example here: https://github.com/dakom/dominator-workers-fluent-auth

Frontend is using Dominator, but the oauth/openid part happens on the backend, using Cloudflare Workers in Rust/WASM

I know this is not a pretty solution, but it works: a custom http_client impl using native web_sys Request/Response types. No live demo there but I've tested with beta client id/secrets w/ both Google and Facebook:

https://github.com/dakom/dominator-workers-fluent-auth/blob/5b78605c10e44914d1164951994acc0816909ec6/workers/api/src/auth/handler/openid.rs#L161

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

No branches or pull requests

4 participants