Interface IRefreshTokenService
- Namespace
- Fdyay.IdentityServer.Services.Tokens
- Assembly
- Fdyay.IdentityServer.Core.dll
Creates, validates, and rotates refresh tokens according to client policy.
public interface IRefreshTokenService
Methods
CreateRefreshTokenAsync(ClaimsPrincipal, Client, IEnumerable<string>, bool, string?, IEnumerable<string>?, string?, Token?, string?, string?, CancellationToken)
Issues a refresh token, bound to a DPoP key when the client proved it holds one.
Task<string> CreateRefreshTokenAsync(ClaimsPrincipal subject, Client client, IEnumerable<string> scopes, bool isOpenId, string? dpopKeyThumbprint = null, IEnumerable<string>? authorizedResourceIndicators = null, string? sessionId = null, Token? accessToken = null, string? requestedResourceIndicator = null, string? description = null, CancellationToken cancellationToken = default)
Parameters
subjectClaimsPrincipalThe user the token is issued for; its subject id and every claim are stored with it.
clientClientThe client the token is issued to; only that client may redeem it, and its lifetime policy applies.
scopesIEnumerable<string>The scopes granted, which every later token request on this refresh token is held to.
isOpenIdboolWhether the grant included
openid, so a refresh can issue an identity token.dpopKeyThumbprintstringThe JWK thumbprint of the DPoP key the client proved, binding the token to it; null when unbound.
authorizedResourceIndicatorsIEnumerable<string>The APIs the authorization named by resource indicator (RFC 8707), which every later token request on this refresh token is held to. Null or empty for a grant whose authorization named none.
sessionIdstringThe user's session id (
sid), so ending the session can find and revoke the token.accessTokenTokenThe access token issued alongside, stored with the refresh token for the resource it was issued for.
requestedResourceIndicatorstringThe resource indicator the access token was requested for, if the request named one.
descriptionstringWhat the user or client called this grant, shown in the user's grant list.
cancellationTokenCancellationTokenCancels the store's write.
Returns
RotateAsync(string, RefreshToken, Client, CancellationToken)
Rotates the refresh token per the client's usage policy, returning the handle to return.
Task<string> RotateAsync(string handle, RefreshToken token, Client client, CancellationToken cancellationToken = default)
Parameters
handlestringtokenRefreshTokenclientClientcancellationTokenCancellationToken
Returns
Remarks
Under OneTimeOnly the predecessor is consumed rather than deleted, so a later presentation of it
is recognisable as a replay — unless PersistentGrantOptions.DeleteOneTimeOnlyRefreshTokensOnUse is on. Under ReUse nothing is consumed and the same handle comes back: a
client on that policy is meant to present it repeatedly, and treating the second use as a replay would
revoke a working session. Under either, a Sliding token's lifetime is extended from its use, never past
AbsoluteRefreshTokenLifetime when that is above zero — the reused one in place, under its handle (0.174.0).
A token TryRedeemAsync(string, RefreshToken, Client, CancellationToken) has taken is already consumed, or deleted, and is not consumed again.
TryRedeemAsync(string, RefreshToken, Client, CancellationToken)
Takes a validated refresh token for the one request about to be answered with it: false when another request took it first, and the request is then refused as a replay would be.
Task<bool> TryRedeemAsync(string handle, RefreshToken token, Client client, CancellationToken cancellationToken = default)
Parameters
handlestringThe handle the client presented.
tokenRefreshTokenThe token ValidateRefreshTokenAsync(string, Client, CancellationToken) returned for it.
clientClientThe client redeeming it.
cancellationTokenCancellationTokenCancels the store's write.
Returns
Remarks
Ours (security), 0.176.0. Validating a token reads it; until this, rotating it consumed it in a second step, so two
requests presenting one OneTimeOnly token at the same moment both read it unconsumed and were both answered
with a successor — two live lineages from one token, and no replay detected. The token endpoint asks this once
every check on the request has passed and before any token is issued. Duende reads and consumes in two steps, as
this did. This default answers true, so a refresh token service of a host's own behaves as before.
ValidateRefreshTokenAsync(string, Client, CancellationToken)
The token behind a handle, or null if it cannot be used.
Task<RefreshToken?> ValidateRefreshTokenAsync(string handle, Client client, CancellationToken cancellationToken = default)
Parameters
handlestringclientClientcancellationTokenCancellationToken
Returns
Remarks
Null covers absent, expired, issued to another client, and replayed. The caller answers all four the
same way — invalid_grant — and deliberately learns no more than that: which one it was tells an
attacker whether a handle was ever real, and the client cannot act differently on any of them.
A replay is not only refused here; it revokes the lineage it belongs to. A consumed token presented within
PersistentGrantOptions.ConsumedRefreshTokenGracePeriod of its consumption is not a replay — off by default.