[][src]Crate actix_web_middleware_keycloak_auth

actix-web-middleware-keycloak-auth

A middleware for Actix Web that handles authentication with a JWT emitted by Keycloak.

Setup middleware

Setting up the middleware is done in 2 steps:

  1. creating a KeycloakAuth struct with the wanted configuration
  2. passing this struct to an Actix Web service wrap() method
use actix_web::{App, web, HttpResponse};
use actix_web_middleware_keycloak_auth::{KeycloakAuth, DecodingKey};

// const KEYCLOAK_PK: &str = "..."; // You should get this from configuration

// Initialize middleware configuration
let keycloak_auth = KeycloakAuth {
    detailed_responses: true,
    keycloak_oid_public_key: DecodingKey::from_rsa_pem(KEYCLOAK_PK.as_bytes()).unwrap(),
    required_roles: vec![],
};

App::new()
    .service(
        web::scope("/private")
            .wrap(keycloak_auth) // Every route in the service will leverage the middleware
            .route("", web::get().to(|| HttpResponse::Ok().body("Private"))),
    )
    .service(web::resource("/").to(|| HttpResponse::Ok().body("Hello World")));

HTTP requests to GET /private will need to have a Authorization header containing Bearer [JWT] where [JWT] is a valid JWT that was signed by the private key associated with the public key provided when the middleware was initialized.

Require roles

You can require one or several specific roles to be included in JWT. If they are not provided, the middleware will return a 403 error.

let keycloak_auth = KeycloakAuth {
    detailed_responses: true,
    keycloak_oid_public_key: DecodingKey::from_rsa_pem(KEYCLOAK_PK.as_bytes()).unwrap(),
    required_roles: vec![
        Role::Realm { role: "admin".to_owned() }, // The "admin" realm role must be provided in the JWT
        Role::Client {
            client: "backoffice".to_owned(),
            role: "readonly".to_owned()
        }, // The "readonly" role of the "backoffice" client must be provided in the JWT
    ],
};

Use several authentication profiles

It is possible to setup multiple authentication profiles if, for example, multiple groups of routes require different roles.

use actix_web::{App, web, HttpResponse};
use actix_web_middleware_keycloak_auth::{KeycloakAuth, DecodingKey, Role};

// const KEYCLOAK_PK: &str = "..."; // You should get this from configuration

// No role required
let keycloak_auth = KeycloakAuth {
    detailed_responses: true,
    keycloak_oid_public_key: DecodingKey::from_rsa_pem(KEYCLOAK_PK.as_bytes()).unwrap(),
    required_roles: vec![],
};

// Admin realm role is required
let keycloak_auth_admin = KeycloakAuth {
    detailed_responses: true,
    keycloak_oid_public_key: DecodingKey::from_rsa_pem(KEYCLOAK_PK.as_bytes()).unwrap(),
    required_roles: vec![Role::Realm { role: "admin".to_owned() }],
};

App::new()
    .service(
        web::scope("/private")
            .wrap(keycloak_auth) // User must be authenticated
            .route("", web::get().to(|| HttpResponse::Ok().body("Private"))),
    )
    .service(
        web::scope("/admin")
            .wrap(keycloak_auth_admin) // User must have the "admin" role
            .route("", web::get().to(|| HttpResponse::Ok().body("Admin"))),
    )
    .service(web::resource("/").to(|| HttpResponse::Ok().body("Hello World")));

Access claims in handlers

When authentication is successful, the middleware will store the decoded claims so that they can be accessed from handlers.

use actix_web::web::ReqData;
use actix_web::{HttpResponse, Responder};
use actix_web_middleware_keycloak_auth::Claims;

async fn private(claims: ReqData<Claims>) -> impl Responder {
    HttpResponse::Ok().body(format!("{:?}", &claims))
}

Structs

Access

Access details

Claims

Claims that are extracted from JWT and can be accessed in handlers using a ReqData<Claims> parameter

DecodingKey

(Re-exported from the jsonwebtoken crate)

KeycloakAuth

Middleware configuration

KeycloakAuthMiddleware

Internal middleware configuration

Enums

Role

A realm or client role