This post has code examples for Actix-Web Basic and Bearer authentications. 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.
Contents
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 is 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.
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 is different from the previous codes. We need a way to set the Authorization HTTP header before sending the request. For this example, we use Authorization: Bearer a-secure-token.