actix-jwt-auth-middleware
This crate implements a JSON Webtoken (JWT) middleware for the actix-web framework.
For the moment it uses Curve25519 implemented in the ed25519_dalek crate for the signing process of the token but i will be working on a generalization soon.
Features
Automatic insertion and extraction of claims into the Extensions object on the request. For this your type has to implement the FromRequest trait or it has to be annotated with the #[derive(actix-jwt-auth-middleware::FromRequest)]
macro which implements this trait for your type.
#[derive(Serialize, Deserialize, Clone, FromRequest)]
struct UserClaims {
id: u32,
role: Role,
}
#[derive(Serialize, Deserialize, Clone)]
enum Role {
Admin,
BaseUser,
}
#[get("/hello")]
async fn hello(user_claims: UserClaims) -> impl Responder {
format!("Hello user with id: {}!", user_claims.id)
}
Guard functions on top of JWT authentication.
Your guard function has to implement the Handler trait and return a type that is partially equatable to a boolean.
Luckily the Handler trait is implemented for functions (up to an arity of 12) by actix_web.
The Application State can also be accessed with the guard function, for this use the web::Data extractor where T is the type of the state.
async fn verify_service_request(user_claims: UserClaims) -> bool {
match user_claims.role {
Role::Admin => true,
Role::BaseUser => false,
}
}
Example
use actix_jwt_auth_middleware::{AuthError, AuthService, Authority, FromRequest};
use actix_web::{
get,
web::{self, Data},
App, HttpResponse, HttpServer, Responder,
};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, FromRequest)]
struct UserClaims {
id: u32,
role: Role,
}
#[derive(Serialize, Deserialize, Clone)]
enum Role {
Admin,
BaseUser,
}
async fn verify_service_request(user_claims: UserClaims) -> bool {
match user_claims.role {
Role::Admin => true,
Role::BaseUser => false,
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let auth_authority = Authority::<UserClaims>::default();
HttpServer::new(move || {
App::new()
.app_data(Data::new(auth_authority.clone()))
.service(login)
.service(login_as_base_user)
.service(web::scope("").service(hello).wrap(AuthService::new(
auth_authority.clone(),
verify_service_request,
)))
})
.bind(("127.0.0.1", 42069))?
.run()
.await
}
#[get("/hello")]
async fn hello(user_claims: UserClaims) -> impl Responder {
format!("Hello user with id: {}!", user_claims.id)
}
#[get("/login")]
async fn login(auth_authority: Data<Authority<UserClaims>>) -> Result<HttpResponse, AuthError> {
let cookie = auth_authority.create_signed_cookie(UserClaims {
id: 69,
role: Role::Admin,
})?;
Ok(HttpResponse::Accepted()
.cookie(cookie)
.body("You are now logged in"))
}
#[get("/login-as-base-user")]
async fn login_as_base_user(
auth_authority: Data<Authority<UserClaims>>,
) -> Result<HttpResponse, AuthError> {
let cookie = auth_authority.create_signed_cookie(UserClaims {
id: 69,
role: Role::BaseUser,
})?;
Ok(HttpResponse::Accepted()
.cookie(cookie)
.body("You are now logged in"))
}
License: MIT OR Apache-2.0