Cookbook / Auth and sessions

API keys and basic auth

Check opaque tokens with your own async lookup, and protect internal tools with HTTP Basic.

BearerAuth takes an async function from the token to your user type (or None). Look the token up in a database, a cache or another service. A successful lookup puts the user in the request extensions; a failed one gets 401 with a WWW-Authenticate header. BasicAuth guards admin pages and internal dashboards.

src/main.rs
use axeon::auth::{constant_time_eq, BasicAuth, BasicUser, BearerAuth};
use axeon::{Router, Server};

#[derive(Clone)]
struct Client {
    name: String,
}

fn main() {
    let mut app = Server::new();

    // Public API: every request needs a known key.
    let mut api = Router::new();
    api.middleware(BearerAuth::new(|token: String| async move {
        // Replace with a database or cache lookup of a hashed key.
        constant_time_eq(token.as_bytes(), b"sk_live_example")
            .then(|| Client { name: "acme-corp".into() })
    }));
    api.get("/whoami", |req| async move {
        req.extension::<Client>().map(|client| client.name.clone())
    });
    app.mount("/api", api);

    // Internal tools behind Basic auth. Use HTTPS only.
    let mut admin = Router::new();
    admin.middleware(BasicAuth::single("admin area", "admin", "correct horse battery staple"));
    admin.get("/", |req| async move {
        req.extension::<BasicUser>().map(|user| format!("welcome, {}", user.0))
    });
    app.mount("/admin", admin);

    app.listen("127.0.0.1:3000").expect("server failed to start");
}
Terminal
curl -i http://127.0.0.1:3000/api/whoami                                  # 401
curl http://127.0.0.1:3000/api/whoami -H 'authorization: Bearer sk_live_example'
curl -u admin:'correct horse battery staple' http://127.0.0.1:3000/admin/

Notes#

  • Compare secrets with constant_time_eq, never ==, so response timing does not leak how much of a guess was right.
  • Store only a hash of each API key, and look up by that hash.
  • BasicAuth::new(realm, |user, password| ..) accepts any check, for example a list of users.