lockpad_auth/
lib.rs

1use crate::error::Result;
2use axum::{
3    extract::{FromRef, FromRequestParts},
4    http::request::Parts,
5    response::{IntoResponse, Response},
6};
7use axum_extra::{
8    headers::{authorization::Bearer, Authorization},
9    TypedHeader,
10};
11use jsonwebtoken::{encode, Algorithm, DecodingKey, EncodingKey, Header};
12use serde::{Deserialize, Serialize};
13
14pub mod error;
15pub mod key;
16
17pub use key::PublicKey;
18
19/// The claims of a JWT
20#[derive(Debug, Deserialize, Serialize)]
21pub struct Claims {
22    pub sub: String,
23    pub exp: usize,
24    pub iat: usize,
25}
26
27impl Claims {
28    /// Create a claim with a given subject
29    /// The expiration time is set to 7 days from the moment of creation
30    pub fn new(sub: String) -> Self {
31        let now = std::time::SystemTime::now();
32        let iat = now.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as usize;
33
34        let token_life = std::time::Duration::from_secs(60 * 60 * 24 * 7); // 7 days
35        let exp = (now + token_life)
36            .duration_since(std::time::UNIX_EPOCH)
37            .unwrap()
38            .as_secs() as usize;
39
40        Self { sub, exp, iat }
41    }
42
43    /// Encode the claims into a JWT string
44    pub async fn encode(&self, key: &EncodingKey) -> Result<String> {
45        let header = Header::new(Algorithm::RS256);
46        let token = encode(&header, self, key)?;
47
48        Ok(token)
49    }
50
51    pub async fn decode(token: &str, key: &DecodingKey) -> Result<Self> {
52        let validation = jsonwebtoken::Validation::new(Algorithm::RS256);
53        let claims = jsonwebtoken::decode::<Self>(token, key, &validation)?;
54
55        Ok(claims.claims)
56    }
57
58    pub async fn decode_validation(
59        token: &str,
60        key: &DecodingKey,
61        validation: &jsonwebtoken::Validation,
62    ) -> Result<Self> {
63        let claims = jsonwebtoken::decode::<Self>(token, key, validation)?;
64
65        Ok(claims.claims)
66    }
67}
68
69#[axum::async_trait]
70impl<S> FromRequestParts<S> for Claims
71where
72    S: Send + Sync,
73    key::PublicKey: axum::extract::FromRef<S>,
74{
75    type Rejection = Response;
76
77    async fn from_request_parts(
78        parts: &mut Parts,
79        state: &S,
80    ) -> std::result::Result<Self, Self::Rejection> {
81        // Extract the authorization header
82        let TypedHeader(Authorization(token)) =
83            TypedHeader::<Authorization<Bearer>>::from_request_parts(parts, state)
84                .await
85                .map_err(|err| err.into_response())?;
86
87        // Verify the token
88        let key: PublicKey = FromRef::from_ref(state);
89        let key: DecodingKey = FromRef::from_ref(&key);
90        let claims = Claims::decode(token.token(), &key)
91            .await
92            .map_err(|err| err.into_response())?;
93
94        Ok(claims)
95    }
96}