# 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

```rs
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

```rs
// 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

```rs
// 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

```rs
// 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:

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

openid_client.validate_token(&id_token).await;
```

## Guix

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