Any examples of PKCE flow in Rust?

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?