Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>0.1.0-rc.3</Version>
<Version>0.1.0</Version>
<NoWarn>$(NoWarn);1591</NoWarn>

<Authors>CodeBeam</Authors>
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ UltimateAuth is an open-source auth framework with platform-level capabilities t
| Phase | Version | Scope | Status | Release Date |
| ----------------------- | ------------- | ----------------------------------------- | -------------- | ------------ |
| First Preview | 0.1.0-preview | "Stable" Preview Core | βœ… Completed | 07.04.2026 |
| First Release* | 0.1.0 | Documented & Quality Tested | βœ… Completed | 12.10.2026 |
| First Release* | 0.1.0 | Documented & Quality Tested (Client Public API Locked) | βœ… Completed | 12.10.2026 |
| Product Expansion | 0.2.0 | Full Auth Modes | 🟑 In Progress | Q4 2026 |
| Security Expansion | 0.3.0 | MFA, Reauth, Rate Limiting | 🟑 In Progress | Q4 2026 |
| Infrastructure Expansion| 0.4.0 | Redis, Distributed Cache, Password Hasher | πŸ”œ Planned | Q1 2027 |
Expand All @@ -43,7 +43,7 @@ UltimateAuth is an open-source auth framework with platform-level capabilities t
| Ecosystem Expansion | 0.8.0 | Migration tools | πŸ”œ Planned | Q2 2027 |
| v1.0 | 1.0.0 | Locked API, align with .NET 11 | πŸ”œ Planned | Q2 2027 |

*v 0.1.0 already provides a skeleton of multi tenancy, MFA, reauth etc. Expansion releases will enhance these areas.
*v 0.1.0 already provides a skeleton and extensibility points of multi tenancy, MFA, reauth etc. Expansion releases will enhance these areas.

> The project roadmap is actively maintained as a GitHub issue:

Expand Down
227 changes: 110 additions & 117 deletions docs/content/auth-flows/refresh-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,178 +4,171 @@ order: 3
group: auth-flows
---

# Refresh Flow

# πŸ”„ Refresh Flow
The refresh flow in UltimateAuth is not a single fixed operation.
Refresh is how an existing authentication context continues without asking the user to authenticate again. In UltimateAuth, it is not synonymous with issuing a new access token: the operation depends on the active authentication mode and on whether the context is represented by a server-side session, refresh credentials, or both.

πŸ‘‰ It is a **mode-dependent continuation strategy**.
A successful refresh preserves the authenticated identity and its continuity. It does **not** prove the user's credentials again, create a new login, or necessarily extend every lifetime associated with the authentication.

## 🧠 What is Refresh?
In traditional systems:
## The model: continuity, not a second login

- Refresh = get a new access token
UltimateAuth distinguishes three related operations:

In UltimateAuth:
- **Authentication** establishes a new authenticated context after the required proof is accepted.
- **Refresh** attempts to continue an already established context, subject to its current validity and security policies.
- **Reauthentication** establishes trust again when continuation is unavailable or no longer permitted.

πŸ‘‰ Refresh continues an existing authentication state
This distinction matters for both security and client behavior. A refresh request can fail because a session expired, a refresh credential is invalid or consumed, the device context does not match, or the underlying chain is no longer usable. The client should treat an unsuccessful refresh as a need to authenticate again, not as an invitation to retry the same credential indefinitely.

> Refresh is not re-authentication
> β†’ It is session continuation
## Mode-dependent strategies

## πŸ”€ Two Refresh Strategies
UltimateAuth supports two fundamentally different refresh behaviors:
UltimateAuth implements refresh through two building blocks: **session touch** and **refresh-token rotation**. A mode selects how they are combined.

### πŸ”Ή Session Touch (Stateful)
Used in **PureOpaque mode**
| Authentication mode | Required refresh input | Strategy | Result |
| --- | --- | --- | --- |
| `PureOpaque` | Session ID | Validate session; touch when policy requires | Continue the existing session; no new access or refresh token |
| `Hybrid` | Session ID **and** refresh token | Validate and conditionally touch session; rotate refresh token | Continue session and issue replacement tokens |
| `SemiHybrid` | Session ID **and** refresh token | Validate session; rotate refresh token | Issue replacement tokens without a session touch |
| `PureJwt` | Refresh token | Validate and rotate refresh token | Issue replacement tokens; no session touch in the flow |

- No tokens involved
- No new session created
- Existing session is extended
**Current support status:** `PureOpaque` and `Hybrid` are the supported modes for the initial release. The `SemiHybrid` and `PureJwt` branches are present in the refresh implementation but should not be presented as completed, generally supported configurations in v0.1.0.

