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 = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -583,6 +584,15 @@ version = "0.3.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
@ -1082,6 +1092,17 @@ dependencies = [
|
|||||||
"digest",
|
"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]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -1103,8 +1124,12 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
"base64",
|
||||||
|
"hmac",
|
||||||
"refinery",
|
"refinery",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1132,6 +1157,12 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.74"
|
version = "2.0.74"
|
||||||
|
@ -4,6 +4,10 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
base64 = "0.22.1"
|
||||||
|
sha2 = "0.10.8"
|
||||||
|
hmac = "0.12.1"
|
||||||
|
serde_json = "1.0.124"
|
||||||
actix-web = "4"
|
actix-web = "4"
|
||||||
actix-utils = "3.0.1"
|
actix-utils = "3.0.1"
|
||||||
refinery = { version = "0.8.14", features = ["rusqlite"] }
|
refinery = { version = "0.8.14", features = ["rusqlite"] }
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
use actix_web::{Error, FromRequest, HttpRequest};
|
use actix_web::{Error, FromRequest, HttpRequest};
|
||||||
use actix_web::dev::Payload;
|
use actix_web::dev::Payload;
|
||||||
use actix_web::error::ErrorUnauthorized;
|
use actix_web::error::ErrorUnauthorized;
|
||||||
use std::string::String;
|
|
||||||
use actix_utils::future::{Ready, ok, err};
|
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 {
|
pub struct AuthorizedUser {
|
||||||
user_id: String,
|
pub user_id: String,
|
||||||
|
pub username: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromRequest for AuthorizedUser {
|
impl FromRequest for AuthorizedUser {
|
||||||
@ -13,17 +19,17 @@ impl FromRequest for AuthorizedUser {
|
|||||||
type Future = Ready<Result<Self, Self::Error>>;
|
type Future = Ready<Result<Self, Self::Error>>;
|
||||||
|
|
||||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||||
if is_authorized(req) {
|
let user = get_authorized_user(req);
|
||||||
ok(Self {
|
|
||||||
user_id: "hi".to_string(),
|
if user.is_some() {
|
||||||
})
|
ok(user.unwrap())
|
||||||
} else {
|
} else {
|
||||||
err(ErrorUnauthorized("Unauthorized"))
|
err(ErrorUnauthorized("Unauthorized"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_authorized(req: &HttpRequest) -> bool {
|
fn get_authorized_user(req: &HttpRequest) -> Option<AuthorizedUser> {
|
||||||
let token = req.headers()
|
let token = req.headers()
|
||||||
.get("Authorization")
|
.get("Authorization")
|
||||||
.and_then(|value| value.to_str().ok())
|
.and_then(|value| value.to_str().ok())
|
||||||
@ -31,11 +37,30 @@ fn is_authorized(req: &HttpRequest) -> bool {
|
|||||||
.and_then(|value| Some(value.replace("Bearer ", "")));
|
.and_then(|value| Some(value.replace("Bearer ", "")));
|
||||||
|
|
||||||
if token.is_none() {
|
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;
|
mod auth;
|
||||||
|
|
||||||
use actix_web::{get, Responder, HttpResponse, HttpServer, App, web};
|
use actix_web::{get, Responder, HttpResponse, HttpServer, App, web};
|
||||||
use actix_web::middleware;
|
|
||||||
use std::sync::{Mutex, Arc};
|
use std::sync::{Mutex, Arc};
|
||||||
use crate::auth::AuthorizedUser;
|
use crate::auth::AuthorizedUser;
|
||||||
|
|
||||||
@ -26,8 +25,8 @@ async fn healthcheck(data: web::Data<AppData>) -> impl Responder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/authorized")]
|
#[get("/authorized")]
|
||||||
async fn authorized(_: AuthorizedUser) -> impl Responder {
|
async fn authorized(auth: AuthorizedUser) -> impl Responder {
|
||||||
HttpResponse::Ok().body("Authorized")
|
HttpResponse::Ok().body(format!("Authorized as {} ({})", auth.username, auth.user_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
|
Loading…
Reference in New Issue
Block a user