this repo has no description
1use serde::{Deserialize, Serialize}; 2use sqlx::PgPool; 3 4pub mod extractor; 5pub mod token; 6pub mod verify; 7 8pub use extractor::{BearerAuth, AuthError, extract_bearer_token_from_header}; 9pub use token::{ 10 create_access_token, create_refresh_token, create_service_token, 11 create_access_token_with_metadata, create_refresh_token_with_metadata, 12 TokenWithMetadata, 13 TOKEN_TYPE_ACCESS, TOKEN_TYPE_REFRESH, TOKEN_TYPE_SERVICE, 14 SCOPE_ACCESS, SCOPE_REFRESH, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED, 15}; 16pub use verify::{get_did_from_token, get_jti_from_token, verify_token, verify_access_token, verify_refresh_token}; 17 18pub struct AuthenticatedUser { 19 pub did: String, 20 pub key_bytes: Option<Vec<u8>>, 21 pub is_oauth: bool, 22} 23 24pub async fn validate_bearer_token( 25 db: &PgPool, 26 token: &str, 27) -> Result<AuthenticatedUser, &'static str> { 28 validate_bearer_token_with_options(db, token, false).await 29} 30 31pub async fn validate_bearer_token_allow_deactivated( 32 db: &PgPool, 33 token: &str, 34) -> Result<AuthenticatedUser, &'static str> { 35 validate_bearer_token_with_options(db, token, true).await 36} 37 38async fn validate_bearer_token_with_options( 39 db: &PgPool, 40 token: &str, 41 allow_deactivated: bool, 42) -> Result<AuthenticatedUser, &'static str> { 43 let did_from_token = get_did_from_token(token).ok(); 44 45 if let Some(ref did) = did_from_token { 46 if let Some(user) = sqlx::query!( 47 "SELECT k.key_bytes, k.encryption_version, u.deactivated_at, u.takedown_ref 48 FROM users u 49 JOIN user_keys k ON u.id = k.user_id 50 WHERE u.did = $1", 51 did 52 ) 53 .fetch_optional(db) 54 .await 55 .ok() 56 .flatten() 57 { 58 if !allow_deactivated && user.deactivated_at.is_some() { 59 return Err("AccountDeactivated"); 60 } 61 if user.takedown_ref.is_some() { 62 return Err("AccountTakedown"); 63 } 64 65 let decrypted_key = match crate::config::decrypt_key(&user.key_bytes, user.encryption_version) { 66 Ok(k) => k, 67 Err(_) => return Err("KeyDecryptionFailed"), 68 }; 69 70 if let Ok(token_data) = verify_access_token(token, &decrypted_key) { 71 let session_exists = sqlx::query_scalar!( 72 "SELECT 1 as one FROM session_tokens WHERE did = $1 AND access_jti = $2 AND access_expires_at > NOW()", 73 did, 74 token_data.claims.jti 75 ) 76 .fetch_optional(db) 77 .await 78 .ok() 79 .flatten(); 80 81 if session_exists.is_some() { 82 return Ok(AuthenticatedUser { 83 did: did.clone(), 84 key_bytes: Some(decrypted_key), 85 is_oauth: false, 86 }); 87 } 88 } 89 } 90 } 91 92 if let Ok(oauth_info) = crate::oauth::verify::extract_oauth_token_info(token) { 93 if let Some(oauth_token) = sqlx::query!( 94 r#"SELECT t.did, t.expires_at, u.deactivated_at, u.takedown_ref 95 FROM oauth_token t 96 JOIN users u ON t.did = u.did 97 WHERE t.token_id = $1"#, 98 oauth_info.token_id 99 ) 100 .fetch_optional(db) 101 .await 102 .ok() 103 .flatten() 104 { 105 if !allow_deactivated && oauth_token.deactivated_at.is_some() { 106 return Err("AccountDeactivated"); 107 } 108 if oauth_token.takedown_ref.is_some() { 109 return Err("AccountTakedown"); 110 } 111 112 let now = chrono::Utc::now(); 113 if oauth_token.expires_at > now { 114 return Ok(AuthenticatedUser { 115 did: oauth_token.did, 116 key_bytes: None, 117 is_oauth: true, 118 }); 119 } 120 } 121 } 122 123 Err("AuthenticationFailed") 124} 125 126#[derive(Debug, Serialize, Deserialize)] 127pub struct Claims { 128 pub iss: String, 129 pub sub: String, 130 pub aud: String, 131 pub exp: usize, 132 pub iat: usize, 133 #[serde(skip_serializing_if = "Option::is_none")] 134 pub scope: Option<String>, 135 #[serde(skip_serializing_if = "Option::is_none")] 136 pub lxm: Option<String>, 137 pub jti: String, 138} 139 140#[derive(Debug, Serialize, Deserialize)] 141pub struct Header { 142 pub alg: String, 143 pub typ: String, 144} 145 146#[derive(Debug, Serialize, Deserialize)] 147pub struct UnsafeClaims { 148 pub iss: String, 149 pub sub: Option<String>, 150} 151 152// fancy boy TokenData equivalent for compatibility/structure 153pub struct TokenData<T> { 154 pub claims: T, 155}