Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update refresh.md #546

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions IdentityServer/v7/docs/content/tokens/refresh.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Refresh tokens are supported for the following flows: authorization code, hybrid
## Requesting a refresh token
You can request a refresh token by adding a scope called *offline_access* to the scope parameter list of the authorize request.

## Requesting an access token using a refresh token
#### Requesting an access token using a refresh token
To get a new access token, you send the refresh token to the token endpoint.
This will result in a new token response containing a new access token and its expiration and potentially also a new refresh token depending on the client configuration (see [rotation]({{< ref "#rotation" >}})).

Expand All @@ -26,7 +26,7 @@ POST /connect/token
refresh_token=hdh922
```

### .NET client library
#### .NET client library
On .NET you can leverage the [IdentityModel](https://identitymodel.readthedocs.io) client library to [request](https://identitymodel.readthedocs.io/en/latest/client/token.html) refresh tokens, e.g.:

```cs
Expand All @@ -47,34 +47,33 @@ var response = await client.RequestRefreshTokenAsync(new RefreshTokenRequest

The [Duende.AccessTokenManagement](https://github.com/DuendeSoftware/Duende.AccessTokenManagement/wiki) library can be used to automate refresh & access token lifetime management in ASP.NET Core.

## Refresh token security considerations
## Binding refresh tokens
brockallen marked this conversation as resolved.
Show resolved Hide resolved
Refresh tokens are a high-value target for attackers, because they typically have a much higher lifetime than access tokens.

It is recommended that a refresh token is either bound to the client via a client secret (for confidential/credentialed clients), or rotated for public clients.
However, refresh tokens for _confidential clients_ are bound to that client; they can only be used by the client they are issued to and the client is required to authenticate itself in order to do so. An attacker able to obtain a refresh token issued to a confidential client cannot use it without the client's credentials.

The following techniques can be used to reduce the attack surface of refresh tokens.
Refresh tokens issued to public clients are not bound to the client in the same way since the client cannot authenticate itself. Instead, we recommend that such refresh tokens be sender-constrained using [Proof of Possession]({{< ref "pop" >}}).

### Consent
You can further reduce the attack surface of refresh tokens using the following techniques.

#### Consent
We encourage you to request consent when a client requests a refresh token, as it not only makes the user aware of the action being taken, but also provides them with an opportunity to opt-out if they choose.

Duende IdentityServer will always ask for consent (if enabled) if the client asks for the *offline_access* scope which follows the recommendations in the OpenID Connect specification.

### Sliding expiration
#### Sliding expiration
Refresh tokens usually have a much longer lifetime than access tokens. You can reduce their exposure by adding a sliding lifetime on top of the absolute lifetime. This allows for scenarios where a refresh token can be silently used if the user is regularly using the client, but needs a fresh authorize request if the client has not been used for a certain time. In other words, they auto-expire much quicker without potentially interfering with the typical usage pattern.

You can use the *AbsoluteRefreshTokenLifetime* and *SlidingRefreshTokenLifetime* client settings to fine tune this behavior.

### Rotation
The security of refresh tokens used by *public clients* can be improved by rotating the tokens on every use, because there is a chance that a stolen token will be unusable by the attacker. If a token is exfiltrated from some storage mechanism, a network trace, or log file, but the owner of the token uses it before the attacker, then the attack fails. However, this comes at the cost of reliability and database pressure and is only necessary for public clients (see [below](#avoid-rotation)). Rotation is configured with the *RefreshTokenUsage* client setting and, beginning in IdentityServer v7.0, is disabled by default.
#### Rotation
Rotation is configured with the *RefreshTokenUsage* client setting and, beginning in IdentityServer v7.0, is by default set to *ReUse* which disables rotation.

When *RefreshTokenUsage* is configured for *OneTime* usage, rotation is enabled and refresh tokens can only be used once. When refresh tokens are used with *OneTime* usage configured, a new refresh token is included in the response along with the new access token. Each time the client application uses the refresh token, it must use the most recent refresh token. This chain of tokens will each appear as distinct token values to the client, but will have identical creation and expiration timestamps in the datastore.

Beginning in version 6.3, the configuration option *DeleteOneTimeOnlyRefreshTokensOnUse* controls what happens to refresh tokens configured for OneTime usage. If the flag is on, then refresh tokens are deleted immediately on use. If the flag is off, the token is marked as consumed instead. Prior to version 6.3, OneTime usage refresh tokens are always marked as consumed.

#### Confidential Clients Should Not Rotate Refresh Tokens {#avoid-rotation}
Confidential clients do not need one-time use refresh tokens because their tokens are bound to the authenticated client. One-time use tokens do not improve the security of confidential clients (see [OAuth 2.0 Security Best Current Practice](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-2.2.2) for more details).

Reusable refresh tokens are robust to network failures in a way that one time use tokens are not. If a one-time use refresh token is used to produce a new token, but the response containing the new refresh token is lost due to a network issue, the client application has no way to recover without the user logging in again. Reusable refresh tokens do not have this problem.
Rotating the tokens on every use [has limited security benefits](https://blog.duendesoftware.com/posts/20240405_refresh_token_reuse/) regardless of which type of client is used. And it comes with the cost of database pressure and reliability issues: Reusable refresh tokens are robust to network failures in a way that one time use tokens are not. If a one-time use refresh token is used to produce a new token, but the response containing the new refresh token is lost due to a network issue, the client application has no way to recover without the user logging in again. Reusable refresh tokens do not have this problem.

Reusable tokens may have better performance in the [persisted grants store]({{< ref "/reference/stores/persisted_grant_store">}}). One-time use refresh tokens require additional records to be written to the store whenever a token is refreshed. Using reusable refresh tokens avoids those writes.

Expand Down
Loading