Most of the relevant code I have tried thus far
Code
Code verifier
fn code_verifier() -> String {
use base64::prelude::{Engine, BASE64_URL_SAFE};
let window = web_sys::window().unwrap();
let crypto = window.crypto().unwrap();
let mut rand_buffer = [0u8; 32];
let rand_bytes = crypto.get_random_values_with_u8_array(&mut rand_buffer);
assert!(rand_bytes.is_ok());
BASE64_URL_SAFE.encode(rand_buffer)
}
fn code_verifier() -> String {
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
thread_rng()
.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect()
}
B64 Encode
fn b64_encode(input: &str) -> String {
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
BASE64_URL_SAFE_NO_PAD
.encode(input)
.replace('=', "")
.replace('+', "-")
.replace('/', "_")
}
Sha256
I had another using Sha256 but forgot to save it anywhere, I doubt it’s the culprit.
fn sha256(input: &str) -> String {
use cryptoxide::digest::Digest;
use cryptoxide::sha2::Sha256;
let mut hasher = Sha256::new();
hasher.input_str(input);
hasher.result_str()
}
Requests
The application is built using Leptos with CSR, for context.
This works fine as far as I can tell, I get a code and the verifier is stored (not sure if this is particularly safe, but the application isn’t public yet so I can change it later) to be reused
#[component]
pub fn LoginPanel() -> impl IntoView {
let code_verifier = code_verifier();
let code_challenge = b64_encode(&sha256(&code_verifier));
let url = url::Url::parse_with_params(
"{domain}/authorize",
vec![
("response_type", "code"),
("code_challenge", &code_challenge),
("code_challenge_method", "S256"),
("client_id", "{client_id}"),
("redirect_uri", "http://127.0.0.1:8081/auth"),
("scope", "openid profile email"),
("audience", "{audience}"),
],
)
.unwrap()
.to_string();
use_local_storage::<String, JsonCodec>("code_verifier")
.1
.set(code_verifier);
view! {
<div>
<a href=url>
"Login"
</a>
</div>
}
}
The next step is where things go awry, I get a 401 error with no details.
#[component]
fn Auth() -> impl IntoView {
let code_verifier = use_local_storage::<String, JsonCodec>("code_verifier")
.0
.get_untracked();
let code = web_sys::UrlSearchParams::new_with_str(
&web_sys::window().unwrap().location().search().unwrap(),
)
.unwrap()
.get("code")
.unwrap();
spawn_local(async move {
let url = url::Url::parse_with_params(
"{domain}/oauth/token",
vec![
("grant_type", "authorization_code"),
("client_id", "{client_id}"),
("code_verifier", code_verifier.as_str()),
("code", code.as_str()),
("redirect_uri", "{redirect_uri}"),
],
)
.unwrap();
let response = reqwasm::http::Request::post(url.as_str())
.header("content-type", "application/x-www-form-urlencoded")
.send()
.await;
log!("{:?}", response);
});
view! {
<div>"Auth"</div>
}
}
I’m following Call Your API Using the Authorization Code Flow with PKCE in Rust, but when trying to call oauth/token
I get a 401 error with no real details and nothing in the logs for me to understand why it’s happening.
Has anyone done this themselves without the Auth0 package/library that is willing to share an example, or can explain what I’m doing wrong here?