···19hex = "0.4"
20jwt-compact = { version = "0.8.0", features = ["es256k"] }
21scrypt = "0.11"
22-#lettre = { version = "0.11.18", default-features = false, features = ["pool", "tokio1-rustls", "smtp-transport", "hostname", "builder"] }
23-#lettre = { version = "0.11", default-features = false, features = ["builder", "webpki-roots", "rustls", "aws-lc-rs", "smtp-transport", "tokio1", "tokio1-rustls"] }
24aws-lc-rs = "1.13.0"
25-lettre = { version = "0.11", default-features = false, features = ["builder", "webpki-roots", "rustls", "aws-lc-rs", "smtp-transport", "tokio1", "tokio1-rustls"] }
26rustls = { version = "0.23", default-features = false, features = ["tls12", "std", "logging", "aws_lc_rs"] }
027handlebars = { version = "6.3.2", features = ["rust-embed"] }
28rust-embed = "8.7.2"
29axum-template = { version = "3.0.0", features = ["handlebars"] }
···19hex = "0.4"
20jwt-compact = { version = "0.8.0", features = ["es256k"] }
21scrypt = "0.11"
22+#Leaveing these two cause I think it is needed by the
023aws-lc-rs = "1.13.0"
024rustls = { version = "0.23", default-features = false, features = ["tls12", "std", "logging", "aws_lc_rs"] }
25+lettre = { version = "0.11", default-features = false, features = ["builder", "webpki-roots", "rustls", "aws-lc-rs", "smtp-transport", "tokio1", "tokio1-rustls"] }
26handlebars = { version = "6.3.2", features = ["rust-embed"] }
27rust-embed = "8.7.2"
28axum-template = { version = "3.0.0", features = ["handlebars"] }
+3
src/main.rs
···175 .finish()
176 .expect("failed to create governor config. this should not happen and is a bug");
177000178 let create_session_governor_limiter = create_session_governor_conf.limiter().clone();
179 let sign_in_governor_limiter = sign_in_governor_conf.limiter().clone();
180 let interval = Duration::from_secs(60);
···175 .finish()
176 .expect("failed to create governor config. this should not happen and is a bug");
177178+ // let create_account_limiter_time: Option<String> =
179+ // env::var("GATEKEEPER_CREATE_ACCOUNT_LIMITER_WINDOW").unwrap_or_else(|_| None);
180+181 let create_session_governor_limiter = create_session_governor_conf.limiter().clone();
182 let sign_in_governor_limiter = sign_in_governor_conf.limiter().clone();
183 let interval = Duration::from_secs(60);
+40-17
src/middleware.rs
···1use crate::helpers::json_error_response;
2use axum::extract::Request;
03use axum::http::{HeaderMap, StatusCode};
4use axum::middleware::Next;
5use axum::response::IntoResponse;
···12#[derive(Clone, Debug)]
13pub struct Did(pub Option<String>);
1400000015#[derive(Serialize, Deserialize)]
16pub struct TokenClaims {
17 pub sub: String,
18}
1920pub async fn extract_did(mut req: Request, next: Next) -> impl IntoResponse {
21- let token = extract_bearer(req.headers());
2223- match token {
24- Ok(token) => {
25- match token {
26 None => json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "")
27 .expect("Error creating an error response"),
28- Some(token) => {
29- let token = UntrustedToken::new(&token);
000030 if token.is_err() {
31 return json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "")
32 .expect("Error creating an error response");
···49 .expect("Error creating an error response");
50 }
51 let token = token.expect("Already checked for error,");
52- //Not going to worry about expiration since it still goes to the PDS
53 req.extensions_mut()
54 .insert(Did(Some(token.claims().custom.sub.clone())));
000000055 next.run(req).await
56 }
57 }
···64 }
65}
6667-fn extract_bearer(headers: &HeaderMap) -> Result<Option<String>, String> {
68 match headers.get(axum::http::header::AUTHORIZATION) {
69 None => Ok(None),
70- Some(hv) => match hv.to_str() {
71- Err(_) => Err("Authorization header is not valid".into()),
72- Ok(s) => {
73- // Accept forms like: "Bearer <token>" (case-sensitive for the scheme here)
74- let mut parts = s.splitn(2, ' ');
75- match (parts.next(), parts.next()) {
76- (Some("Bearer"), Some(tok)) if !tok.is_empty() => Ok(Some(tok.to_string())),
77- _ => Err("Authorization header must be in format 'Bearer <token>'".into()),
0000078 }
79 }
80- },
81 }
82}
···1use crate::helpers::json_error_response;
2use axum::extract::Request;
3+use axum::http::header::AUTHORIZATION;
4use axum::http::{HeaderMap, StatusCode};
5use axum::middleware::Next;
6use axum::response::IntoResponse;
···13#[derive(Clone, Debug)]
14pub struct Did(pub Option<String>);
1516+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17+pub enum AuthScheme {
18+ Bearer,
19+ DPoP,
20+}
21+22#[derive(Serialize, Deserialize)]
23pub struct TokenClaims {
24 pub sub: String,
25}
2627pub async fn extract_did(mut req: Request, next: Next) -> impl IntoResponse {
28+ let auth = extract_auth(req.headers());
2930+ match auth {
31+ Ok(auth_opt) => {
32+ match auth_opt {
33 None => json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "")
34 .expect("Error creating an error response"),
35+ Some((scheme, token_str)) => {
36+ // For Bearer, validate JWT and extract DID from `sub`.
37+ // For DPoP, we currently only pass through and do not validate here; insert None DID.
38+ // match scheme {
39+ // AuthScheme::Bearer => {
40+ let token = UntrustedToken::new(&token_str);
41 if token.is_err() {
42 return json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "")
43 .expect("Error creating an error response");
···60 .expect("Error creating an error response");
61 }
62 let token = token.expect("Already checked for error,");
63+ // Not going to worry about expiration since it still goes to the PDS
64 req.extensions_mut()
65 .insert(Did(Some(token.claims().custom.sub.clone())));
66+ // }
67+ // AuthScheme::DPoP => {
68+ // // No DID extraction from DPoP here; leave None
69+ // req.extensions_mut().insert(Did(None));
70+ // }
71+ // }
72+73 next.run(req).await
74 }
75 }
···82 }
83}
8485+fn extract_auth(headers: &HeaderMap) -> Result<Option<(AuthScheme, String)>, String> {
86 match headers.get(axum::http::header::AUTHORIZATION) {
87 None => Ok(None),
88+ Some(hv) => {
89+ match hv.to_str() {
90+ Err(_) => Err("Authorization header is not valid".into()),
91+ Ok(s) => {
92+ // Accept forms like: "Bearer <token>" or "DPoP <token>" (case-sensitive for the scheme here)
93+ let mut parts = s.splitn(2, ' ');
94+ match (parts.next(), parts.next()) {
95+ (Some("Bearer"), Some(tok)) if !tok.is_empty() =>
96+ Ok(Some((AuthScheme::Bearer, tok.to_string()))),
97+ (Some("DPoP"), Some(tok)) if !tok.is_empty() =>
98+ Ok(Some((AuthScheme::DPoP, tok.to_string()))),
99+ _ => Err("Authorization header must be in format 'Bearer <token>' or 'DPoP <token>'".into()),
100+ }
101 }
102 }
103+ }
104 }
105}