this repo has no description
1use super::{Claims, Header, TokenData, UnsafeClaims};
2use super::token::{TOKEN_TYPE_ACCESS, TOKEN_TYPE_REFRESH, SCOPE_ACCESS, SCOPE_REFRESH, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED};
3use anyhow::{Context, Result, anyhow};
4use base64::Engine as _;
5use base64::engine::general_purpose::URL_SAFE_NO_PAD;
6use chrono::Utc;
7use hmac::{Hmac, Mac};
8use k256::ecdsa::{Signature, SigningKey, VerifyingKey, signature::Verifier};
9use sha2::Sha256;
10use subtle::ConstantTimeEq;
11type HmacSha256 = Hmac<Sha256>;
12pub fn get_did_from_token(token: &str) -> Result<String, String> {
13 let parts: Vec<&str> = token.split('.').collect();
14 if parts.len() != 3 {
15 return Err("Invalid token format".to_string());
16 }
17 let payload_bytes = URL_SAFE_NO_PAD
18 .decode(parts[1])
19 .map_err(|e| format!("Base64 decode failed: {}", e))?;
20 let claims: UnsafeClaims =
21 serde_json::from_slice(&payload_bytes).map_err(|e| format!("JSON decode failed: {}", e))?;
22 Ok(claims.sub.unwrap_or(claims.iss))
23}
24pub fn get_jti_from_token(token: &str) -> Result<String, String> {
25 let parts: Vec<&str> = token.split('.').collect();
26 if parts.len() != 3 {
27 return Err("Invalid token format".to_string());
28 }
29 let payload_bytes = URL_SAFE_NO_PAD
30 .decode(parts[1])
31 .map_err(|e| format!("Base64 decode failed: {}", e))?;
32 let claims: serde_json::Value =
33 serde_json::from_slice(&payload_bytes).map_err(|e| format!("JSON decode failed: {}", e))?;
34 claims.get("jti")
35 .and_then(|j| j.as_str())
36 .map(|s| s.to_string())
37 .ok_or_else(|| "No jti claim in token".to_string())
38}
39pub fn verify_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> {
40 verify_token_internal(token, key_bytes, None, None)
41}
42pub fn verify_access_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> {
43 verify_token_internal(
44 token,
45 key_bytes,
46 Some(TOKEN_TYPE_ACCESS),
47 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]),
48 )
49}
50pub fn verify_refresh_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> {
51 verify_token_internal(
52 token,
53 key_bytes,
54 Some(TOKEN_TYPE_REFRESH),
55 Some(&[SCOPE_REFRESH]),
56 )
57}
58pub fn verify_access_token_hs256(token: &str, secret: &[u8]) -> Result<TokenData<Claims>> {
59 verify_token_hs256_internal(
60 token,
61 secret,
62 Some(TOKEN_TYPE_ACCESS),
63 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]),
64 )
65}
66pub fn verify_refresh_token_hs256(token: &str, secret: &[u8]) -> Result<TokenData<Claims>> {
67 verify_token_hs256_internal(
68 token,
69 secret,
70 Some(TOKEN_TYPE_REFRESH),
71 Some(&[SCOPE_REFRESH]),
72 )
73}
74fn verify_token_internal(
75 token: &str,
76 key_bytes: &[u8],
77 expected_typ: Option<&str>,
78 allowed_scopes: Option<&[&str]>,
79) -> Result<TokenData<Claims>> {
80 let parts: Vec<&str> = token.split('.').collect();
81 if parts.len() != 3 {
82 return Err(anyhow!("Invalid token format"));
83 }
84 let header_b64 = parts[0];
85 let claims_b64 = parts[1];
86 let signature_b64 = parts[2];
87 let header_bytes = URL_SAFE_NO_PAD
88 .decode(header_b64)
89 .context("Base64 decode of header failed")?;
90 let header: Header =
91 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?;
92 if let Some(expected) = expected_typ {
93 if header.typ != expected {
94 return Err(anyhow!("Invalid token type: expected {}, got {}", expected, header.typ));
95 }
96 }
97 let signature_bytes = URL_SAFE_NO_PAD
98 .decode(signature_b64)
99 .context("Base64 decode of signature failed")?;
100 let signature = Signature::from_slice(&signature_bytes)
101 .map_err(|e| anyhow!("Invalid signature format: {}", e))?;
102 let signing_key = SigningKey::from_slice(key_bytes)?;
103 let verifying_key = VerifyingKey::from(&signing_key);
104 let message = format!("{}.{}", header_b64, claims_b64);
105 verifying_key
106 .verify(message.as_bytes(), &signature)
107 .map_err(|e| anyhow!("Signature verification failed: {}", e))?;
108 let claims_bytes = URL_SAFE_NO_PAD
109 .decode(claims_b64)
110 .context("Base64 decode of claims failed")?;
111 let claims: Claims =
112 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?;
113 let now = Utc::now().timestamp() as usize;
114 if claims.exp < now {
115 return Err(anyhow!("Token expired"));
116 }
117 if let Some(scopes) = allowed_scopes {
118 let token_scope = claims.scope.as_deref().unwrap_or("");
119 if !scopes.contains(&token_scope) {
120 return Err(anyhow!("Invalid token scope: {}", token_scope));
121 }
122 }
123 Ok(TokenData { claims })
124}
125fn verify_token_hs256_internal(
126 token: &str,
127 secret: &[u8],
128 expected_typ: Option<&str>,
129 allowed_scopes: Option<&[&str]>,
130) -> Result<TokenData<Claims>> {
131 let parts: Vec<&str> = token.split('.').collect();
132 if parts.len() != 3 {
133 return Err(anyhow!("Invalid token format"));
134 }
135 let header_b64 = parts[0];
136 let claims_b64 = parts[1];
137 let signature_b64 = parts[2];
138 let header_bytes = URL_SAFE_NO_PAD
139 .decode(header_b64)
140 .context("Base64 decode of header failed")?;
141 let header: Header =
142 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?;
143 if header.alg != "HS256" {
144 return Err(anyhow!("Expected HS256 algorithm, got {}", header.alg));
145 }
146 if let Some(expected) = expected_typ {
147 if header.typ != expected {
148 return Err(anyhow!("Invalid token type: expected {}, got {}", expected, header.typ));
149 }
150 }
151 let signature_bytes = URL_SAFE_NO_PAD
152 .decode(signature_b64)
153 .context("Base64 decode of signature failed")?;
154 let message = format!("{}.{}", header_b64, claims_b64);
155 let mut mac = HmacSha256::new_from_slice(secret)
156 .map_err(|e| anyhow!("Invalid secret: {}", e))?;
157 mac.update(message.as_bytes());
158 let expected_signature = mac.finalize().into_bytes();
159 let is_valid: bool = signature_bytes.ct_eq(&expected_signature).into();
160 if !is_valid {
161 return Err(anyhow!("Signature verification failed"));
162 }
163 let claims_bytes = URL_SAFE_NO_PAD
164 .decode(claims_b64)
165 .context("Base64 decode of claims failed")?;
166 let claims: Claims =
167 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?;
168 let now = Utc::now().timestamp() as usize;
169 if claims.exp < now {
170 return Err(anyhow!("Token expired"));
171 }
172 if let Some(scopes) = allowed_scopes {
173 let token_scope = claims.scope.as_deref().unwrap_or("");
174 if !scopes.contains(&token_scope) {
175 return Err(anyhow!("Invalid token scope: {}", token_scope));
176 }
177 }
178 Ok(TokenData { claims })
179}
180pub fn get_algorithm_from_token(token: &str) -> Result<String, String> {
181 let parts: Vec<&str> = token.split('.').collect();
182 if parts.len() != 3 {
183 return Err("Invalid token format".to_string());
184 }
185 let header_bytes = URL_SAFE_NO_PAD
186 .decode(parts[0])
187 .map_err(|e| format!("Base64 decode failed: {}", e))?;
188 let header: Header =
189 serde_json::from_slice(&header_bytes).map_err(|e| format!("JSON decode failed: {}", e))?;
190 Ok(header.alg)
191}