openidconnect-rs

repository·main·Indexed 20 days ago

https://github.com/ramosbugs/openidconnect-rs

A Rust library providing strongly-typed, extensible interfaces for the OpenID Connect (OIDC) protocol. It supports Relying Party flows (code, implicit, hybrid), ID token verification (RSA, HMAC, ECDSA, EdDSA), and OIDC Discovery. The library also implements OAuth 2.0 Token Introspection (RFC 7662), Token Revocation (RFC 7009), and Device Authorization Grant (RFC 8628).

Tokens
18.6K
Snippets
56
Records
78
Agent score
67%

What's inside openidconnect

  1. Overview of the openidconnect crate

    main
    The openidconnect crate provides extensible, strongly-typed Rust interfaces for the OpenID Connect protocol. It is designed to facilitate user authentication via various identity providers such as Google, GitLab, Microsoft, and other OpenID Connect certified providers. For detailed API usage, refer to the docs.rs documentation.
  2. Use stateful HTTP clients with AsyncHttpClient and SyncHttpClient

    main

    To enable TCP connection reuse and easier customization (like adding headers), 4.0 moves from stateless function-based HTTP clients to stateful trait-based clients.

    Traits

    • AsyncHttpClient: For asynchronous requests.
    • SyncHttpClient: For synchronous requests.

    Implementations

    • AsyncHttpClient is implemented for:
      • reqwest::Client (with reqwest feature).
      • Any function: Fn(HttpRequest) -> F where F: Future<Output = Result<HttpResponse, E>>.
    • SyncHttpClient is implemented for:
      • reqwest::blocking::Client (with reqwest-blocking feature).
      • ureq::Agent (with ureq feature).
      • openidconnect::CurlHttpClient (with curl feature).
      • Any function: Fn(HttpRequest) -> Result<HttpResponse, E>.
    WARNING

    To prevent SSRF vulnerabilities, configure your HTTP client not to follow redirects (e.g., use redirect::Policy::none in reqwest).

  3. Check Minimum Supported Rust Version (MSRV)

    main

    Before integrating this crate, ensure your Rust toolchain meets the required version based on the crate version you are using:

    • Version 3.3 and newer: Rust 1.65
    • Versions 3.0 to 3.2: Rust 1.57
    • Version 2.x: Rust 1.45

    Note: Since version 3.0.0, the crate maintains a policy of supporting Rust releases going back at least 6 months. MSRV changes occur during minor version updates and not in patch releases.

  4. Upgrade from 3.x to 4.x: JWT and Token Response changes

    main

    The 4.0 release replaces several generic type parameters with associated types to reduce redundancy and improve type safety.

    JWT Traits

    JsonWebKey no longer uses generic parameters for signing algorithms or key types. Instead, it uses:

    • type KeyUse: JsonWebKeyUse;
    • type SigningAlgorithm: JwsSigningAlgorithm;

    JwsSigningAlgorithm now uses:

    • type KeyType: JsonWebKeyType;

    Token Responses

    In OAuth2TokenResponse and TokenIntrospectionResponse, the generic TT: TokenType parameter has been replaced with an associated type:

    • type TokenType;

    Note: If you provide custom implementations of TokenResponse or TokenIntrospectionResponse, you must update them to use the associated type instead of the generic parameter.

  5. Upgrade from 3.x to 4.x: Typestate-based Client configuration

    main

    In 4.0, Client uses typestates to track which endpoints have been configured at compile time. This prevents runtime errors by ensuring auth flows only run if their required endpoints (like authorization or token URIs) are set.

    Key Changes:

    • All endpoints are now optional.
    • Three Typestates:
      • EndpointNotSet: Cannot be used.
      • EndpointSet: Ready for use.
      • EndpointMaybeSet: Used when using Client::from_provider_metadata(). These endpoints must be accessed via fallible methods that return Err(ConfigurationError::MissingUrl(_)) if the endpoint was missing from the provider metadata.

    Migration Steps:

    1. Update Client::new(): Use the three-argument constructor: Client::new(client_id, issuer_url, jwk_set). Use setters like set_auth_uri(), set_token_uri(), etc., to configure the client.
    2. Handle Discovery Errors: If using Client::from_provider_metadata(), update call sites (e.g., exchange_code()) to handle potential ConfigurationError results.
    3. Update Type Definitions: If you store Client or CoreClient in custom data structures, you may need to add generic typestate parameters:
      • HasAuthUrl: EndpointState
      • HasDeviceAuthUrl: EndpointState
      • HasIntrospectionUrl: EndpointState
      • HasRevocationUrl: EndpointState
      • HasTokenUrl: EndpointState
      • HasUserInfoUrl: EndpointState
    // Example: Annotating a CoreClient with specific typestates
    type MyClient = CoreClient<EndpointSet, EndpointNotSet, EndpointNotSet, EndpointNotSet, EndpointSet, EndpointNotSet>;
  6. Configure the `reqwest-blocking` feature for synchronous HTTP

    main

    In 4.0, the reqwest feature only enables the asynchronous client. To use the synchronous (blocking) reqwest client, you must explicitly enable the reqwest-blocking feature in your Cargo.toml.

    openidconnect = { version = "4", features = ["reqwest-blocking"] }
    openidconnect = { version = "4", features = ["reqwest-blocking" ] }
  7. Configure Authentication Flows

    main

    The AuthenticationFlow enum determines how the Authorization Server returns tokens to the Relying Party. You can choose from three primary flows:

    • AuthorizationCode: The standard flow where the server returns an authorization code, which you then exchange for tokens using Client::exchange_code().
    • Implicit(bool): Returns tokens directly. The boolean parameter indicates whether an OAuth2 access token should also be returned (if true, both access and ID tokens are returned; if false, only the ID token is returned).
    • Hybrid(Vec<RT>): A hybrid flow where you specify a vector of desired ResponseTypes.
    use openidconnect::AuthenticationFlow;
    use openidconnect::core::CoreResponseType;
    
    // Authorization Code Flow
    let flow = AuthenticationFlow::<CoreResponseType>::AuthorizationCode;
    
    // Implicit Flow (returning both ID token and Access token)
    let flow = AuthenticationFlow::<CoreResponseType>::Implicit(true);
    
    // Hybrid Flow
    let flow = AuthenticationFlow::<CoreResponseType>::Hybrid(vec![CoreResponseType::Code]);
  8. Use LogoutProviderMetadata for RP-Initiated Logout

    main

    If your OpenID Connect provider implements RP-Initiated Logout 1.0, its discovery metadata will include an end_session_endpoint.

    You can use the LogoutProviderMetadata<A> struct to wrap your provider's additional metadata. Alternatively, use the ProviderMetadataWithLogout type alias for a pre-configured ProviderMetadata that includes logout support with no additional custom metadata.

    // Using the convenience type alias for providers with logout support
    let provider_metadata: ProviderMetadataWithLogout = serde_json::from_str(json_response).unwrap();
    
    // Accessing the end session endpoint
    let endpoint = provider_metadata.additional_metadata().end_session_endpoint;
  9. Implement custom additional claims

    main

    If your Identity Provider returns claims that are not part of the OpenID Connect Core standard, you can include them by implementing the AdditionalClaims trait. This allows you to co-flatten your custom claims alongside StandardClaims during deserialization.

    To use no additional claims, use the EmptyAdditionalClaims struct.

    use openidconnect::AdditionalClaims;
    
    #[derive(Debug, serde::Deserialize, serde::Serialize)]
    struct MyCustomClaims {
        custom_id: String,
    }
    impl AdditionalClaims for MyCustomClaims {}
    
    // Use MyCustomClaims when working with StandardClaims<GC, MyCustomClaims> (if the API supports it)
  10. Securely compare secrets

    main

    When comparing secrets (like client secrets or tokens) to avoid timing side-channel attacks, do not use standard equality.

    Options:

    1. Manual: Compute a cryptographically-secure hash (e.g., SHA-256) of both values and compare the hashes using ==.
    2. Feature Flag: Enable the timing-resistant-secret-traits feature flag. This adds a safe (but more expensive) PartialEq implementation to the crate's secret types.
  11. Understand JsonWebKeyAlgorithm compatibility

    main

    The JsonWebKeyAlgorithm<A> enum describes how a key's alg field restricts its usage. It is returned by the signing_alg() method on a JsonWebKey.

    Variants:

    • Algorithm(A): The alg field is present and restricts the key to this specific algorithm only.
    • Unspecified: No alg field is present in the JWK, meaning no specific algorithm constraint is applied.
    • Unsupported: The alg field is present but is incompatible with the requested operation (e.g., an encryption algorithm used in a signing context).
  12. Securely compare secrets with `timing-resistant-secret-traits`

    main

    To avoid timing side-channel attacks, comparing secrets like CsrfToken or Nonce should be done in constant time.

    In 4.0, IdToken, IdTokenClaims, and IdTokenFields only implement PartialEq if the timing-resistant-secret-traits feature flag is enabled. Enabling this flag provides a safe (but more expensive) PartialEq implementation for secret types.