Skip to content

Access token is never persisted — POST /oauth/v2/token fires on every background-page wake-up #552

@xuan-w

Description

@xuan-w

Access token is never persisted — POST /oauth/v2/token fires on every background-page wake-up

Wallabagger 1.23.1, Firefox 149.0.2 on Linux and macOS, self-hosted wallabag/wallabag Docker instance.

This is not about the broader auth-flow / credential-storage concern already discussed in #151 — that's waiting on upstream wallabag/wallabag#2800 and is out of scope here. The narrow issue below is purely about how often the current flow re-runs, and is independent of whatever flow eventually replaces it.

What happens

Leaving Firefox running, totally idle, I watched my Wallabag access log and saw this pattern roughly every ~30 seconds, forever:

20:36:41.552  POST /oauth/v2/token HTTP/1.1  200
20:36:41.643  GET  /api/tags.json   HTTP/1.1  200
20:37:12.545  POST /oauth/v2/token HTTP/1.1  200
20:37:12.638  GET  /api/tags.json   HTTP/1.1  200
20:37:44.026  POST /oauth/v2/token HTTP/1.1  200
20:37:44.133  GET  /api/tags.json   HTTP/1.1  200

Each POST /oauth/v2/token is a fresh grant_type=password login — even though the access token returned a few seconds earlier is still well within its expires_in window. On an always-on machine this works out to ~2,880 token requests per day per browser profile, all of them unnecessary.

This happens regardless of the "Indicate if page already saved" (AllowExistCheck) option, because boot() at wallabagger/js/background.js:193 calls await api.getTags() unconditionally on every event-page start.

Why

#getAppToken in wallabagger/js/wallabag-api.js:259-276 updates this.data.ApiToken, RefreshToken, and ExpireDate in memory and returns — but never writes them back to browser.storage.local. A grep for browser.storage.local.set in that file returns only saveParams() on line 107, which is invoked only when the user interacts with the options page. So when Firefox suspends the non-persistent MV3 background page (default ~30 s idle) the in-memory token evaporates; on the next wake-up, api.init()#load() reads storage, finds ApiToken: null (the default from defaultValues — it was never saved), and #checkToken() fires another passwordToken().

Suggested fix (1 line)

In #getAppToken, after the in-memory mutations, persist this.data:

this.data.ApiToken = data.access_token;
this.data.RefreshToken = data.refresh_token;
this.data.ExpireDate = Date.now() + data.expires_in * 1000;
this.data.isTokenExpired = this.#isTokenExpired();
await browser.storage.local.set({ wallabagdata: this.data });   // ← this line
return data;

(Also make #getAppToken async, or chain the set inside the existing .then.)

With this change, a refreshed token survives background-page suspends and gets reused until ExpireDate is actually past — one refresh per expires_in interval (3600 s by default) instead of one per ~30 s wake-up. ~100× reduction in the OAuth traffic without touching anything in the auth-flow design debate from #151.

One side note worth knowing: PRIVACY.md in this repo currently states

… refresh token, which is used for refreshing access token after it is expired … After the refresh token expires itself, the stored credentials are used for obtaining a new one.

The refresh token is stored into this.data.RefreshToken by #getAppToken, but no code path ever reads it back — every token refresh goes through the password grant. Once #2800 upstream settles and this repo picks up a proper flow, PRIVACY.md will become accurate again; in the meantime the persistence fix above at least stops the noise.

Happy to send a PR for the one-liner if it's helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions