snarkos_node_rest/helpers/
auth.rs1use snarkvm::prelude::*;
17
18use ::time::OffsetDateTime;
19use anyhow::{Result, anyhow};
20use axum::{
21 RequestPartsExt,
22 body::Body,
23 http::{Request, StatusCode},
24 middleware::Next,
25 response::{IntoResponse, Response},
26};
27use axum_extra::{
28 TypedHeader,
29 headers::authorization::{Authorization, Bearer},
30};
31use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode};
32use once_cell::sync::OnceCell;
33use serde::{Deserialize, Serialize};
34
35pub const EXPIRATION: i64 = 10 * 365 * 24 * 60 * 60; fn jwt_secret() -> &'static Vec<u8> {
40 static SECRET: OnceCell<Vec<u8>> = OnceCell::new();
41 SECRET.get_or_init(|| {
42 let seed: [u8; 16] = ::rand::thread_rng().gen();
43 seed.to_vec()
44 })
45}
46
47#[derive(Debug, Deserialize, Serialize)]
49pub struct Claims {
50 sub: String,
52 iat: i64,
54 exp: i64,
56}
57
58impl Claims {
59 pub fn new<N: Network>(address: Address<N>) -> Self {
60 let issued_at = OffsetDateTime::now_utc().unix_timestamp();
61 let expiration = issued_at.saturating_add(EXPIRATION);
62
63 Self { sub: address.to_string(), iat: issued_at, exp: expiration }
64 }
65
66 pub fn is_expired(&self) -> bool {
68 OffsetDateTime::now_utc().unix_timestamp() >= self.exp
69 }
70
71 pub fn to_jwt_string(&self) -> Result<String> {
73 encode(&Header::default(), &self, &EncodingKey::from_secret(jwt_secret())).map_err(|e| anyhow!(e))
74 }
75}
76
77pub async fn auth_middleware(request: Request<Body>, next: Next) -> Result<Response, Response> {
78 let (mut parts, body) = request.into_parts();
80 let auth: TypedHeader<Authorization<Bearer>> =
81 parts.extract().await.map_err(|_| StatusCode::UNAUTHORIZED.into_response())?;
82
83 match decode::<Claims>(auth.token(), &DecodingKey::from_secret(jwt_secret()), &Validation::new(Algorithm::HS256)) {
84 Ok(decoded) => {
85 let claims = decoded.claims;
86 if claims.is_expired() {
87 return Err((StatusCode::UNAUTHORIZED, "Expired JSON Web Token".to_owned()).into_response());
88 }
89 }
90
91 Err(_) => {
92 return Err(StatusCode::UNAUTHORIZED.into_response());
93 }
94 }
95
96 let request = Request::from_parts(parts, body);
98
99 Ok(next.run(request).await)
100}