Upgrade — DPoP Nonce Auto-Management#

The framework now internally manages the DPoP nonce lifecycle for its requests to the authorization server. 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 does not cover a resource server you call yourself with a DPoP-bound access token. That server has its own nonce space, which the framework neither sees nor tracks; handling it stays with the application. See OAuthTokenManager.

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#

BeforeAfter
Caller extracts DPoP-Nonce from each responseFramework extracts the header internally
Caller passes dpopNonce to the next HaapiTokenManager callFramework retrieves the latest nonce from its session
Caller is responsible for re-trying 401 + use_dpop_nonce errorsFramework handles use_dpop_nonce as a retryable error transparently

Affected Surfaces#

  • iOS Driver Layer — HaapiTokenManager methods that previously accepted dpopNonce: parameters.
  • Android Driver Layer — HaapiTokenManager.getDPoPTokensFor(...) (when used directly without the OkHttp interceptor) and DPoPNonceStack direct API.
  • SDK and UI Layers — no caller-facing change; the Driver-Layer improvement flows through transitively.

Migration#

Deprecated methods#

Deprecated methodReplacement
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.dpopNonce

After#

let tokenInfo = try await haapiTokenManager.getHaapiTokenAsync(
    forceFresh: false
)
// tokenInfo.dpopNonce remains readable, but you don't need to pass it back

Backward compatibility#

  • Existing code that passes dpopNonce continues to compile and work. The caller-provided nonce takes precedence over the internally managed one.
  • The dpopNonce property on DpopAccessTokenInfo remains 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 nonce

Deprecated 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 methodReplacement
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 a future 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-Nonce header — inspect a request from Charles / Proxyman / mitmproxy to confirm.
  • 401 + use_dpop_nonce responses no longer surface as errors to your code; the framework refreshes the nonce internally and retries.
  • The tokens.dpopNonce / tokenInfo.dpopNonce property 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.

Was this helpful?