Implement PKCE for Mastodon auth

O01eg
Apr 13, 2026, 9:46 AM
CJDJTP27H2CZOFMKYVGCLYTKIFCRL3AZXKULFDFJ2NP4V22C5JFAC

Dependencies

  • [2] BXEBSO25 Update base64 dependency
  • [3] 46JE6BRG Update pct_str
  • [4] LTQCLSBU Split database usage in pages
  • [5] WVHXYKCV Add postgresql pools
  • [6] H6GGDVHW Show auth info in reset password page
  • [7] 4TRDTVC4 Fix login page
  • [8] G4JCZ5F7 Store try to register on Mastodon domain
  • [9] TEB4R7OU Add form to join game
  • [10] KDKRTAYJ Register application on Mastodon domain
  • [11] FUCFD4UV Add log in and log out support
  • [12] HTYEGVBU Add data to reset password page
  • [13] HBDTKI2B Add auth info to password reset
  • [14] MGRTVGLJ Redirect to Mastodon domain
  • [15] W2AVMCLO Log in with Mastodon
  • [16] 564OK4ZC Add state parameter to Mastodon query
  • [*] WW3KRXX6 Add page for reset game password
  • [*] EVP2FSBH Split index page
  • [*] 4MZ4VIR7 Initial commit
  • [*] BCXEUKX6 Add config, static files and web server
  • [*] CMA5SKJ3 Copy turns Atom generator

Change contents

  • replacement in src/templates/login.html at line 30
    [4.93][4.93:157]()
    <legend>Social Auth with Mastodon (not yet supported)</legend>
    [4.93]
    [4.157]
    <legend>Social Auth with Mastodon</legend>
  • replacement in src/pages/reset_game_pwd.rs at line 42
    [4.366][4.1022:1083](),[4.189][4.1022:1083]()
    let token = match Uuid::parse_str(&token.into_inner()) {
    [4.366]
    [4.1083]
    let token = match Uuid::parse_str(&token.into_inner().replace("=", "")) {
  • replacement in src/pages/mod.rs at line 50
    [4.41][4.41:104]()
    pub cache_mastodon_state: Mutex<TtlCache<String, String>>,
    [4.41]
    [4.1056]
    pub cache_mastodon_state: Mutex<TtlCache<String, (String, String)>>,
  • edit in src/pages/log_in.rs at line 5
    [4.2709]
    [4.2709]
    use rand::{rng, RngExt};
    use sha2::{Digest, Sha256};
  • edit in src/pages/log_in.rs at line 10
    [4.2710]
    [4.2710]
    use base64::Engine;
  • edit in src/pages/log_in.rs at line 260
    [4.2342]
    [4.2342]
    code_verifier: &str,
  • replacement in src/pages/log_in.rs at line 268
    [4.2657][4.2657:2777]()
    "client_id={}&client_secret={}&redirect_uri={}&grant_type=authorization_code&code={}&scopes=read:accounts",
    [4.2657]
    [3.487]
    "client_id={}&client_secret={}&redirect_uri={}&grant_type=authorization_code&code={}&scopes=read:accounts&code_verifier={}",
  • replacement in src/pages/log_in.rs at line 272
    [3.783][3.783:872]()
    pct_str::PctString::encode(code.chars(), pct_str::UriReserved::Any).as_str()
    [3.783]
    [4.3142]
    pct_str::PctString::encode(code.chars(), pct_str::UriReserved::Any).as_str(),
    code_verifier
  • edit in src/pages/log_in.rs at line 560
    [4.1]
    [4.105]
    let mut random_bytes = [0u8; 32];
    rng().fill(&mut random_bytes);
    let code_verifier = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(random_bytes);
    let mut hasher = Sha256::new();
    hasher.update(code_verifier.as_bytes());
    let code_challenge = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(hasher.finalize());
  • replacement in src/pages/log_in.rs at line 569
    [4.7436][4.150:285]()
    let location = format!("https://{}/oauth/authorize?client_id={}&scope=read:accounts&redirect_uri={}&response_type=code&state={}",
    [4.7436]
    [4.7562]
    let location = format!("https://{}/oauth/authorize?client_id={}&scope=read:accounts&redirect_uri={}&response_type=code&state={}&code_challenge_method=S256&code_challenge={}",
  • replacement in src/pages/log_in.rs at line 573
    [3.1062][4.379:395](),[4.379][4.379:395]()
    state);
    [3.1062]
    [4.395]
    state,
    code_challenge);
  • replacement in src/pages/log_in.rs at line 580
    [4.507][4.507:527]()
    domain,
    [4.507]
    [4.527]
    (domain, code_verifier),
  • replacement in src/pages/log_in.rs at line 629
    [4.1004][4.1004:1190]()
    if cached_data.is_none_or(|x| x != domain) {
    log::warn!("Unknown state for mastodon redirect: {}", state);
    return HttpResponse::BadRequest().body("Incorrect");
    }
    [4.1004]
    [4.1190]
    let code_verifier = match cached_data {
    None => {
    log::warn!("Unknown state for mastodon redirect: {}", state);
    return HttpResponse::BadRequest().body("Incorrect");
    }
    Some((cached_domain, code_verifier)) => {
    if cached_domain != domain {
    log::warn!(
    "Unknown domain and state for mastodon redirect: {} domain {} != {}",
    state,
    domain,
    cached_domain
    );
    return HttpResponse::BadRequest().body("Incorrect");
    } else {
    code_verifier
    }
    }
    };
  • edit in src/pages/log_in.rs at line 692
    [4.10080]
    [4.10080]
    &code_verifier,
  • edit in Cargo.toml at line 29
    [2.16]
    [4.2575]
    [dependencies.rand]
    version = "0.10"
    features = ["thread_rng"]
  • edit in Cargo.lock at line 1004
    [22.4438]
    [21.20518]
    "rand 0.10.1",