Skip to content

Commit bb0baf1

Browse files
committed
Add upgrade guide
1 parent 23b952b commit bb0baf1

File tree

1 file changed

+184
-0
lines changed

1 file changed

+184
-0
lines changed

UPGRADE.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# Upgrade Guide
2+
3+
## Upgrading from 4.x to 5.x
4+
5+
The 5.0 release includes breaking changes to address several long-standing API issues, along with
6+
a few minor improvements. Consider following the tips below to help ensure a smooth upgrade
7+
process.
8+
9+
### Add typestate const generics to `Client`
10+
11+
Each auth flow depends on one or more server endpoints. For example, the
12+
authorization code flow depends on both an authorization endpoint and a token endpoint, while the
13+
client credentials flow only depends on a token endpoint. Previously, it was possible to instantiate
14+
a `Client` without a token endpoint and then attempt to use an auth flow that required a token
15+
endpoint, leading to errors at runtime. Also, the authorization endpoint was always required, even
16+
for auth flows that do not use it.
17+
18+
In the 5.0 release, all endpoints are optional.
19+
[Typestates](https://cliffle.com/blog/rust-typestate/) are used to statically track, at compile
20+
time, which endpoints' setters (e.g., `set_auth_uri()`) have been called. Auth flows that depend on
21+
an endpoint cannot be used without first calling the corresponding setter, which is enforced by the
22+
compiler's type checker. This guarantees that certain errors will not arise at runtime.
23+
24+
The following code changes are required to support the new interface:
25+
1. Update calls to
26+
[`Client::new()`](https://docs.rs/oauth2/latest/oauth2/struct.Client.html#method.new) to use the
27+
single-argument constructor (which accepts only a `ClientId`). Use the `set_auth_uri()`,
28+
`set_token_uri()`, and `set_client_secret()` methods to set the optional authorization endpoint,
29+
token endpoint, and client secret, respectively, if applicable to your application's auth flows.
30+
2. If required by your usage of the `Client` or `BasicClient` types (i.e., if you see related
31+
compiler errors), add the following generic parameters:
32+
```rust
33+
const HAS_AUTH_URL: bool,
34+
const HAS_DEVICE_AUTH_URL: bool,
35+
const HAS_INTROSPECTION_URL: bool,
36+
const HAS_REVOCATION_URL: bool,
37+
const HAS_TOKEN_URL: bool,
38+
```
39+
For example, if you store a `BasicClient` within another data type, you may need to annotate it
40+
as `BasicClient<true, false, false, false, true>` if it has both an authorization endpoint and a
41+
token endpoint set. Compiler error messages will likely guide you to the appropriate combination
42+
of Boolean values.
43+
44+
If, instead of using `BasicClient`, you are directly using `Client` with a different set of type
45+
parameters, you will need to append the five Boolean typestate parameters. For example, replace:
46+
```rust
47+
type SpecialClient = Client<
48+
BasicErrorResponse,
49+
SpecialTokenResponse,
50+
BasicTokenType,
51+
BasicTokenIntrospectionResponse,
52+
StandardRevocableToken,
53+
BasicRevocationErrorResponse,
54+
>;
55+
```
56+
with:
57+
```rust
58+
type SpecialClient<
59+
const HAS_AUTH_URL: bool = false,
60+
const HAS_DEVICE_AUTH_URL: bool = false,
61+
const HAS_INTROSPECTION_URL: bool = false,
62+
const HAS_REVOCATION_URL: bool = false,
63+
const HAS_TOKEN_URL: bool = false,
64+
> = Client<
65+
BasicErrorResponse,
66+
SpecialTokenResponse,
67+
BasicTokenType,
68+
BasicTokenIntrospectionResponse,
69+
StandardRevocableToken,
70+
BasicRevocationErrorResponse,
71+
HAS_AUTH_URL,
72+
HAS_DEVICE_AUTH_URL,
73+
HAS_INTROSPECTION_URL,
74+
HAS_REVOCATION_URL,
75+
HAS_TOKEN_URL,
76+
>;
77+
```
78+
The default values (`= false`) are optional but often helpful since they will allow you to
79+
instantiate a client using `SpecialClient::new()` instead of having to specify
80+
`SpecialClient::<false, false, false, false, false>::new()`.
81+
82+
### Rename endpoint getters and setters for consistency
83+
84+
The 4.0 release aimed to align the naming of each endpoint with the terminology used in the relevant
85+
RFC. For example, [RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749#section-3.1) uses the
86+
term "endpoint URI" to refer to the authorization and token endpoints, while
87+
[RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009#section-2) refers to the
88+
"token revocation endpoint URL," and
89+
[RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662#section-2) uses neither "URI" nor "URL"
90+
to describe the introspection endpoint. However, the renaming in 4.0 was both internally
91+
inconsistent, and inconsistent with the specs.
92+
93+
In 5.0, the `Client`'s getters and setters for each endpoint are now named as follows:
94+
* Authorization endpoint: `auth_uri()`/`set_auth_uri()`
95+
* Token endpoint: `token_uri()`/`set_token_uri()`
96+
* Redirect: `redirect_uri()`/`set_redirect_uri()`
97+
* Revocation endpoint: `revocation_url()`/`set_revocation_url()`
98+
* Introspection endpoint: `introspection_url()`/`set_introspection_url()`
99+
* Device authorization endpoint: `device_authorization_url()`/`set_device_authorization_url()`
100+
(no change)
101+
102+
### Use stateful HTTP clients
103+
104+
Previously, the HTTP clients provided by this crate were stateless. For example, the
105+
`oauth2::reqwest::async_http_client()` method would instantiate a new `reqwest::Client` for each
106+
request. This meant that TCP connections could not be reused across requests, and customizing HTTP
107+
clients (e.g., adding a custom request header to every request) was inconvenient.
108+
109+
The 5.0 release introduces two new traits: `AsyncHttpClient` and `SyncHttpClient`. Each
110+
`request_async()` and `request()` method now accepts a reference to a type that implements these
111+
traits, respectively, rather than a function type.
112+
113+
> [!WARNING]
114+
> To prevent
115+
[SSRF](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
116+
vulnerabilities, be sure to configure the HTTP client **not to follow redirects**. For example,
117+
use [`redirect::Policy::none`](::reqwest::redirect::Policy::none) when using
118+
[`reqwest`](::reqwest), or [`redirects(0)`](::ureq::AgentBuilder::redirects) when using
119+
[`ureq`](::ureq).
120+
121+
The `AsyncHttpClient` trait is implemented for the following types:
122+
* `reqwest::Client` (when the default `reqwest` feature is enabled)
123+
* Any function type that implements:
124+
```rust
125+
Fn(HttpRequest) -> F
126+
where
127+
E: std::error::Error + 'static,
128+
F: Future<Output = Result<HttpResponse, E>>,
129+
```
130+
To implement a custom asynchronous HTTP client, either directly implement the `AsyncHttpClient`
131+
trait, or use a function that implements the signature above.
132+
133+
The `SyncHttpClient` trait is implemented for the following types:
134+
* `reqwest::blocking::Client` (when the `reqwest-blocking` feature is enabled; see below)
135+
* `ureq::Agent` (when the `ureq` feature is enabled)
136+
* `oauth2::curl::CurlHttpClient` (when the `curl` feature is enabled)
137+
* Any function type that implements:
138+
```rust
139+
Fn(HttpRequest) -> Result<HttpResponse, E>
140+
where
141+
E: std::error::Error + 'static,
142+
```
143+
To implement a custom synchronous HTTP client, either directly implement the `SyncHttpClient`
144+
trait, or use a function that implements the signature above.
145+
146+
### Enable the `reqwest-blocking` feature to use the synchronous `reqwest` HTTP client
147+
148+
In 4.0, enabling the (default) `reqwest` feature also enabled `reqwest`'s `blocking` feature.
149+
To reduce dependencies and improve compilation speed, the `reqwest` feature now only enables
150+
`reqwest`'s asynchronous (non-blocking) client. To use the synchronous (blocking) client, enable the
151+
`reqwest-blocking` feature in `Cargo.toml`:
152+
```toml
153+
oauth2 = { version = "5", features = ["reqwest-blocking" ] }
154+
```
155+
156+
### Use `http::{Request, Response}` for custom HTTP clients
157+
158+
The `HttpRequest` and `HttpResponse` structs have been replaced with type aliases to
159+
[`http::Request`](https://docs.rs/http/latest/http/request/struct.Request.html) and
160+
[`http::Response`](https://docs.rs/http/latest/http/response/struct.Response.html), respectively.
161+
Custom HTTP clients will need to be updated to use the `http` types. See the
162+
[`reqwest` client implementations](https://github.com/ramosbugs/oauth2-rs/blob/23b952b23e6069525bc7e4c4f2c4924b8d28ce3a/src/reqwest.rs)
163+
for an example.
164+
165+
### Import device code flow and token revocation types from the root
166+
167+
Previously, certain types were exported from both the root of the crate and the `devicecode` or
168+
`revocation` modules. These modules are no longer public, and their public types are exported from
169+
the root. For example, if you were previously importing
170+
`oauth2::devicecode::DeviceAuthorizationResponse`, instead import
171+
`oauth2::DeviceAuthorizationResponse`.
172+
173+
### Add `Display` to `ErrorResponse` trait
174+
175+
To improve error messages, the
176+
[`RequestTokenError::ServerResponse`](https://docs.rs/oauth2/latest/oauth2/enum.RequestTokenError.html#variant.ServerResponse)
177+
enum variant now prints a message describing the server response using the `Display` trait. For most
178+
users (i.e., those using the default
179+
[`StandardErrorResponse`](https://docs.rs/oauth2/latest/oauth2/struct.StandardErrorResponse.html)),
180+
this does not require any code changes. However, users providing their own implementations
181+
of the `ErrorResponse` trait must now implement the `Display` trait. See
182+
`StandardErrorResponse`'s
183+
[`Display` implementation](https://github.com/ramosbugs/oauth2-rs/blob/9d8f11addf819134f15c6d7f03276adb3d32e80b/src/error.rs#L88-L108)
184+
for an example.

0 commit comments

Comments
 (0)