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 k256::ecdsa::{Signature, SigningKey, VerifyingKey, signature::Verifier};
8
9pub fn get_did_from_token(token: &str) -> Result<String, String> {
10 let parts: Vec<&str> = token.split('.').collect();
11 if parts.len() != 3 {
12 return Err("Invalid token format".to_string());
13 }
14
15 let payload_bytes = URL_SAFE_NO_PAD
16 .decode(parts[1])
17 .map_err(|e| format!("Base64 decode failed: {}", e))?;
18
19 let claims: UnsafeClaims =
20 serde_json::from_slice(&payload_bytes).map_err(|e| format!("JSON decode failed: {}", e))?;
21
22 Ok(claims.sub.unwrap_or(claims.iss))
23}
24
25pub fn get_jti_from_token(token: &str) -> Result<String, String> {
26 let parts: Vec<&str> = token.split('.').collect();
27 if parts.len() != 3 {
28 return Err("Invalid token format".to_string());
29 }
30
31 let payload_bytes = URL_SAFE_NO_PAD
32 .decode(parts[1])
33 .map_err(|e| format!("Base64 decode failed: {}", e))?;
34
35 let claims: serde_json::Value =
36 serde_json::from_slice(&payload_bytes).map_err(|e| format!("JSON decode failed: {}", e))?;
37
38 claims.get("jti")
39 .and_then(|j| j.as_str())
40 .map(|s| s.to_string())
41 .ok_or_else(|| "No jti claim in token".to_string())
42}
43
44pub fn verify_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> {
45 verify_token_internal(token, key_bytes, None, None)
46}
47
48pub fn verify_access_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> {
49 verify_token_internal(
50 token,
51 key_bytes,
52 Some(TOKEN_TYPE_ACCESS),
53 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]),
54 )
55}
56
57pub fn verify_refresh_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> {
58 verify_token_internal(
59 token,
60 key_bytes,
61 Some(TOKEN_TYPE_REFRESH),
62 Some(&[SCOPE_REFRESH]),
63 )
64}
65
66fn verify_token_internal(
67 token: &str,
68 key_bytes: &[u8],
69 expected_typ: Option<&str>,
70 allowed_scopes: Option<&[&str]>,
71) -> Result<TokenData<Claims>> {
72 let parts: Vec<&str> = token.split('.').collect();
73 if parts.len() != 3 {
74 return Err(anyhow!("Invalid token format"));
75 }
76
77 let header_b64 = parts[0];
78 let claims_b64 = parts[1];
79 let signature_b64 = parts[2];
80
81 let header_bytes = URL_SAFE_NO_PAD
82 .decode(header_b64)
83 .context("Base64 decode of header failed")?;
84 let header: Header =
85 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?;
86
87 if let Some(expected) = expected_typ {
88 if header.typ != expected {
89 return Err(anyhow!("Invalid token type: expected {}, got {}", expected, header.typ));
90 }
91 }
92
93 let signature_bytes = URL_SAFE_NO_PAD
94 .decode(signature_b64)
95 .context("Base64 decode of signature failed")?;
96 let signature = Signature::from_slice(&signature_bytes)
97 .map_err(|e| anyhow!("Invalid signature format: {}", e))?;
98
99 let signing_key = SigningKey::from_slice(key_bytes)?;
100 let verifying_key = VerifyingKey::from(&signing_key);
101
102 let message = format!("{}.{}", header_b64, claims_b64);
103 verifying_key
104 .verify(message.as_bytes(), &signature)
105 .map_err(|e| anyhow!("Signature verification failed: {}", e))?;
106
107 let claims_bytes = URL_SAFE_NO_PAD
108 .decode(claims_b64)
109 .context("Base64 decode of claims failed")?;
110 let claims: Claims =
111 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?;
112
113 let now = Utc::now().timestamp() as usize;
114 if claims.exp < now {
115 return Err(anyhow!("Token expired"));
116 }
117
118 if let Some(scopes) = allowed_scopes {
119 let token_scope = claims.scope.as_deref().unwrap_or("");
120 if !scopes.contains(&token_scope) {
121 return Err(anyhow!("Invalid token scope: {}", token_scope));
122 }
123 }
124
125 Ok(TokenData { claims })
126}