Note on research process: The investigation underlying this issue was conducted with AI assistance (Claude via Claude Code). The AI was used to trace the authentication code paths, read the WordPress.com OAuth2 documentation, and reason about the available implementation options. Findings were reviewed and verified by a human before filing.
What
Extend the duration of WordPress.com login sessions in Studio so that users are not required to re-authenticate every two weeks. Ideally, sessions should persist until the user explicitly logs out.
Why
Currently, Studio uses the OAuth 2.0 Implicit Flow (response_type=token) to authenticate with WordPress.com. This flow returns an access token directly in the redirect URL fragment with a fixed expires_in of approximately two weeks. Studio stores this expiration timestamp locally and treats the token as invalid once it passes — requiring the user to log in again.
This is frustrating in practice: a user who installs Studio and logs in will silently lose their authenticated session roughly every fortnight, with no background renewal possible.
Root cause in the codebase
Two separate code paths both enforce the two-week limit:
- Desktop app (
apps/studio/src/lib/deeplink/handlers/auth.ts): stores expirationTime directly from the server's expires_in response field.
- CLI (
apps/cli/commands/auth/login.ts): ignores the server's expires_in and hardcodes DEFAULT_TOKEN_LIFETIME_MS = DAY_MS * 14 from tools/common/constants.ts.
Both paths are then subject to a local expiry check in tools/common/lib/shared-config.ts:
if ( Date.now() >= token.expirationTime ) {
return null; // forces re-authentication
}
Why a trivial local fix won't help
The two-week limit is not just a local artifact — it reflects the server-side lifetime of Implicit Flow tokens issued by WordPress.com's OAuth server. Relaxing or removing the local expiry check would still result in invalid_token API errors once the server-side token expires, which Studio already handles by forcing a logout.
Why the obvious alternative (PKCE) isn't currently available
For native/desktop apps, the standard solution to this problem is to switch from the Implicit Flow to the Authorization Code Flow with PKCE (RFC 7636). PKCE allows a public client (one that cannot safely store a client_secret) to use the Auth Code Flow securely. Auth Code Flow tokens are typically longer-lived or non-expiring, and the flow also supports refresh tokens for silent renewal.
However, PKCE is not currently documented or supported by WordPress.com's OAuth2 server (public-api.wordpress.com/oauth2/). The Authorization Code Flow is supported, but it requires a client_secret for the token exchange — which cannot be safely embedded in a distributed desktop application binary.
How
Three implementation paths are available, roughly ordered by preference:
Option A — Request PKCE support from the WordPress.com platform team (preferred)
PKCE is a well-understood, additive extension to the existing Auth Code Flow (RFC 7636). The server-side implementation is modest:
- Accept
code_challenge and code_challenge_method parameters on the authorization endpoint; store them alongside the short-lived auth code.
- On the token endpoint, validate the incoming
code_verifier by computing BASE64URL(SHA256(code_verifier)) and comparing it to the stored challenge.
- Optionally enforce PKCE for public clients (RFC 9700 best practice).
This is roughly 1–3 days of engineering work for someone familiar with their OAuth codebase, plus review. It is not an architectural change — it slots into the existing Authorization Code Flow infrastructure.
Impact for Studio: Studio's tools/common/lib/oauth.ts would switch from response_type=token to response_type=code, generate a PKCE code verifier/challenge per login, and exchange the returned code for a token via POST /oauth2/token. No client_secret needed. If refresh tokens are also issued, tools/common/lib/auth-token-schema.ts and tools/common/lib/shared-config.ts would be updated to store and silently refresh them.
This benefits not just Studio but all public OAuth clients building on the WordPress.com platform.
Option B — Proxy service for Authorization Code Flow (secure, requires new infrastructure)
A small Studio-owned backend endpoint holds the client_secret and handles the token exchange server-to-server. The flow:
- Studio opens the WordPress.com authorization URL with
response_type=code.
- WordPress.com redirects back with a
code via the wp-studio://auth deeplink.
- Studio sends the
code to the proxy endpoint, which exchanges it for an access token using the client_secret.
- The proxy returns the token to Studio.
This keeps the secret off the client binary entirely and should yield long-lived (or non-expiring) Authorization Code tokens. The documented Auth Code token response notably omits expires_in, suggesting tokens from this flow may not expire.
Trade-off: Requires operating and maintaining a backend service. Adds a network dependency to the login flow. Feels somewhat silly when A8C also runs the endpoint we're authenticating to.
Option C — Embed client_secret in the desktop app (pragmatic, lower security bar)
Many open-source desktop apps (VS Code, GitHub Desktop) embed OAuth secrets directly in their binaries, accepting that the secret is technically extractable. Studio already embeds CLIENT_ID = '95109' in tools/common/constants.ts.
Adding CLIENT_SECRET and switching to response_type=code with a direct token exchange from the desktop process would be a self-contained change with no new infrastructure. If Auth Code tokens are long-lived or non-expiring, this immediately solves the user-facing problem.
Trade-off: The secret is extractable from the binary. For an open-source app where the source is already public, the marginal risk is low — but it is not best practice and would need a deliberate security sign-off.
Summary
| Option |
Infrastructure |
Security |
Complexity |
Solves re-auth? |
| A — PKCE (platform change) |
None |
Best |
Low (server-side) |
Yes, with refresh tokens |
| B — Proxy backend |
New service required |
Best |
Medium |
Yes |
| C — Embedded secret |
None |
Acceptable for OSS desktop |
Low |
Yes (if Auth Code tokens are long-lived) |
The recommended first step is to open a conversation with the WordPress.com platform team about Option A, since it is the most correct solution and benefits the broader developer ecosystem. Options B and C are available as fallbacks if PKCE support is not forthcoming.
What
Extend the duration of WordPress.com login sessions in Studio so that users are not required to re-authenticate every two weeks. Ideally, sessions should persist until the user explicitly logs out.
Why
Currently, Studio uses the OAuth 2.0 Implicit Flow (
response_type=token) to authenticate with WordPress.com. This flow returns an access token directly in the redirect URL fragment with a fixedexpires_inof approximately two weeks. Studio stores this expiration timestamp locally and treats the token as invalid once it passes — requiring the user to log in again.This is frustrating in practice: a user who installs Studio and logs in will silently lose their authenticated session roughly every fortnight, with no background renewal possible.
Root cause in the codebase
Two separate code paths both enforce the two-week limit:
apps/studio/src/lib/deeplink/handlers/auth.ts): storesexpirationTimedirectly from the server'sexpires_inresponse field.apps/cli/commands/auth/login.ts): ignores the server'sexpires_inand hardcodesDEFAULT_TOKEN_LIFETIME_MS = DAY_MS * 14fromtools/common/constants.ts.Both paths are then subject to a local expiry check in
tools/common/lib/shared-config.ts:Why a trivial local fix won't help
The two-week limit is not just a local artifact — it reflects the server-side lifetime of Implicit Flow tokens issued by WordPress.com's OAuth server. Relaxing or removing the local expiry check would still result in
invalid_tokenAPI errors once the server-side token expires, which Studio already handles by forcing a logout.Why the obvious alternative (PKCE) isn't currently available
For native/desktop apps, the standard solution to this problem is to switch from the Implicit Flow to the Authorization Code Flow with PKCE (RFC 7636). PKCE allows a public client (one that cannot safely store a
client_secret) to use the Auth Code Flow securely. Auth Code Flow tokens are typically longer-lived or non-expiring, and the flow also supports refresh tokens for silent renewal.However, PKCE is not currently documented or supported by WordPress.com's OAuth2 server (
public-api.wordpress.com/oauth2/). The Authorization Code Flow is supported, but it requires aclient_secretfor the token exchange — which cannot be safely embedded in a distributed desktop application binary.How
Three implementation paths are available, roughly ordered by preference:
Option A — Request PKCE support from the WordPress.com platform team (preferred)
PKCE is a well-understood, additive extension to the existing Auth Code Flow (RFC 7636). The server-side implementation is modest:
code_challengeandcode_challenge_methodparameters on the authorization endpoint; store them alongside the short-lived auth code.code_verifierby computingBASE64URL(SHA256(code_verifier))and comparing it to the stored challenge.This is roughly 1–3 days of engineering work for someone familiar with their OAuth codebase, plus review. It is not an architectural change — it slots into the existing Authorization Code Flow infrastructure.
Impact for Studio: Studio's
tools/common/lib/oauth.tswould switch fromresponse_type=tokentoresponse_type=code, generate a PKCE code verifier/challenge per login, and exchange the returnedcodefor a token viaPOST /oauth2/token. Noclient_secretneeded. If refresh tokens are also issued,tools/common/lib/auth-token-schema.tsandtools/common/lib/shared-config.tswould be updated to store and silently refresh them.This benefits not just Studio but all public OAuth clients building on the WordPress.com platform.
Option B — Proxy service for Authorization Code Flow (secure, requires new infrastructure)
A small Studio-owned backend endpoint holds the
client_secretand handles the token exchange server-to-server. The flow:response_type=code.codevia thewp-studio://authdeeplink.codeto the proxy endpoint, which exchanges it for an access token using theclient_secret.This keeps the secret off the client binary entirely and should yield long-lived (or non-expiring) Authorization Code tokens. The documented Auth Code token response notably omits
expires_in, suggesting tokens from this flow may not expire.Trade-off: Requires operating and maintaining a backend service. Adds a network dependency to the login flow. Feels somewhat silly when A8C also runs the endpoint we're authenticating to.
Option C — Embed
client_secretin the desktop app (pragmatic, lower security bar)Many open-source desktop apps (VS Code, GitHub Desktop) embed OAuth secrets directly in their binaries, accepting that the secret is technically extractable. Studio already embeds
CLIENT_ID = '95109'intools/common/constants.ts.Adding
CLIENT_SECRETand switching toresponse_type=codewith a direct token exchange from the desktop process would be a self-contained change with no new infrastructure. If Auth Code tokens are long-lived or non-expiring, this immediately solves the user-facing problem.Trade-off: The secret is extractable from the binary. For an open-source app where the source is already public, the marginal risk is low — but it is not best practice and would need a deliberate security sign-off.
Summary
The recommended first step is to open a conversation with the WordPress.com platform team about Option A, since it is the most correct solution and benefits the broader developer ecosystem. Options B and C are available as fallbacks if PKCE support is not forthcoming.