feat(oauth): allow overriding providerId and extraParameters in built-in OAuthService providers - #80
Open
smorin wants to merge 3 commits into
Open
feat(oauth): allow overriding providerId and extraParameters in built-in OAuthService providers#80smorin wants to merge 3 commits into
smorin wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR extends the built-in OAuthService provider factory options to support multi-account / multi-workspace scenarios by allowing callers to (1) override the internal token-storage namespace (providerId) and (2) add/override authorization-request query parameters (extraParameters) across all seven built-in providers.
Changes:
- Adds
providerId?: stringandextraParameters?: Record<string, string>to the built-in provider option types and wires them into each provider’sOAuth.PKCEClientconstruction / authorize request flow. - Merges
extraParametersover built-in defaults where defaults exist (Linearactor=user, Slackuser_scope=...) while preserving current behavior when omitted. - Adds unit coverage for providerId/default behavior and extraParameters merging, and updates docs with a multi-workspace Linear example plus updated option tables.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| test/raycast-api-mock.ts | Expands the Raycast API mock to include OAuth.RedirectMethod and a PKCEClient that retains constructor options for assertions. |
| test/OAuthService.test.ts | Adds tests verifying default/overridden providerId and extraParameters behavior across all built-in providers. |
| src/oauth/types.ts | Extends provider option types to include providerId and extraParameters. |
| src/oauth/OAuthService.ts | Threads providerId into the internally constructed OAuth.PKCEClient and passes/merges extraParameters into the authorize request. |
| docs/utils-reference/oauth/OAuthService.md | Documents multi-login usage and adds providerId/extraParameters to provider option tables. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
I'm building multi-workspace support for the Linear extension — the long-standing request in raycast/extensions#24297 ("Unable to switch between organizations"): people who use Linear across two companies currently have to log out and back in to switch. A companion PR to
extensions/linearin the extensions repo is in progress that adds the full feature (per-workspace logins, a Manage Workspaces command, workspace switchers on every command, a multi-workspace menu bar, workspace-routable AI tools).Before writing any of it, I ran an empirical spike against the real Raycast OAuth proxy and Linear's API to verify what multi-login actually requires. The result: everything works with Raycast's existing OAuth machinery — token storage, the proxy, PKCE — except that
OAuthService's built-in providers hardcode exactly two things a second login needs to vary. This PR makes those two things optional parameters. It is deliberately the smallest possible change: two optional fields, no behavior change for any existing caller, no new concepts.The extension PR doesn't block on this — it currently constructs the service manually (copying the proxy URLs and clientId this library already ships) behind a switch, and flips to
OAuthService.linear()once this lands in a release. This PR is the clean path that lets that copied plumbing be deleted.What
Adds two optional fields to the built-in
OAuthServiceprovider options (BaseProviderOptions), threaded through all seven built-in services (asana,github,google,jira,linear,slack,zoom):providerId?: string— passed to the internally-constructedOAuth.PKCEClient; defaults to today's hardcoded value (e.g."linear").providerIdis purely a local token-storage namespace: it never appears in any request the OAuth flow constructs (verified: authorize URLs are byte-identical across providerIds, modulo the per-request PKCE values), so overriding it simply gives a second, independent login slot for the same provider.extraParameters?: Record<string, string>— merged over the provider's defaults, caller wins (Linear:{ actor: "user", ...options.extraParameters }; Slack:{ user_scope: options.scope, ...options.extraParameters }; the other providers have no defaults).Fully backward compatible: with both options omitted, every provider constructs exactly what it constructs today. Tests (covering all seven providers' defaults, overrides, and merge semantics) and docs included.
Why these two fields specifically
A Linear OAuth token is scoped to a single workspace, so "log into a second workspace" needs exactly two things
OAuthService.linear()cannot currently express:providerId. The provider hardcodesproviderId: "linear", so a second login overwrites the first.prompt=consenton the authorize request. Without it, Linear silently skips the consent screen for an already-granted app, so the user can never complete a grant for another workspace. (Verified empirically: Linear's consent page has no in-flow workspace picker — the grant binds to whichever account/workspace is active at linear.app, which the user steers with linear.app's workspace switcher;prompt=consentreliably re-prompts on every run and is what makes a second-workspace grant reachable at all.)Today, extensions that need either capability re-implement the provider by hand:
raycast/extensions→extensions/productlane/src/oauth.tsmanually drives the same Linear proxy (https://linear.oauth.raycast.com) and clientId that this library already ships, just to control the client construction.raycast/extensions→extensions/coze/src/services/api.tsxconstructs per-workspaceOAuthServices with dynamic providerIds.raycast/extensionsconstructOAuthServicemanually (the docs' own manual example: https://developers.raycast.com/utilities/oauth/oauthservice) rather than using a built-in provider.References: Linear's OAuth documentation (workspace-scoped tokens,
actor,prompt): https://linear.app/developers/oauth-2-0-authentication. Prior art in the extensions repo: raycast/extensions#24297 (the request), raycast/extensions#25646 (a PAT-based attempt, closed stale — this approach stays entirely on Raycast's OAuth proxy and token storage instead).How it will be used
This generalizes beyond Linear to any provider where users hold several accounts (GitHub work/personal, multiple Slack workspaces, multiple Jira sites).
Open questions
providerName/descriptionso extensions can disambiguate? (Out of scope here; noting the observed behavior.)actorfor Linear). If you'd rather protect provider defaults from overrides, happy to change the spread order.