Implement JWT verification in rust
This commit is contained in:
parent
687d366f3d
commit
62659a7746
31
rust-backend/Cargo.lock
generated
31
rust-backend/Cargo.lock
generated
@ -423,6 +423,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -583,6 +584,15 @@ version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.12"
|
||||
@ -1082,6 +1092,17 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
@ -1103,8 +1124,12 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"base64",
|
||||
"hmac",
|
||||
"refinery",
|
||||
"rusqlite",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1132,6 +1157,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.74"
|
||||
|
@ -4,6 +4,10 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.22.1"
|
||||
sha2 = "0.10.8"
|
||||
hmac = "0.12.1"
|
||||
serde_json = "1.0.124"
|
||||
actix-web = "4"
|
||||
actix-utils = "3.0.1"
|
||||
refinery = { version = "0.8.14", features = ["rusqlite"] }
|
||||
|
@ -1,11 +1,17 @@
|
||||
use actix_web::{Error, FromRequest, HttpRequest};
|
||||
use actix_web::dev::Payload;
|
||||
use actix_web::error::ErrorUnauthorized;
|
||||
use std::string::String;
|
||||
use actix_utils::future::{Ready, ok, err};
|
||||
use std::string::String;
|
||||
use std::option::Option;
|
||||
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||
use sha2::Sha256;
|
||||
use hmac::{Hmac, Mac};
|
||||
use serde_json::Value;
|
||||
|
||||
pub struct AuthorizedUser {
|
||||
user_id: String,
|
||||
pub user_id: String,
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
impl FromRequest for AuthorizedUser {
|
||||
@ -13,17 +19,17 @@ impl FromRequest for AuthorizedUser {
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
|
||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||
if is_authorized(req) {
|
||||
ok(Self {
|
||||
user_id: "hi".to_string(),
|
||||
})
|
||||
let user = get_authorized_user(req);
|
||||
|
||||
if user.is_some() {
|
||||
ok(user.unwrap())
|
||||
} else {
|
||||
err(ErrorUnauthorized("Unauthorized"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_authorized(req: &HttpRequest) -> bool {
|
||||
fn get_authorized_user(req: &HttpRequest) -> Option<AuthorizedUser> {
|
||||
let token = req.headers()
|
||||
.get("Authorization")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
@ -31,11 +37,30 @@ fn is_authorized(req: &HttpRequest) -> bool {
|
||||
.and_then(|value| Some(value.replace("Bearer ", "")));
|
||||
|
||||
if token.is_none() {
|
||||
return false;
|
||||
return None;
|
||||
}
|
||||
|
||||
// TODO implement
|
||||
let token = token.unwrap();
|
||||
let jwt_parts: Vec<&str> = token.split('.').collect();
|
||||
|
||||
true
|
||||
if jwt_parts.len() != 3 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let header: Value = serde_json::from_slice(&URL_SAFE_NO_PAD.decode(jwt_parts.get(0).unwrap()).ok()?).ok()?;
|
||||
let payload: Value = serde_json::from_slice(&URL_SAFE_NO_PAD.decode(jwt_parts.get(1).unwrap()).ok()?).ok()?;
|
||||
let signature = URL_SAFE_NO_PAD.decode(jwt_parts.get(2).unwrap()).ok()?;
|
||||
|
||||
let mut mac = Hmac::<Sha256>::new_from_slice("DenHerMåAldrigVæreOffentligKunIDetteDemoProjekt".as_bytes()).ok()?;
|
||||
mac.update(format!("{}.{}", jwt_parts.get(0).unwrap(), jwt_parts.get(1).unwrap()).as_bytes());
|
||||
|
||||
if mac.verify_slice(&signature).is_err() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AuthorizedUser {
|
||||
user_id: payload["sub"].as_str()?.to_string(),
|
||||
username: payload["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"].as_str()?.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
mod auth;
|
||||
|
||||
use actix_web::{get, Responder, HttpResponse, HttpServer, App, web};
|
||||
use actix_web::middleware;
|
||||
use std::sync::{Mutex, Arc};
|
||||
use crate::auth::AuthorizedUser;
|
||||
|
||||
@ -26,8 +25,8 @@ async fn healthcheck(data: web::Data<AppData>) -> impl Responder {
|
||||
}
|
||||
|
||||
#[get("/authorized")]
|
||||
async fn authorized(_: AuthorizedUser) -> impl Responder {
|
||||
HttpResponse::Ok().body("Authorized")
|
||||
async fn authorized(auth: AuthorizedUser) -> impl Responder {
|
||||
HttpResponse::Ok().body(format!("Authorized as {} ({})", auth.username, auth.user_id))
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
|
Loading…
Reference in New Issue
Block a user