1 unstable release

new 0.1.11 Jan 31, 2025

#381 in Authentication

MIT license

42KB
1K SLoC

ONES OIDC Rust Authentication Library

Package to authenticate with ONES using OpenID Connect (OIDC) and Client Initiated Backchannel Authentication (CIBA).

Usage

1.0 Device Authentication

use ones_oidc_rust::{OpenIdconnectClient, load_device_config};

let device_config = load_device_config(&device_config_path).expect("Failed to load device config");

let client_id = ClientId::new(device_config.client_id.clone().to_string());
let issuer_url = IssuerUrl::new(device_config.issuer_url.to_string())
    .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Failed to parse issuer URL"))?;

let provider_metadata = CoreProviderMetadata::discover_async(
    issuer_url.clone(),
    async_http_client,
)
.await
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Failed to discover OIDC metadata"))?;

let openid_client = OpenIdconnectClient {
    client_id: client_id,
    issuer_url: issuer_url,
    issuer_jwks: None,
    provider_metadata: provider_metadata.clone(),
    // read_private_key: you can supply your own implementation here
    private_key: read_private_key(&device_config.private_key_path).unwrap(),
};

1.1 Get Device Access Token

// Reuse the `openid_client` from the previous example
let openid_client = OpenIdconnectClient { ... };

let device_access_token = openid_client.request_device_access_token().await;

2.0 CIBA

Authenticate with user username (OnesID).

2.1 Make CIBA Request

// Reuse the `openid_client` from the previous example
let openid_client = OpenIdconnectClient { ... };

let login_hint = LoginHint {
    kind: payload.login_hint_kind,
    value: payload.user_identifier.clone(),
};
let result = openid_client.make_ciba_request(
    &login_hint,
    "openid offline_access profile".to_string(),
    "Authorize login to Example.com?".to_string(),
    Some("https://example.com".to_string())
).await;

if result.is_err() {
    let e = result.err().unwrap();
    debug!("Error: {}", e);
    return Ok(HttpResponse::InternalServerError().finish());
}

let result = result.unwrap();
Ok(HttpResponse::Ok().json(result))

2.2 Check CIBA Status

// Reuse the `openid_client` from the previous example
let openid_client = OpenIdconnectClient { ... };

let ciba_status = openid_client.check_ciba_status(&auth_request_id).await;
if ciba_status.is_err() {
    let ciba_status = ciba_status.unwrap_err();
    if ciba_status.to_string() == "Authorization pending" {
        println!("Authorization pending");
        Ok(HttpResponse::BadRequest().json(ciba_status.to_string()))
    } else {
        println!("Error: {:?}", ciba_status);
        Ok(HttpResponse::InternalServerError().json(ciba_status.to_string()))
    }
} else {
    let ciba_status = ciba_status.unwrap();
    println!("CIBA Status: {:?}", ciba_status);
    Ok(HttpResponse::Ok().json(ciba_status))
}

Validate token:

// Reuse the `openid_client` from the previous example
let openid_client = OpenIdconnectClient { ... };

openid_client.validate_token(&id_token).await;

Guix

guix shell rust-cargo rust gcc-toolchain pkg-config openssl
export OPENSSL_DIR=$(dirname $(dirname $(realpath $(which openssl))))
CC=gcc cargo build

Dependencies

~19–34MB
~558K SLoC