rust-axum vs. Auth0: A Trial by Combat Integration

Grzegorz Bylica
3 min read3 days ago

--

This is the first part, covering only Path Login.

@startuml
actor User
User -> gui : Open app (trigger init)
gui -> my_backend : init (login request)
my_backend -> auth0 : Redirect to Auth0 login
auth0 -> User : Show login form
User -> auth0 : Submit credentials
auth0 -> my_backend : Callback with token
my_backend -> gui : Response with tokens (token in cookies)
@enduml

I will utilize existing code. I will implement a login controller to redirect users to Auth0 (login), and then I will ensure it works correctly.

I’m using the code from https://github.com/opendrafts-rs-os/openapi-axum-server/tree/start

Note: The code generated by OpenAPI for rust-axum does not work in this case, so the login controller (redirect to Auth0) will not be generated code.

I’m editing the script so that after generating the code, it adds the required dependencies, as rust-axum does not allow selective overwriting of Cargo.toml.

Here is the current state of the script gen.sh:

#!/bin/bash

java -jar openapi-generator-cli.jar generate \
-i ./example-openapi.yaml \
-g rust-axum \
-o ../api \


# added dependency for ../api/src/bin/main.rs
sed -i '/^\[dependencies\]/a clap = { version = "4.0", features = ["derive"] }' ../api/Cargo.toml
sed -i '/^\[dependencies\]/a urlencoding = "2.1.3"' ../api/Cargo.toml
sed -i '/^\[dependencies\]/a rand = "0.8.5"' ../api/Cargo.toml

I’m running the generation

cd /openapi
./gen.sh

I am modifying main.rs by adding a login controller and registering it in the routing.

use http::Method;
...
pub async fn login_get( _method: Method, _host: Host, _cookies: CookieJar) -> Redirect {

let csrf_state = generate_random_string(32);
let nonce = generate_random_string(32);

// TODO: Save csrf_state and nonce in (session or cookie), eg.:
// session.insert("auth_csrf_state", &csrf_state);
// session.insert("auth_nonce", &nonce);

let auth_url = format!(
"https://{}/authorize\
?response_type={}\
&client_id={}\
&redirect_uri={}\
&scope={}\
&state={}\
&nonce={}",
AUTH0.get().unwrap().auth0_domain,
AUTH0.get().unwrap().auth0_response_type,
AUTH0.get().unwrap().auth0_client_id,
urlencoding::encode(&AUTH0.get().unwrap().auth0_redirect_uri),
AUTH0.get().unwrap().auth0_scope,
csrf_state,
nonce
);

Redirect::to(&auth_url)

}
...


#[tokio::main]
async fn main() {

let args = ArgsAuth0::parse();

AUTH0.set(args).unwrap();

let api_impl = MyApi;

let app_open_api = server::new(api_impl);

let app_login = Router::new()
.route("/login", get(login_get));

let app = Router::new()
.merge(app_open_api)
.merge(app_login);

...
}

Define environment variables in .env to provide the application with the Auth0 configuration

EXAMPLE_AUTH0_DOMAIN=dev-...auth0.com
EXAMPLE_CLIENT_ID=KX...z
EXAMPLE_REDIRECT_URI=http://localhost:3000/callback
EXAMPLE_RESPONSE_TYPE="token"
EXAMPLE_SCOPE="openid"

#RUST_BACKTRACE=1

Important notice: Adding .env to .gitignore

target
Cargo.lock
.envg

I am creating a startup script, run.sh

#!/bin/bash
set -o allexport
source .env
set +o allexport

cargo run \
--quite
-- \
--auth0-domain "$EXAMPLE_AUTH0_DOMAIN" \
--auth0-client-id "$EXAMPLE_CLIENT_ID" \
--auth0-client-secret "$EXAMPLE_CLIENT_SECRET"

I am completing the code that parses arguments main.rs

use clap::Parser;

...

#[derive(Parser, Debug)]
#[command(name = "Auth0 CLI", about = "")]
struct ArgsAuth0 {
#[arg(long)]
auth0_domain: String,

#[arg(long)]
auth0_client_id: String,

#[arg(long, default_value = "http://localhost:3000/callback")]
auth0_redirect_uri: String,

#[arg(long)]
auth0_response_type: String,

#[arg(long)]
auth0_scope: String,

}

static AUTH0: OnceLock<ArgsAuth0> = OnceLock::new();

...
#[tokio::main]
async fn main() {

let args = ArgsAuth0::parse();

AUTH0.set(args).unwrap();

...
}

I am starting the service

cd ./api
./run.sh

I am checking if it works.

In the browser, open:

http://127.0.0.1:3000

and verify whether we are correctly redirected to the Auth0 login prompt.

Summary

With a small amount of code, authentication can be quickly implemented. The parameters are easy to read, and even in case of complications, the libraries are designed to be easily used in various scenarios.

code: https://github.com/opendrafts-rs-os/openapi-axum-server/tree/auth0

Reading materials

https://traefik-forward-auth0.readthedocs.io/en/latest/dev/diagrams.htm

Thank you :)

--

--

Grzegorz Bylica
Grzegorz Bylica

Written by Grzegorz Bylica

I work with different technologies, but programming in Rust is especially fun. Connect with me on LinkedIn https://www.linkedin.com/in/grzegorz-bylica

No responses yet