Basic JWT checks

This commit is contained in:
2025-01-30 21:33:54 +01:00
parent 6874aebfc7
commit 0e8b4751b4
3 changed files with 112 additions and 13 deletions

View File

@ -2,7 +2,7 @@ use crate::user::{APIClient, APIClientID, UserConfig, UserID};
use actix_web::dev::Payload;
use actix_web::{FromRequest, HttpRequest};
use jwt_simple::common::VerificationOptions;
use jwt_simple::prelude::{HS256Key, MACLike};
use jwt_simple::prelude::{Duration, HS256Key, MACLike};
use std::str::FromStr;
pub struct APIClientAuth {
@ -12,7 +12,10 @@ pub struct APIClientAuth {
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct JWTClaims {}
pub struct TokenClaims {
pub method: String,
pub uri: String,
}
impl APIClientAuth {
async fn extract_auth(req: &HttpRequest) -> Result<Self, actix_web::Error> {
@ -78,17 +81,31 @@ impl APIClientAuth {
// Decode JWT
let key = HS256Key::from_bytes(client.secret.as_bytes());
let claims =
match key.verify_token::<JWTClaims>(jwt_token, Some(VerificationOptions::default())) {
Ok(t) => t,
Err(e) => {
log::error!("JWT validation failed! {e}");
return Err(actix_web::error::ErrorForbidden("JWT validation failed!"));
}
};
let mut verif = VerificationOptions::default();
verif.max_validity = Some(Duration::from_mins(15));
let claims = match key.verify_token::<TokenClaims>(jwt_token, Some(verif)) {
Ok(t) => t,
Err(e) => {
log::error!("JWT validation failed! {e}");
return Err(actix_web::error::ErrorForbidden("JWT validation failed!"));
}
};
// Check for nonce
if claims.nonce.is_none() {
return Err(actix_web::error::ErrorBadRequest(
"A nonce is required in JWT!",
));
}
// Check URI & verb
if claims.custom.uri != req.uri().to_string() {
return Err(actix_web::error::ErrorBadRequest("URI mismatch!"));
}
if claims.custom.method != req.method().to_string() {
return Err(actix_web::error::ErrorBadRequest("Method mismatch!"));
}
// TODO : check timing
// TODO : check URI & verb
// TODO : handle payload
// TODO : check read only access
// TODO : update last use (if required)

View File

@ -42,7 +42,8 @@ async fn main() -> std::io::Result<()> {
.route("/oidc_cb", web::get().to(web_ui::oidc_cb))
.route("/sign_out", web::get().to(web_ui::sign_out))
// API routes
.route("/api/", web::get().to(api::api_home))
.route("/api", web::get().to(api::api_home))
.route("/api", web::post().to(api::api_home))
})
.bind(&AppConfig::get().listen_address)?
.run()