The 4.0 release includes breaking changes to address several long-standing API issues, along with a few minor improvements. Consider following the tips below to help ensure a smooth upgrade process. This document is not exhaustive but covers the breaking changes most likely to affect typical uses of this crate.
Each auth flow depends on one or more server endpoints. For example, the
authorization code flow depends on both an authorization endpoint and a token endpoint, while the
client credentials flow only depends on a token endpoint. Previously, it was possible to instantiate
a Client
without a token endpoint and then attempt to use an auth flow that required a token
endpoint, leading to errors at runtime. Also, the authorization endpoint was always required, even
for auth flows that do not use it.
In the 4.0 release, all endpoints are optional.
Typestates are used to statically track, at compile
time, which endpoints' setters (e.g., set_auth_uri()
) have been called. Auth flows that depend on
an endpoint cannot be used without first calling the corresponding setter, which is enforced by the
compiler's type checker. This guarantees that certain errors will not arise at runtime.
When using OpenID Connect Discovery
(i.e., Client::from_provider_metadata()
),
each discoverable endpoint is set to a conditional typestate (EndpointMaybeSet
). This is because
it cannot be determined at compile time whether each of these endpoints will be returned by the
OpenID Provider. When the conditional typestate is set, endpoints can be used via fallible methods
that return Err(ConfigurationError::MissingUrl(_))
if an endpoint has not been set.
There are three possible typestates, each implementing the EndpointState
trait:
EndpointNotSet
: the corresponding endpoint has not been set and cannot be used.EndpointSet
: the corresponding endpoint has been set and is ready to be used.EndpointMaybeSet
: the corresponding endpoint may have been set and can be used via fallible methods that returnResult<_, ConfigurationError>
.
The following code changes are required to support the new interface:
-
Update calls to
Client::new()
to use the three-argument constructor (which accepts only aClientId
,IssuerUrl
, andJsonWebKeySet
). Use theset_auth_uri()
,set_token_uri()
,set_user_info_url()
, andset_client_secret()
methods to set the authorization endpoint, token endpoint, user info endpoint, and client secret, respectively, if applicable to your application's auth flows. -
If using
Client::from_provider_metadata()
, update call sites that use each auth flow (e.g.,Client::exchange_code()
) to handle the possibility of aConfigurationError
if the corresponding endpoint was not specified in the provider metadata. -
If required by your usage of the
Client
orCoreClient
types (i.e., if you see related compiler errors), add the following generic parameters:HasAuthUrl: EndpointState, HasDeviceAuthUrl: EndpointState, HasIntrospectionUrl: EndpointState, HasRevocationUrl: EndpointState, HasTokenUrl: EndpointState, HasUserInfoUrl: EndpointState,
For example, if you store a
CoreClient
within another data type, you may need to annotate it asCoreClient<EndpointSet, EndpointNotSet, EndpointNotSet, EndpointNotSet, EndpointSet, EndpointNotSet>
if it has both an authorization endpoint and a token endpoint set. Compiler error messages will likely guide you to the appropriate combination of typestates.If, instead of using
CoreClient
, you are directly usingClient
with a different set of type parameters, you will need to append the five generic typestate parameters. For example, replace:type SpecialClient = Client< EmptyAdditionalClaims, CoreAuthDisplay, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType, CoreJsonWebKeyUse, CoreJsonWebKey, CoreAuthPrompt, StandardErrorResponse<CoreErrorResponseType>, SpecialTokenResponse, CoreTokenType, CoreTokenIntrospectionResponse, CoreRevocableToken, CoreRevocationErrorResponse, >;
with:
type SpecialClient< HasAuthUrl = EndpointNotSet, HasDeviceAuthUrl = EndpointNotSet, HasIntrospectionUrl = EndpointNotSet, HasRevocationUrl = EndpointNotSet, HasTokenUrl = EndpointNotSet, HasUserInfoUrl = EndpointNotSet, > = Client< EmptyAdditionalClaims, CoreAuthDisplay, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJsonWebKey, CoreAuthPrompt, StandardErrorResponse<CoreErrorResponseType>, SpecialTokenResponse, CoreTokenIntrospectionResponse, CoreRevocableToken, CoreRevocationErrorResponse, HasAuthUrl, HasDeviceAuthUrl, HasIntrospectionUrl, HasRevocationUrl, HasTokenUrl, HasUserInfoUrl, >;
The default values (
= EndpointNotSet
) are optional but often helpful since they will allow you to instantiate a client usingSpecialClient::new()
instead of having to specifySpecialClient::<EndpointNotSet, EndpointNotSet, EndpointNotSet, EndpointNotSet, EndpointNotSet, EndpointNotSet>::new()
.Also note that the
CoreJwsSigningAlgorithm
(JS
),CoreJsonWebKeyType
(JT
),CoreJsonWebKeyUse
(JU
), andCoreTokenType
(TT
) type parameters have been removed (see below) since they are now implied by theJsonWebKey
(K
) andTokenResponse
(TR
)/TokenIntrospectionResponse
(TIR
) type parameters.
Previously, the JsonWebKey
trait had the following generic type parameters:
JS: JwsSigningAlgorithm<JT>,
JT: JsonWebKeyType,
JU: JsonWebKeyUse,
In the 4.0 release, these generic type parameters have been removed and replaced with two associated types:
/// Allowed key usage.
type KeyUse: JsonWebKeyUse;
/// JSON Web Signature (JWS) algorithm.
type SigningAlgorithm: JwsSigningAlgorithm;
The JT
type parameter was similarly removed from the JwsSigningAlgorithm
trait and replaced
with an associated type:
/// Key type (e.g., RSA).
type KeyType: JsonWebKeyType;
Similar changes were made to the lesser-used PrivateSigningKey
and JweContentEncryptionAlgorithm
traits.
With the conversion to associated types, many generic type parameters throughout this crate became
redundant and were removed in the 4.0 release. For example, the Client
no longer needs the
JS
, JT
, or JU
parameters, which are implied by the JsonWebKey
(K
) type.
The 2.0 release aimed to align the naming of each endpoint with the terminology used in the relevant RFC. For example, RFC 6749 uses the term "endpoint URI" to refer to the authorization and token endpoints, while RFC 7009 refers to the "token revocation endpoint URL," and RFC 7662 uses neither "URI" nor "URL" to describe the introspection endpoint. However, the renaming in 2.0 was both internally inconsistent, and inconsistent with the specs.
In 4.0, the Client
's getters and setters for each endpoint are now named as follows:
- Authorization endpoint:
auth_uri()
/set_auth_uri()
(newly added) - Token endpoint:
token_uri()
/set_token_uri()
(newly added) - Redirect:
redirect_uri()
/set_redirect_uri()
(no change to setter) - Revocation endpoint:
revocation_url()
/set_revocation_url()
- Introspection endpoint:
introspection_url()
/set_introspection_url()
- Device authorization endpoint:
device_authorization_url()
/set_device_authorization_url()
- User info:
user_info_url()
/set_user_info_url()
(newly added)
Previously, the HTTP clients provided by this crate were stateless. For example, the
openidconnect::reqwest::async_http_client()
method would instantiate a new reqwest::Client
for
each request. This meant that TCP connections could not be reused across requests, and customizing
HTTP clients (e.g., adding a custom request header to every request) was inconvenient.
The 4.0 release introduces two new traits: AsyncHttpClient
and SyncHttpClient
. Each
request_async()
and request()
method now accepts a reference to a type that implements these
traits, respectively, rather than a function type.
Warning
To prevent
SSRF
vulnerabilities, be sure to configure the HTTP client not to follow redirects. For example, use
redirect::Policy::none
when using reqwest
, or
redirects(0)
when using ureq
.
The AsyncHttpClient
trait is implemented for the following types:
reqwest::Client
(when the defaultreqwest
feature is enabled)- Any function type that implements:
To implement a custom asynchronous HTTP client, either directly implement the
Fn(HttpRequest) -> F where E: std::error::Error + 'static, F: Future<Output = Result<HttpResponse, E>>,
AsyncHttpClient
trait, or use a function that implements the signature above.
The SyncHttpClient
trait is implemented for the following types:
reqwest::blocking::Client
(when thereqwest-blocking
feature is enabled; see below)ureq::Agent
(when theureq
feature is enabled)openidconnect::CurlHttpClient
(when thecurl
feature is enabled)- Any function type that implements:
To implement a custom synchronous HTTP client, either directly implement the
Fn(HttpRequest) -> Result<HttpResponse, E> where E: std::error::Error + 'static,
SyncHttpClient
trait, or use a function that implements the signature above.
The 4.0 release of this crate depends on the new stable http
1.0 release, which affects various public interfaces. In particular, reqwest
has been upgraded
to 0.12, which uses http
1.0.
In 4.0, enabling the (default) reqwest
feature also enabled reqwest
's blocking
feature.
To reduce dependencies and improve compilation speed, the reqwest
feature now only enables
reqwest
's asynchronous (non-blocking) client. To use the synchronous (blocking) client, enable the
reqwest-blocking
feature in Cargo.toml
:
openidconnect = { version = "4", features = ["reqwest-blocking" ] }
The HttpRequest
and HttpResponse
structs have been replaced with type aliases to
http::Request
and
http::Response
, respectively.
Custom HTTP clients will need to be updated to use the http
types. See the
reqwest
client implementations
in the underlying oauth2
crate for an example.
Previously, the TokenResponse
, OAuth2TokenResponse
, and TokenIntrospectionResponse
traits had
a generic type parameter TT: TokenType
. This has been replaced with an associated type called
TokenType
in OAuth2TokenResponse
and TokenIntrospectionResponse
.
Uses of CoreTokenResponse
and CoreTokenIntrospectionResponse
should continue to work without
changes, but custom implementations of either trait will need to be updated to replace the type
parameter with an associated type.
Removing the TT
generic type parameter from TokenResponse
(see above) made the TT
parameters
to Client
and each *Request
(e.g., CodeTokenRequest
) redundant. Consequently, the TT
parameter has been removed from each of these types. CoreClient
should continue to work
without any changes, but code that provides generic types for Client
or any of the *Response
types will need to be updated to remove the TT
type parameter.
To improve error messages, the
RequestTokenError::ServerResponse
enum variant now prints a message describing the server response using the Display
trait. For most
users (i.e., those using the default
StandardErrorResponse
),
this does not require any code changes. However, users providing their own implementations
of the ErrorResponse
trait must now implement the Display
trait. See
oauth2::StandardErrorResponse
's
Display
implementation
for an example.
The 4.0 release removes the jwk-alg
feature flag and unconditionally deserializes the optional
alg
field in CoreJsonWebKey
. If a key specifies the alg
field, the key may only be used for
the purposes of verifying signatures using that specific JWS signature algorithm. By comparison,
the 3.0 release ignored the alg
field unless the jwk-alg
feature flag was enabled.
OpenID Connect flows require comparing secrets (e.g., CsrfToken
and Nonce
) received from
providers. To do so securely
while avoiding timing side-channels, the
comparison must be done in constant time, either using a constant-time crate such as
constant_time_eq
(which could break if a future
compiler version decides to be overly smart
about its optimizations), or by first computing a cryptographically-secure hash (e.g., SHA-256)
of both values and then comparing the hashes using ==
.
The timing-resistant-secret-traits
feature flag adds a safe (but comparatively expensive)
PartialEq
implementation to the secret types. Timing side-channels are why PartialEq
is
not auto-derived for this crate's secret types, and the lack of PartialEq
is intended to
prompt users to think more carefully about these comparisons.
In the 3.0 release, the Nonce
type implemented PartialEq
by default, which also allowed the
IdToken
, IdTokenClaims
, and IdTokenFields
types to implement PartialEq
. In 4.0, these
types implement PartialEq
only if the timing-resistant-secret-traits
feature flag is enabled.
Certain JWS signature algorithms (e.g., EdDSA
) require information from the corresponding public
key (e.g., the crv
value) to determine which hash function to use for computing the at_hash
and
c_hash
ID token claims. To accommodate this requirement, the 4.0 release moves the hash_bytes()
method from the JwsSignatureAlgorithm
trait to the JsonWebKey
trait.
The AccessTokenHash::from_token()
and AuthorizationCodeHash::from_code()
methods now require
a JsonWebKey
as an argument.