```
Session β†’ Validate β†’ Touch β†’ Continue
The mode is resolved from the effective authentication context on the server. Clients do not select a refresh algorithm independently.

## Session touch

A session touch is a controlled update to existing server-side authentication state. It does not issue a replacement credential or create a new session.

```text
Session ID
β†’ Validate session, chain and device context
β†’ Recheck active state and chain/session relationship
β†’ Touch if policy requires
β†’ Continue with the same session ID
```

πŸ‘‰ This updates activity without changing identity
In `PureOpaque`, the refresh flow requires a session ID. The session validator checks the session against the current tenant, device context and session options. The touch service then reloads the session and chain from the store and checks that they are still active and consistent, including the chain's active session, tenant, user and chain identifiers.

### πŸ”Ή Token Rotation (Stateless / Hybrid)
Used in:
Touch behavior is governed by the effective session options:

- Hybrid
- SemiHybrid
- PureJwt
- `TouchInterval` avoids writing activity state on every request. If the interval has not elapsed, the operation may succeed as **NoOp**.
- `TouchMode` is `IfNeeded` by default; `Force` requests a touch even when the normal interval has not elapsed.
- `SlidingExpiration` allows a touch to extend the session expiration using the configured `Lifetime`.
- An absolute chain expiration remains an upper bound; sliding expiration cannot extend the session beyond it.
- `IdleTimeout` participates in chain-state validation. An idle or otherwise inactive chain cannot be revived merely by touching it.

- Refresh token is validated
- Old token is revoked
- New tokens are issued
A successful **NoOp** is not a failed refresh: the existing state was valid, but no persistence update was necessary. Likewise, a touch is not an unconditional promise to extend the session's expiry; that depends on policy and the absolute limit.

## Refresh-token rotation

Rotation replaces a valid refresh credential while preserving its association with the existing authentication context.

```text
Refresh token
β†’ Validate token, tenant, device and expected session (if supplied)
β†’ Resolve user, session and chain
β†’ Prepare replacement refresh/access tokens
β†’ Atomically consume old refresh token and store replacement
β†’ Return new credentials
```
RefreshToken β†’ Validate β†’ Revoke β†’ Issue New Tokens
```

πŸ‘‰ This ensures forward security
The rotation service first rejects modes or configurations that do not permit refresh-token issuance (`PureOpaque` or `IssueRefresh = false`). It then validates the presented credential and its context. The replacement refresh token and access token are prepared before the existing token is consumed. Persistence uses the refresh-token store's execution boundary and `TryConsumeAsync`, so competing requests cannot both successfully consume the same old token.

Rotation preserves the validated user, session and chain association; it is **not** a new authentication or a new session. Claims for the replacement access token are obtained through the configured claims provider.

<br>
### Reuse and concurrent requests

## βš–οΈ Mode-Based Behavior
Refresh behavior is determined by the authentication mode:
A consumed refresh token cannot be used again to obtain another replacement. UltimateAuth distinguishes two situations:

| Mode | Behavior |
|-------------|-----------------------|
| PureOpaque | Session Touch |
| Hybrid | Rotation + Touch |
| SemiHybrid | Rotation |
| PureJwt | Rotation |
- **Likely concurrent request:** if a consumed token has a recorded replacement and the second request falls within `RefreshTokenConcurrentRequestWindow`, the second rotation is rejected without treating it as a confirmed replay.
- **Replay outside that window:** the rotation service revokes the associated refresh-token chain, or tokens associated with the session when a chain identifier is unavailable.

πŸ‘‰ UltimateAuth automatically selects the correct strategy.
This distinction reduces destructive responses to ordinary client-side concurrency while retaining a response to suspicious reuse. The concurrency window is **not** a grace period in which the old token may be exchanged again: the second request still fails.

<br>
The rotation implementation shown here revokes refresh-token state. Do not interpret that as an unconditional guarantee that every related server-side session is also revoked; session invalidation is a separate concern.

## πŸ”„ Step-by-Step Execution
## How each mode executes

### 1️⃣ Input Resolution
The system resolves:
### PureOpaque: session continuation

- SessionId (if present)
- RefreshToken (if present)
- Device context
`PureOpaque` has no refresh-token rotation step. The endpoint resolves the existing session from the authentication flow context, validates it and performs a conditional touch. It returns `Touched` when state was updated or `NoOp` when the valid session required no update.

### 2️⃣ Mode-Based Branching
The system determines the refresh strategy:
No access token or refresh token is issued. The existing session ID remains the credential representing the continued context.

- Session-based β†’ Touch
- Token-based β†’ Rotation
- Hybrid β†’ Both
### Hybrid: session and token continuity

### 3️⃣ Session Validation
If session is involved:
`Hybrid` requires **both** a valid session ID and a refresh token. The flow:

- Session is validated
- Device binding is checked
- Expiration is evaluated
1. Validates the session and device context.
2. Performs the session touch operation according to policy.
3. Validates and rotates the refresh token, requiring it to match the expected session.
4. Returns the existing session ID together with replacement access and refresh tokens.

### 4️⃣ Token Validation (if applicable)
If refresh token is used:
The order is important: **session touch happens before token rotation** in the current implementation. These operations are not presented as one cross-store transaction. If rotation subsequently fails, the refresh response fails, although a preceding session touch may already have been persisted. This should not be described as an all-or-nothing operation.

- Token is validated
- Session and chain are verified
- Device consistency is checked
### SemiHybrid and PureJwt: implementation branches

### 5️⃣ Security Checks
The system enforces:
The current service includes branches for two additional modes:

