veryfront/oauth.
The module provides:
- pre-configured providers such as GitHub, Google, Slack, and Notion
- route helpers for init, callback, status, and disconnect
- per-user token storage through a required
getUserIdfunction
Prerequisites
- An app session that lets you identify the signed-in user (
getSessionUserIdin the examples below). - A shared token store backing every handler in the flow. Production stores
that persist refresh tokens must implement
RefreshCapableTokenStoreusing atomic compare-and-set and a distributed refresh lease. - Provider credentials (client id, client secret, callback URL) set as
environment variables. See the matching provider config object in
veryfront/oauth.
Quick setup
Two routes handle the full OAuth flow: redirect to the provider and handle the callback. The init handler requires agetUserId function that returns the
authenticated user’s id from your session; unauthenticated requests receive a
401. The callback recovers that identity from the one-shot state row.
/api/auth/github to start the flow. After authorization, they’re
redirected back to your callback route. Tokens are stored in that user’s
per-user slot: never in a single shared slot.
Security.getUserIdis required. The init handler rejects any request where it returnsnull,undefined, or an empty string. The user’s id is bound into the OAuth state row and the callback stores tokens keyed by(serviceId, userId), so one user cannot overwrite another user’s tokens by completing an OAuth flow.
Choose a provider
Pre-configured provider exports are available for the supported default end-user integrations: GitHub, Slack, Notion, Figma, Linear, GitLab, Airtable, Asana, Gmail, Google Calendar (calendarConfig), Sheets, Google Drive, Jira,
Confluence, Outlook, Teams, SharePoint, and OneDrive.
Some provider configs are retained for source compatibility but require a
provider-specific runtime adapter. Those configs cannot be passed to the
generic handlers. An integration becomes scaffoldable only after its adapter
implements the provider’s complete wire protocol.
Each provider exports a config object (e.g., githubConfig, gmailConfig).
Use the matching export from
veryfront/oauth as the source of truth
for exact config names.
API setup for OAuth credentials
For each OAuth provider, create an application and configure the callback URL:Google APIs (shared credentials)
Google Calendar, Gmail, Docs, Drive, and Sheets all use the sameGOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET. Register one Google OAuth app and
enable all required APIs in the Cloud Console:
Microsoft APIs (shared credentials)
Outlook, Teams, OneDrive, and SharePoint all useMICROSOFT_CLIENT_ID /
MICROSOFT_CLIENT_SECRET. Register one Azure AD app with the required Microsoft
Graph permissions.
API-key integrations
These integrations use API keys set by the developer in their project environment variables. No OAuth app is needed. Use Integrations for connector setup and keep API-key variables in your deployment environment.Token storage
The handlers use an in-memory store only in an explicit development or test environment. Outside those environments, constructing a handler without a store fails immediately. Use one shared store for init, callback, status, disconnect, and provider API calls:createApplicationOAuthTokenStore represents the factory exported by your
storage extension; it is not a core Veryfront dependency.
The application adapter must provide the complete contract:
- key tokens by
(serviceId, userId)and issue a new opaque revision for every successful write; - implement compare-and-set as one backing-store operation;
- serialize refresh for one token slot with a bounded, crash-recoverable, distributed lease;
- expire state rows after a short TTL and consume each row with one atomic read-and-delete operation.
setTokens(serviceId, userId, tokens). If the state row is missing, expired,
forged, or already consumed, the callback returns an error without storing
anything.
Status and disconnect
Check if a user is connected, or disconnect them. These handlers also requiregetUserId so they act on the caller’s own tokens only:
Custom OAuth provider
For providers not included, create your own config:Calling provider APIs on behalf of a user
OAuth service clients (e.g.OAuthService.fetch, OAuthService.getAccessToken)
require the authenticated user’s id so tokens are looked up from that user’s
slot:
Verify it worked
Sign in as a test user, then open the init route in a browser:- Redirects to the provider’s consent screen.
-
Returns to your callback route with
?code=...andstate=.... -
Stores tokens for the signed-in user. Confirm via:
-
Calling
gmail.fetch(userId, ...)(or any provider service) returns the expected provider response without a401.