#authorization #tenant #b2b

propelauth

A Rust crate for managing authentication and authorization with support for multi-tenant / B2B products, powered by PropelAuth

20 breaking releases

0.20.0 Nov 14, 2024
0.18.0 Sep 27, 2024
0.14.0 Jun 17, 2024
0.10.0 Feb 18, 2024
0.1.0 Dec 6, 2022

#97 in Authentication

Download history 257/week @ 2024-08-19 136/week @ 2024-08-26 265/week @ 2024-09-02 296/week @ 2024-09-09 388/week @ 2024-09-16 519/week @ 2024-09-23 169/week @ 2024-09-30 338/week @ 2024-10-07 168/week @ 2024-10-14 258/week @ 2024-10-21 341/week @ 2024-10-28 165/week @ 2024-11-04 267/week @ 2024-11-11 312/week @ 2024-11-18 3/week @ 2024-11-25 68/week @ 2024-12-02

662 downloads per month

MIT license

320KB
7K SLoC

PropelAuth

Add authentication and authorization to your application.

This library is meant to be used with a PropelAuth account. You can sign up and get started for free.

Initialize

First, you'll need to initialize the library. You can either call PropelAuth::init or PropelAuth::fetch_and_init (which will fetch any unspecified metadata).

let auth = PropelAuth::fetch_and_init(AuthOptions {
    auth_url: "REPLACE_ME".to_string(),
    api_key: "REPLACE_ME".to_string(),
}).await.expect("Unable to initialize authentication");

Usage / Protecting APIs

Want us to add support for another framework? Reach out at support@propelauth.com

Axum

To use Axum, make sure to enable the axum feature in your Cargo.toml.

Then, add PropelAuthLayer to your Router:

let auth_layer = PropelAuthLayer::new(auth);

let app = Router::new()
    .route("/whoami", get(whoami))
    .route("/org/:org_name/whoami", get(org_whoami))
    .layer(auth_layer); // <-- here

You can then take User in as an argument, which will look for an access token in the Authorization header.

// User will automatically return a 401 (Unauthorized) if a valid access token wasn't provided
async fn whoami(user: User) -> String {
    user.user_id
}

You can also check which organizations the user is in, and which roles and permissions they have.

// If the user isn't in the provided organization, a 403 is returned
async fn org_whoami(user: User,
                    Path(org_name): Path<String>) -> Result<String, UnauthorizedOrForbiddenError> {
    let org = user.validate_org_membership(RequiredOrg::OrgName(&org_name),
                                           UserRequirementsInOrg::IsRole("Admin"))?;
    Ok(format!("You are a {} in {}", org.user_role, org.org_name))
}

You can also get the full auth struct and make API calls with it:

// Extension(auth) is useful for making API requests
async fn make_req(Extension(auth): Extension<Arc<PropelAuth>>) -> String {
    let magic_link = auth.user().create_magic_link(CreateMagicLinkRequest {
        email: "user@customer.com".to_string(),
        ..Default::default()
    }).await.expect("Couldn't create magic link");
    magic_link.url
}

Actix

To use Actix, make sure to enable the actix4 feature in your Cargo.toml.

Add your PropelAuth to your Router:

let auth = PropelAuth::fetch_and_init(/*...*/)
//...
HttpServer::new(move || {
    App::new()
        .service(whoami)
        .service(org_whoami)
        .app_data(web::Data::new(auth.clone())) // <-- here
})

You can then take User in as an argument, which will look for an access token in the Authorization header.

// User will automatically return a 401 (Unauthorized) if a valid access token wasn't provided
#[get("/whoami")]
async fn whoami(user: User) -> impl Responder {
    HttpResponse::Ok().json(user)
}

You can also check which organizations the user is in, and which roles and permissions they have.

// If the user isn't in the provided organization, a 403 is returned
#[get("/org/{org_name}/whoami")]
async fn whoami(user: User, org_name: web::Path<String>) -> Result<impl Responder, UnauthorizedOrForbiddenError> {
   let org = user.validate_org_membership(RequiredOrg::OrgName(&org_name.into_inner()),
                                          UserRequirementsInOrg::IsRole("Admin"))?;
   Ok(HttpResponse::Ok()
       .body(format!("You are a {} in {}", org.user_role, org.org_name)))
}

You can also get the full auth struct and make API calls with it:

#[post("/magic_link")]
async fn make_req(auth: web::Data<PropelAuth>) -> impl Responder {
    let magic_link = auth.user().create_magic_link(CreateMagicLinkRequest {
        email: "user@customer.com".to_string(),
        ..Default::default()
    }).await.expect("Couldn't create magic link");
    HttpResponse::Ok().json(magic_link)
}

Rustls instead of OpenSSL

If you'd rather use a pure Rust TLS implementation rather than OpenSSL disable the default features and enable rustls as so:

propelauth = { version >= "0.12.1", features = ["rustls"], default-features = false }

Other

After initializing auth, you can verify access tokens by passing in the Authorization header (formatted Bearer TOKEN):

let result = auth.verify().validate_authorization_header(&authorization_header);
match result {
    Ok(user) => { /* valid access token in the header */ }
    Err(_) => { /* invalid access token, typically we return a 401 Unauthorized here */ }
}

You can also check which organizations the user is in, and which roles and permissions they have.

let org = auth.validate_org_membership(
    &authorization_header,
    RequiredOrg::OrgName("acme"),
    UserRequirementsInOrg::IsRole("Admin")
)?;

// Alternatively, if you already have a user from validate_authorization_header
let org = user.validate_org_membership(
    RequiredOrg::OrgName("acme"),
    UserRequirementsInOrg::IsRole("Admin")
)?;

And finally, you can make API calls directly from auth.user() and auth.org()

Where do the access tokens come from?

They come from your frontend. You can read more about integrating your frontend here.

Dependencies

~11–25MB
~468K SLoC