This post has code examples for Actix-Web Basic and Bearer authentications in Rust. In a Basic authentication scheme, a client transmits credentials as user Id and password pairs in base64 format. Meanwhile, a client sends a string token in a Bearer authentication. In either case, the server application must validate the credentials or token.
No JWT And Database for Authentication Examples
To keep things simple, the code examples for Actix-Web Basic and Bearer authentications do not use JWT and database. Therefore, we will compare the credentials or token against hard-coded string values.
Dependencies For Actix-Web Authentication Examples
For both authentication examples, we use the same Cargo.toml file with three dependencies.
1 2 3 4 5 6 7 8 9 10 | [package] name = "actix-web-rest-auth" version = "0.1.0" authors = ["Karl San Gabriel"] edition = "2018" [dependencies] actix-web = "2.0.0" actix-rt = "1.1.1" actix-web-httpauth = "0.4.1" |
Basic Authentication Example
For the Actix-web Basic Authentication example, we need the following imports.
1 2 3 4 5 6 | use actix_web_httpauth::extractors::AuthenticationError; use actix_web_httpauth::extractors::basic::{BasicAuth, Config}; use actix_web_httpauth::middleware::HttpAuthentication; use actix_web::{get, web, App, HttpServer, Error, Responder, dev::ServiceRequest}; //.. |
Our main function is as follows. An Actix-Web App instance wraps up an instance of HttpAuthentication<BasicAuth, fn(...)> to intercept requests to any defined routes.
1 2 3 4 5 6 7 8 9 10 11 12 | #[actix_rt::main] async fn main() -> std::io::Result<()> { HttpServer::new(move || { let auth = HttpAuthentication::basic(basic_auth_validator); App::new() .wrap(auth) .route("/{id}/{name}/index.html", web::get().to(index)) }) .bind("127.0.0.1:8080")? .run() .await } |
All requests to /{id}/{name}/index.html go to the index function.
1 2 3 4 | // #[get("/{id}/{name}/index.html")] async fn index(info: web::Path<(u32, String)>) -> impl Responder { format!("Hello {}! id:{}", info.1, info.0) } |
We need to define an asynchronous function. It receives HTTP requests along with the user credentials.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | async fn basic_auth_validator(req: ServiceRequest, credentials: BasicAuth) -> Result<ServiceRequest, Error> { let config = req .app_data::<Config>() .map(|data| data.get_ref().clone()) .unwrap_or_else(Default::default); match validate_credentials(credentials.user_id(), credentials.password().unwrap().trim()) { Ok(res) => { if res == true { Ok(req) } else { Err(AuthenticationError::from(config).into()) } } Err(_) => Err(AuthenticationError::from(config).into()), } } |
Then, we need another function to validate user credentials against hard-coded string values.
1 2 3 4 5 6 7 8 | fn validate_credentials(user_id: &str, user_password: &str) -> Result<bool, std::io::Error> { if(user_id.eq("karl") && user_password.eq("password")) { return Ok(true); } return Err(std::io::Error::new(std::io::ErrorKind::Other, "Authentication failed!")); } |
To test the codes, start up the application. Then, go to a URL, as shown in the video below. The user name and password are karl and password, respectively.
Bearer Authentication Example
The Actix-Web Bearer authentication example is slightly different from the previous codes. It needs to use Bearer-related imports.
1 2 3 4 5 6 | use actix_web_httpauth::extractors::bearer::{BearerAuth, Config}; use actix_web_httpauth::extractors::AuthenticationError; use actix_web_httpauth::middleware::HttpAuthentication; use actix_web::{get, web, App, HttpServer, Error, Responder, dev::ServiceRequest}; // ... |
The main function now uses HttpAuthentication::bearer instead of HttpAuthentication::basic.
1 2 3 4 5 6 7 8 9 10 11 12 13 | #[actix_rt::main] async fn main() -> std::io::Result<()> { HttpServer::new(move || { let auth = HttpAuthentication::bearer(bearer_auth_validator); App::new() .wrap(auth) //.data(pool.clone()) .route("/{id}/{name}/index.html", web::get().to(index)) }) .bind("127.0.0.1:8080")? .run() .await } |
The validator function now also accepts BearerAuth.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | async fn bearer_auth_validator(req: ServiceRequest, credentials: BearerAuth) -> Result<ServiceRequest, Error> { let config = req .app_data::<Config>() .map(|data| data.get_ref().clone()) .unwrap_or_else(Default::default); match validate_token(credentials.token()) { Ok(res) => { if res == true { Ok(req) } else { Err(AuthenticationError::from(config).into()) } } Err(_) => Err(AuthenticationError::from(config).into()), } } |
Lastly, we validate the token against a hard-coded value, which could be anything in real-life applications.
1 2 3 4 5 6 7 8 | fn validate_token(str: &str) -> Result<bool, std::io::Error> { if(str.eq("a-secure-token")) { return Ok(true); } return Err(std::io::Error::new(std::io::ErrorKind::Other, "Authentication failed!")); } |
Testing these codes differs from the previous ones because we need a way to set the Authorization HTTP header before sending the request. For this example, we use Authorization: Bearer a-secure-token.
In Actix-Web, we perform authentication either via Basic or Bearer authentication. Each is unique in terms of coding and specific implementation.