Upgrade — DPoP Nonce Auto-Management#
The framework now internally manages the DPoP nonce lifecycle. Previously, callers were responsible for extracting the DPoP-Nonce header from server responses and passing it back on subsequent calls via the dpopNonce parameter. This is no longer necessary — the framework tracks and applies the latest nonce automatically through its internal URL session / interceptor.
This migration applies primarily at the Driver Layer, where HaapiTokenManager is exposed directly. Higher layers (SDK, UI) compose the Driver Layer and never required caller-managed nonces.
For the concept and rationale, see DPoP and Nonces .
What Changed#
| Before | After |
|---|---|
Caller extracts DPoP-Nonce from each response | Framework extracts the header internally |
Caller passes dpopNonce to the next HaapiTokenManager call | Framework retrieves the latest nonce from its session |
Caller is responsible for re-trying 401 + use_dpop_nonce errors | Framework handles use_dpop_nonce as a retryable error transparently |
Affected Surfaces#
- iOS Driver Layer —
HaapiTokenManagermethods that previously accepteddpopNonce:parameters. - Android Driver Layer —
HaapiTokenManager.getDPoPTokensFor(...)(when used directly without the OkHttp interceptor) andDPoPNonceStackdirect API. - SDK and UI Layers — no caller-facing change; the Driver-Layer improvement flows through transitively.
Migration#
Deprecated methods#
| Deprecated method | Replacement |
|---|---|
getHaapiTokenAsync(forceFresh:dpopNonce:) | getHaapiTokenAsync(forceFresh:) |
getHaapiToken(forceFresh:dpopNonce:completionHandler:) (Result-based) | getHaapiTokenAsync(forceFresh:) |
Objective-C variants remain deprecated as part of the broader Objective-C support discontinuation.
Before (deprecated API)#
var lastNonce: String? = nil
let tokenInfo = try await haapiTokenManager.getHaapiTokenAsync(
forceFresh: false,
dpopNonce: lastNonce
)
// Extract nonce for next call
lastNonce = tokenInfo.dpopNonceAfter#
let tokenInfo = try await haapiTokenManager.getHaapiTokenAsync(
forceFresh: false
)
// tokenInfo.dpopNonce remains readable, but you don't need to pass it backBackward compatibility#
- Existing code that passes
dpopNoncecontinues to compile and work. The caller-provided nonce takes precedence over the internally managed one. - The
dpopNonceproperty onDpopAccessTokenInforemains available for reading. - No changes are required for users of
HaapiClient, which already managed nonces internally.
What’s automatic now#
When you use the OkHttp interceptor (addHaapiInterceptor), nonce management has always been automatic — this change doesn’t affect that path.
When you call HaapiTokenManager.getDPoPTokensFor(...) directly without the interceptor, the returned tokens.dpopNonce is now populated from the internally held nonce. You no longer need to remember the previous response’s DPoP-Nonce header and feed it back into the next call.
val tokens = haapiTokenManager.getDPoPTokensFor(method, uri)
// tokens.dpopNonce is already populated from the internally held nonceDeprecated DPoPNonceStack direct methods#
DPoPNonceStack now implements the Storage interface. The previous direct methods are deprecated in favor of the Storage methods keyed by DPoPNonceStack.DPOP_NONCE_KEY:
| Deprecated method | Replacement |
|---|---|
get() | get(DPoPNonceStack.DPOP_NONCE_KEY) |
set(value) | set(value, DPoPNonceStack.DPOP_NONCE_KEY) |
clear() | delete(DPoPNonceStack.DPOP_NONCE_KEY) |
pop() | get(DPoPNonceStack.DPOP_NONCE_KEY) |
push(value) | set(value, DPoPNonceStack.DPOP_NONCE_KEY) |
peek() | get(DPoPNonceStack.DPOP_NONCE_KEY) |
The deprecated methods continue to work in the current version but will be removed along with the public DPoPNonceStack class in the next major version. If you were not calling DPoPNonceStack directly (the typical case), no migration is needed.
Verification#
After migration:
- Outgoing HAAPI requests still carry the correct
DPoP-Nonceheader — inspect a request fromCharles/Proxyman/mitmproxyto confirm. - 401 +
use_dpop_nonceresponses no longer surface as errors to your code; the framework refreshes the nonce internally and retries. - The
tokens.dpopNonce/tokenInfo.dpopNonceproperty remains readable if you have downstream code that needs it.
On iOS, the caller-provided nonce takes precedence over the internally managed one for backward compatibility. If you have stale code that still passes a dpopNonce parameter, the framework respects it — even when its internal value is fresher. Remove the caller-provided nonce explicitly during migration; don’t rely on the framework to override.