axum_supabase_auth/middleware/
decoder.rs1use super::AuthClaims;
2use crate::AuthTypes;
3use jsonwebtoken::{DecodingKey, Validation};
4use std::marker::PhantomData;
5
6pub struct Decoder<T>
7where
8 T: AuthTypes,
9{
10 keys: Keys,
11 validation: Validation,
12
13 phantom: PhantomData<T>,
14}
15
16impl<T> Decoder<T>
17where
18 T: AuthTypes,
19{
20 pub fn new(secret: &str) -> Self {
21 let mut validation = Validation::default();
22 validation.set_audience(&["authenticated"]);
23
24 Self::new_with_validation(secret, validation)
25 }
26
27 pub fn new_with_validation(secret: &str, validation: Validation) -> Self {
28 Self {
29 keys: Keys::new(secret.as_bytes()),
30 validation,
31 phantom: PhantomData,
32 }
33 }
34
35 pub fn decode(&self, token: &str) -> Result<AuthClaims<T>, jsonwebtoken::errors::Error> {
36 jsonwebtoken::decode::<AuthClaims<T>>(token, &self.keys.decoding, &self.validation)
37 .map(|data| data.claims)
38 }
39}
40
41struct Keys {
42 decoding: DecodingKey,
43}
44
45impl Keys {
46 fn new(secret: &[u8]) -> Self {
47 Self {
48 decoding: DecodingKey::from_secret(secret),
49 }
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::middleware::Empty;
57 use jsonwebtoken::Validation;
58 use serde::{Deserialize, Serialize};
59 use std::sync::LazyLock;
60
61 #[derive(Debug, Deserialize, Serialize)]
62 struct AppMetadata {
63 groups: Vec<String>,
64 }
65
66 const JWT: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjU0MzIxL2F1dGgvdjEiLCJzdWIiOiIzNGFiYzFmNy1lMzQ2LTRiMzAtYmMyNi0xYjUzZjcwN2JmNTQiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzIzOTc2ODY5LCJpYXQiOjE3MjM5NzMyNjksImVtYWlsIjoidGVzdHVzZXJAdGVzdC5jb20iLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJncm91cHMiOlsiZmsiXSwicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6e30sInJvbGUiOiJhdXRoZW50aWNhdGVkIiwiYWFsIjoiYWFsMSIsImFtciI6W3sibWV0aG9kIjoicGFzc3dvcmQiLCJ0aW1lc3RhbXAiOjE3MjM5NzMyNjl9XSwic2Vzc2lvbl9pZCI6Ijk5OTRhNTk4LTMyNjMtNDBlNC1iMWMwLTU5YTE0ODNhODRlMiIsImlzX2Fub255bW91cyI6ZmFsc2V9.1U6LglCXAD1yJwnXndgYuvQ6muCn2MUb_ivwlKIirgk";
67 const SECRET: &str = "super-secret-jwt-token-with-at-least-32-characters-long";
68
69 static VALIDATION: LazyLock<Validation> = LazyLock::new(|| {
70 let mut validation = Validation::default();
71 validation.validate_exp = false;
72 validation.set_audience(&["authenticated"]);
73 validation
74 });
75
76 struct MyAuthTypes;
77
78 impl AuthTypes for MyAuthTypes {
79 type AppData = AppMetadata;
80 type UserData = Empty;
81 type AdditionalData = Empty;
82 }
83
84 struct EmptyAuthTypes;
85
86 impl AuthTypes for EmptyAuthTypes {
87 type AppData = Empty;
88 type UserData = Empty;
89 type AdditionalData = Empty;
90 }
91
92 #[test]
93 fn test_decode_additional_app_metadata() {
94 let decoder = Decoder::<MyAuthTypes>::new_with_validation(SECRET, VALIDATION.clone());
95 let claims = decoder.decode(JWT).unwrap();
96
97 assert_eq!(claims.app_metadata.additional.groups, vec!["fk"]);
98 assert_eq!(claims.app_metadata.provider, "email");
99 }
100
101 #[test]
102 fn test_decode_no_additional_app_metadata() {
103 let decoder = Decoder::<EmptyAuthTypes>::new_with_validation(SECRET, VALIDATION.clone());
104 let claims = decoder.decode(JWT).unwrap();
105
106 assert_eq!(claims.app_metadata.provider, "email");
107 }
108}