Automattic / Automattic/studio
Feature Request: Extend WordPress.com authentication session duration
- Dominant language
- TypeScript
- Stars
- 517
- Forks
- 95
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 172
Description
> **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`:
```typescript
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:
1. Accept `code_challenge` and `code_challenge_method` parameters on the authorization endpoint; store them alongside the short-lived auth code.
2. On the token endpoint, validate the incoming `code_verifier` by computing `BASE64URL(SHA256(code_verifier))` and comparing it to the stored challenge.
3. 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:
1. Studio opens the WordPress.com authorization URL with `response_type=code`.
2. WordPress.com redirects back with a `code` via the `wp-studio://auth` deeplink.
3. Studio sends the `code` to the proxy endpoint, which exchanges it for an access token using the `client_secret`.
4. 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.
Contributor guide
Research direction
Start by reading tools/common/lib/oauth.ts, tools/common/lib/auth-token-schema.ts, tools/common/lib/shared-config.ts, apps/studio/src/lib/deeplink/handlers/auth.ts, and apps/cli/commands/auth/login.ts. Confirm which WordPress.com authorization flow is supported and get a security-approved implementation path. Done means Studio can maintain authentication without forcing users to re-authenticate every two weeks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- authentication, cli, desktop
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100