Skip to content

Caching and expiry in DPoP token provider - #30

Merged
langsamu merged 3 commits into
mainfrom
token-cache
Aug 24, 2026
Merged

Caching and expiry in DPoP token provider#30
langsamu merged 3 commits into
mainfrom
token-cache

Conversation

@langsamu

@langsamu langsamu commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

This change adds initial token caching functionality to the DPoP token provider: Access tokens will be cached locally until they expire.

The resulting current functionality is that the second fetch to a given URI will only issue the unauthenticated original request and the upgraded request. UI, disco, dynreg are bypassed.

All requests with the same request URI get the same cached token. DPoP proofs are still calculated anew per request.

In a future change I will expose the cache in the provider constructor so it can be supplied and managed by the caller. But here it's private to the provider instance.

Later I will also want to add some headroom to the expiry calculation. A few seconds to account for the time it takes for requests to travel.

Also will need to think more carefully about token responses that lack expiry. This is likely to involve changes to the reactive authentication algorithm itself rather than this provider.

@langsamu langsamu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explanation

Comment thread src/DPoPTokenProvider.ts
import type { TokenProvider } from "./TokenProvider.js"
import type { GetIssuerCallback } from "./GetIssuerCallback.js"

type CacheEntry = { created: number, tokenResult: oauth.TokenEndpointResponse, dpopKey: CryptoKeyPair }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Locally represents what we cache.
Will move out of here once cache also moves out.

Comment thread src/DPoPTokenProvider.ts
return new Request(request, {headers})
}

private async obtainToken(request: Request): Promise<CacheEntry> {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All upgrade logic except caching is unchanged in this extracted method.

Comment thread src/DPoPTokenProvider.ts
headers.set("Authorization", ["DPoP", tokenResult.access_token].join(" "))

return new Request(request, {headers})
return {created: Date.now(), tokenResult, dpopKey}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the newly extracted method we don't actually upgrade the request.
That has moved to 32-37 above.

@langsamu
langsamu marked this pull request as ready for review August 19, 2026 15:13
@langsamu langsamu mentioned this pull request Aug 20, 2026
Comment thread src/DPoPTokenProvider.ts

async upgrade(request: Request): Promise<Request> {
let tokenData = this.#cache.get(request.url)
if (tokenData === undefined || isExpired(tokenData)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (tokenData === undefined || isExpired(tokenData)) {
// TODO: Support proactive refreshing as well
if (tokenData === undefined || isExpired(tokenData)) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/DPoPTokenProvider.ts
}

async upgrade(request: Request): Promise<Request> {
let tokenData = this.#cache.get(request.url)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The request url is not necessarily the right cache key. I'd suggest a callback to map from request to key.

For instance, we will often want this token to be shareable across all URLs in one or multiple storages.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Co-authored-by: Jesse Wright <63333554+jeswr@users.noreply.github.com>
@langsamu
langsamu merged commit 8a23152 into main Aug 24, 2026
5 checks passed
@langsamu
langsamu deleted the token-cache branch August 24, 2026 07:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants