1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
use std::sync::Arc;
use axum::{
extract::{Request, State},
response::IntoResponse,
};
use axum_extra::{
extract::{cookie::Cookie, CookieJar},
headers::{authorization::Bearer, Authorization},
routing::TypedPath,
TypedHeader,
};
use jsonwebtoken::{DecodingKey, Validation};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use uuid::Uuid;
use crate::{error::AuthError, state::AppState, Error};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Claims {
pub sub: Uuid,
pub iat: i64,
pub exp: i64,
pub jti: Uuid,
}
impl Claims {
const MAX_AGE: i64 = 3600;
pub fn new(sub: Uuid) -> Self {
let iat = OffsetDateTime::now_utc().unix_timestamp();
let exp = iat + Self::MAX_AGE;
let jti = uuid::Uuid::new_v4();
Self { sub, iat, exp, jti }
}
pub fn encode(&self, secret: &[u8]) -> Result<String, jsonwebtoken::errors::Error> {
jsonwebtoken::encode(
&jsonwebtoken::Header::default(),
self,
&jsonwebtoken::EncodingKey::from_secret(secret),
)
}
}
impl From<Uuid> for Claims {
fn from(value: Uuid) -> Self {
Self::new(value)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
struct Session {
jti: Uuid,
uuid: Uuid,
}
#[derive(Debug, Deserialize, TypedPath)]
#[typed_path("/api/auth/refresh")]
pub struct Refresh;
impl Refresh {
#[tracing::instrument]
pub async fn post(
self,
State(state): State<Arc<AppState>>,
TypedHeader(Authorization(bearer)): TypedHeader<Authorization<Bearer>>,
cookie_jar: CookieJar,
) -> Result<impl IntoResponse, Error> {
let Claims { sub, .. } = jsonwebtoken::decode::<Claims>(
bearer.token(),
&DecodingKey::from_secret(state.jwt_secret.as_ref()),
&Validation::default(),
)?
.claims;
let claims = Claims::from(sub);
let token = jsonwebtoken::encode(
&jsonwebtoken::Header::default(),
&claims,
&jsonwebtoken::EncodingKey::from_secret(state.jwt_secret.as_ref()),
)?;
let cookie = Cookie::build(("token", token))
.expires(OffsetDateTime::from_unix_timestamp(claims.exp)?)
.secure(true)
.http_only(true);
Ok(cookie_jar.add(cookie))
}
}
pub async fn authenticate(
State(state): State<Arc<AppState>>,
cookie_jar: CookieJar,
mut req: Request,
) -> Result<Request, AuthError> {
let token = cookie_jar
.get("token")
.ok_or(AuthError::JwtNotFound)?
.to_string();
let claims = jsonwebtoken::decode::<Claims>(
&token,
&DecodingKey::from_secret(state.jwt_secret.as_ref()),
&Validation::default(),
)?
.claims;
req.extensions_mut().insert(claims);
Ok(req)
}
|