- Token reuse detection
- Session validity
- Chain integrity
- `SemiHybrid` requires a session ID and refresh token, validates the session and rotates the token, but does not touch the session.
- `PureJwt` uses the refresh token for rotation without a session touch step in `RefreshFlowService`.

πŸ‘‰ If validation fails β†’ reauthentication required
These describe the existing code paths, **not a promise of v0.1.0 production support**. Configuration validation and supported-mode documentation remain authoritative for which modes applications can enable.

### 6️⃣ Execution
Depending on the strategy:
## Request resolution and response delivery

#### Session Touch
- Updates `LastSeenAt`
- Applies sliding expiration
- No new tokens issued
The refresh endpoint uses the server's `AuthFlowContext`. It obtains the session ID from `flow.Session`, the device from `flow.Device`, and the refresh token through `RefreshTokenResolver`.

#### Token Rotation
- Revokes old refresh token
- Issues new access token
- Issues new refresh token
For refresh-token input, the resolver checks:

#### Hybrid Mode
- Validates session
- Rotates tokens
- Updates session activity
1. The explicit `X-Refresh-Token` header, if present.
2. The configured refresh cookie, when cookie delivery is active.
3. The configured refresh header, when header delivery is active.

### 7️⃣ Response Generation
The response may include:
The explicit header takes precedence. Header values may be supplied with a `Bearer ` prefix, which the resolver strips.

- SessionId
- Access token
- Refresh token
On success, the endpoint chooses a **primary credential** according to the refresh response policy, rather than automatically writing every credential returned by the flow. The refresh token is written separately when permitted by the response policy and when a replacement exists. Actual cookie/header placement is determined by the effective response configuration and client profile.

πŸ‘‰ Output depends on mode and client
The HTTP endpoint returns **`204 No Content`** on successful refresh and **`401 Unauthorized`** when continuation fails. Clients should obtain credentials from the configured response delivery mechanism rather than expecting a JSON token payload. When `Diagnostics.EnableRefreshDetails` is enabled, the endpoint also writes the refresh outcome through its diagnostic response writer.

<br>
> Response delivery customization is not fully independent in v0.1.0: mode templates and client-profile adaptation constrain the effective behavior. Do not assume every configured credential-delivery override is honored.

## πŸ” Security Model
The refresh flow includes strong protections:
## Outcomes and client expectations

### πŸ” Token Reuse Detection
If a refresh token is reused:
| Outcome | Meaning | Client interpretation |
| --- | --- | --- |
| `NoOp` | Existing session is valid; no touch was necessary | Continue normally |
| `Touched` | Existing session activity was updated | Continue normally |
| `Rotated` | Refresh token was consumed and replacement credentials issued | Replace stored credentials with the new values |
| `ReauthRequired` | The flow could not safely continue | Stop using the failed refresh credential and require authentication |

- Chain may be revoked
- Session may be revoked
`RefreshFlowResult` also recognizes `Success` as a successful outcome, although the mode-specific branches described above return `NoOp`, `Touched`, `Rotated` or `ReauthRequired`.

πŸ‘‰ This prevents replay attacks
Clients should serialize refresh attempts where practical. Parallel refresh requests using the same token do not produce multiple valid replacements, even when they fall within the concurrency window.

### πŸ”— Session Binding
- Tokens are bound to session
- Session is bound to device
## Security boundaries

πŸ‘‰ Prevents token misuse across devices
Refresh continues existing trust only while the relevant state remains valid:

### 🧬 Chain Integrity
- Refresh operates within a chain
- Cross-device usage is rejected
- **Session and chain checks:** session-based paths validate active state, tenant, user, chain and session relationships.
- **Device context:** session and refresh-token validation receive the current device context; mismatches can prevent continuation.
- **Single-use refresh tokens:** successful rotation consumes the old credential and persists a replacement.
- **Replay response:** suspicious reuse can revoke refresh-token state associated with the chain or session.
- **Expiration:** idle, session, chain and refresh-token validity rules can end continuation. Refresh does not bypass those limits.
- **Issuance policy:** `IssueRefresh = false` prevents rotation before the old token is consumed.

<br>
These controls complement one another. A refresh token should be treated as a sensitive credential, not as a harmless request to generate another access token.

## 🧠 Mental Model
If you remember one thing:
## Summary

πŸ‘‰ Refresh = continuation
πŸ‘‰ Not new authentication
The useful distinction is not simply *stateful versus stateless*. It is **what must remain valid for authentication to continue**:

## πŸ“Œ Key Takeaways
- `PureOpaque` continues an existing server-side session, with conditional activity updates.
- `Hybrid` requires both session validity and successful refresh-token rotation.
- Other rotation branches exist, but are not part of the completed v0.1.0 supported-mode set.

- Refresh behavior depends on auth mode
- Stateful systems use session touch
- Stateless systems use token rotation
- Hybrid systems combine both
- Security is enforced at every step
Refresh does not authenticate a user again. It extends the usefulness of an already established authentication context only within the boundaries of the selected mode and configured security policies.

## ➑️ Next Step
## Next step

Continue to **Logout Flow**
Continue to [Logout Flow](./logout-flow).
Loading