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}