openid-client 0.2.7

OpenID client for Rust
Documentation
use crate::{
    client::Client,
    helpers::convert_json_to,
    http_client::DefaultHttpClient,
    issuer::Issuer,
    jwks::Jwks,
    types::{HttpMethod, IssuerMetadata},
};

use crate::tests::test_http_client::TestHttpReqRes;

static DEFAULT_CLIENT_READ: &str = r#"{"client_id":"identifier","client_secret":"secure"}"#;

#[tokio::test]
async fn accepts_and_assigns_discovered_metadata() {
    let http_client = TestHttpReqRes::new("https://op.example.com/client/identifier")
        .assert_request_method(HttpMethod::GET)
        .assert_request_header("accept", vec!["application/json".to_string()])
        .set_response_body(DEFAULT_CLIENT_READ)
        .set_response_content_type_header("application/json")
        .build();

    let client_registration_uri = "https://op.example.com/client/identifier";

    let client = Client::from_uri_async(
        &http_client,
        &client_registration_uri,
        &Issuer::new(IssuerMetadata::default()),
        None,
        None,
        None,
        None,
    )
    .await
    .unwrap();

    assert_eq!("identifier", client.client_id);

    assert_eq!("secure", client.client_secret.unwrap());
}

#[tokio::test]
async fn is_rejected_with_op_error_upon_oidc_error() {
    let http_client = TestHttpReqRes::new("https://op.example.com/client/identifier")
        .assert_request_method(HttpMethod::GET)
        .assert_request_header("accept", vec!["application/json".to_string()])
        .set_response_body(
            r#"{"error":"server_error","error_description":"bad things are happening"}"#,
        )
        .set_response_status_code(500)
        .build();

    let client_registration_uri = "https://op.example.com/client/identifier";

    let client_error = Client::from_uri_async(
        &http_client,
        &client_registration_uri,
        &Issuer::new(IssuerMetadata::default()),
        None,
        None,
        None,
        None,
    )
    .await
    .unwrap_err();

    assert!(client_error.is_op_error());

    let err = client_error.op_error().error;

    assert_eq!("server_error", err.error);

    assert_eq!(
        Some("bad things are happening".to_string()),
        err.error_description
    );
}

#[tokio::test]
async fn is_rejected_with_op_error_upon_oidc_error_in_www_authenticate_header() {
    let http_client = TestHttpReqRes::new("https://op.example.com/client/identifier")
        .assert_request_method(HttpMethod::GET)
        .assert_request_header("accept", vec!["application/json".to_string()])
        .set_response_body("Unauthorized")
        .set_response_www_authenticate_header(
            r#"Bearer error="invalid_token", error_description="bad things are happening""#,
        )
        .set_response_status_code(401)
        .build();

    let client_registration_uri = "https://op.example.com/client/identifier";

    let client_error = Client::from_uri_async(
        &http_client,
        &client_registration_uri,
        &Issuer::new(IssuerMetadata::default()),
        None,
        None,
        None,
        None,
    )
    .await
    .unwrap_err();

    assert!(client_error.is_op_error());

    let err = client_error.op_error().error;

    assert_eq!("invalid_token", err.error);

    assert_eq!(
        Some("bad things are happening".to_string()),
        err.error_description
    );
}

#[tokio::test]
async fn is_rejected_with_when_non_200_is_returned() {
    let http_client = TestHttpReqRes::new("https://op.example.com/client/identifier")
        .assert_request_method(HttpMethod::GET)
        .assert_request_header("accept", vec!["application/json".to_string()])
        .set_response_body("Internal Server Error")
        .set_response_status_code(500)
        .build();

    let client_registration_uri = "https://op.example.com/client/identifier";

    let client_error = Client::from_uri_async(
        &http_client,
        &client_registration_uri,
        &Issuer::new(IssuerMetadata::default()),
        None,
        None,
        None,
        None,
    )
    .await
    .unwrap_err();

    assert!(client_error.is_op_error());

    let err = client_error.op_error();

    assert!(err.response.is_some());

    assert_eq!(
        Some("expected 200 OK, got: 500 Internal Server Error".to_string()),
        err.error.error_description
    );
}

#[tokio::test]
async fn is_rejected_with_json_parse_error_upon_invalid_response() {
    let http_client = TestHttpReqRes::new("https://op.example.com/client/identifier")
        .assert_request_method(HttpMethod::GET)
        .assert_request_header("accept", vec!["application/json".to_string()])
        .set_response_body(r#"{"notavalid"}"#)
        .build();

    let client_registration_uri = "https://op.example.com/client/identifier";

    let client_error = Client::from_uri_async(
        &http_client,
        &client_registration_uri,
        &Issuer::new(IssuerMetadata::default()),
        None,
        None,
        None,
        None,
    )
    .await
    .unwrap_err();

    assert!(client_error.is_type_error());

    let err = client_error.type_error().error;

    assert_eq!("unexpected body type", err.message);
}

#[tokio::test]
async fn does_not_accept_oct_keys() {
    let client_registration_uri = "https://op.example.com/client/registration";

    let jwks = Some(convert_json_to::<Jwks>(r#"{"keys":[{"k":"qHedLw","kty":"oct","kid":"R5OsS5S7xvrW7E0k0t0PwRsskJpdOkyfnAZi8S806Bg"}]}"#).unwrap());

    let client_error = Client::from_uri_async(
        &DefaultHttpClient,
        &client_registration_uri,
        &Issuer::new(IssuerMetadata::default()),
        None,
        jwks,
        None,
        None,
    )
    .await
    .unwrap_err();

    assert_eq!(
        "jwks must only contain private keys",
        client_error.error().error.message
    );
}

#[tokio::test]
async fn does_not_accept_public_keys() {
    let client_registration_uri = "https://op.example.com/client/registration";

    let jwks = Some(convert_json_to::<Jwks>(r#"{"keys":[{"kty":"EC","kid":"MFZeG102dQiqbANoaMlW_Jmf7fOZmtRsHt77JFhTpF0","crv":"P-256","x":"FWZ9rSkLt6Dx9E3pxLybhdM6xgR5obGsj5_pqmnz5J4","y":"_n8G69C-A2Xl4xUW2lF0i8ZGZnk_KPYrhv4GbTGu5G4"}]}"#).unwrap());
    let client_error = Client::from_uri_async(
        &DefaultHttpClient,
        &client_registration_uri,
        &Issuer::new(IssuerMetadata::default()),
        None,
        jwks.clone(),
        None,
        None,
    )
    .await
    .unwrap_err();

    assert_eq!(
        "jwks must only contain private keys",
        client_error.error().error.message
    );
}