@molecule/api-oauth-client
v1.0.1
Published
OAuth 2.0 client core interface for molecule.dev — consume external OAuth APIs with authorization, token exchange, refresh, and revocation
Maintainers
Readme
@molecule/api-oauth-client
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Provider-agnostic OAuth 2.0 client interface for molecule.dev.
Defines the OAuthClientProvider interface for consuming external OAuth 2.0
APIs — building authorization URLs, exchanging codes for tokens, refreshing
tokens, making authenticated requests, and revoking access. Bond packages
(generic OAuth2, etc.) implement this interface. Application code uses the
convenience functions (getAuthorizationUrl, getToken, refreshToken,
request, revokeToken) which delegate to the bonded provider.
Quick Start
import { setProvider, getAuthorizationUrl, getToken } from '@molecule/api-oauth-client'
import { provider as genericOAuth } from '@molecule/api-oauth-client-generic'
setProvider(genericOAuth)
const config = {
id: 'github',
clientId: 'abc123',
clientSecret: 'secret',
authorizationUrl: 'https://github.com/login/oauth/authorize',
tokenUrl: 'https://github.com/login/oauth/access_token',
redirectUri: 'https://myapp.com/callback',
scopes: ['user', 'repo'],
}
const authUrl = getAuthorizationUrl(config, { state: 'csrf-token' })
const tokens = await getToken(config, 'authorization-code')Type
core
Installation
npm install @molecule/api-oauth-client @molecule/api-bond @molecule/api-i18nAPI
Interfaces
AuthorizationUrlOptions
Options for the authorization URL.
interface AuthorizationUrlOptions {
/** A CSRF-prevention state value. */
state?: string
/** PKCE code challenge. */
codeChallenge?: string
/** PKCE code challenge method (`'S256'` or `'plain'`). */
codeChallengeMethod?: 'S256' | 'plain'
/** Additional query parameters to include. */
additionalParams?: Record<string, string>
}OAuthClientConfig
Configuration options for oauth-client providers.
interface OAuthClientConfig {
/** Default timeout for HTTP requests in milliseconds. */
timeout?: number
/** Custom user-agent header for requests. */
userAgent?: string
}OAuthClientProvider
OAuth client provider interface.
All OAuth client providers must implement this interface. Bond packages provide concrete implementations that handle the OAuth 2.0 flow for consuming external APIs.
interface OAuthClientProvider {
/**
* Builds the authorization URL that the user should be redirected to.
*
* @param config - The OAuth provider configuration.
* @param options - Optional authorization URL parameters.
* @returns The fully-qualified authorization URL.
*/
getAuthorizationUrl(config: OAuthConfig, options?: AuthorizationUrlOptions): string
/**
* Exchanges an authorization code for access/refresh tokens.
*
* @param config - The OAuth provider configuration.
* @param code - The authorization code received from the provider.
* @param options - Optional token exchange parameters.
* @returns The token set.
*/
getToken(config: OAuthConfig, code: string, options?: TokenExchangeOptions): Promise<OAuthTokens>
/**
* Refreshes an expired access token using a refresh token.
*
* @param config - The OAuth provider configuration.
* @param refreshToken - The refresh token.
* @returns A new token set.
*/
refreshToken(config: OAuthConfig, refreshToken: string): Promise<OAuthTokens>
/**
* Makes an authenticated HTTP request to a resource server.
*
* @param tokens - The current token set.
* @param url - The resource URL.
* @param options - Optional request parameters.
* @returns The parsed response body.
*/
request(tokens: OAuthTokens, url: string, options?: RequestOptions): Promise<unknown>
/**
* Revokes an access or refresh token.
*
* @param config - The OAuth provider configuration.
* @param token - The token to revoke.
* @returns Resolves when the token is revoked.
*/
revokeToken(config: OAuthConfig, token: string): Promise<void>
}OAuthConfig
Configuration for an OAuth 2.0 provider (the external service).
interface OAuthConfig {
/** Unique identifier for this provider configuration. */
id: string
/** OAuth 2.0 client ID. */
clientId: string
/** OAuth 2.0 client secret. */
clientSecret: string
/** Authorization endpoint URL. */
authorizationUrl: string
/** Token endpoint URL. */
tokenUrl: string
/** Token revocation endpoint URL, if supported. */
revocationUrl?: string
/** Redirect URI registered with the provider. */
redirectUri: string
/** Requested scopes. */
scopes?: string[]
/** Scope delimiter (defaults to `' '`). */
scopeDelimiter?: string
}OAuthTokens
OAuth 2.0 access and refresh tokens.
interface OAuthTokens {
/** The access token. */
accessToken: string
/** The refresh token, if granted. */
refreshToken?: string
/** Token type (typically `'Bearer'`). */
tokenType: string
/** Access token lifetime in seconds, if provided. */
expiresIn?: number
/** Absolute expiration timestamp (ISO 8601). */
expiresAt?: string
/** Granted scopes (may differ from requested scopes). */
scope?: string
}RequestOptions
Options for making an authenticated request to a resource server.
interface RequestOptions {
/** HTTP method. Defaults to `'GET'`. */
method?: HttpMethod
/** Request headers. */
headers?: Record<string, string>
/** Request body (for POST/PUT/PATCH). */
body?: unknown
/** Content type. Defaults to `'application/json'`. */
contentType?: string
}TokenExchangeOptions
Options for the token exchange.
interface TokenExchangeOptions {
/** PKCE code verifier, required when a code challenge was used. */
codeVerifier?: string
/** Additional body parameters to include. */
additionalParams?: Record<string, string>
}Types
HttpMethod
HTTP method for authenticated requests.
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'OAuthGrantType
Supported OAuth 2.0 grant types.
type OAuthGrantType = 'authorization_code' | 'client_credentials' | 'refresh_token'OAuthResponseType
Supported OAuth 2.0 response types.
type OAuthResponseType = 'code' | 'token'Functions
getAuthorizationUrl(config, options)
Builds the authorization URL that the user should be redirected to.
function getAuthorizationUrl(config: OAuthConfig, options?: AuthorizationUrlOptions): stringconfig— The OAuth provider configuration.options— Optional authorization URL parameters.
Returns: The fully-qualified authorization URL.
getProvider()
Retrieves the bonded OAuth client provider, throwing if none is configured.
function getProvider(): OAuthClientProviderReturns: The bonded OAuth client provider.
getToken(config, code, options)
Exchanges an authorization code for access/refresh tokens.
function getToken(
config: OAuthConfig,
code: string,
options?: TokenExchangeOptions,
): Promise<OAuthTokens>config— The OAuth provider configuration.code— The authorization code received from the provider.options— Optional token exchange parameters.
Returns: The token set.
hasProvider()
Checks whether an OAuth client provider is currently bonded.
function hasProvider(): booleanReturns: true if an OAuth client provider is bonded.
refreshToken(config, token)
Refreshes an expired access token using a refresh token.
function refreshToken(config: OAuthConfig, token: string): Promise<OAuthTokens>config— The OAuth provider configuration.token— The refresh token string.
Returns: A new token set.
request(tokens, url, options)
Makes an authenticated HTTP request to a resource server.
function request(tokens: OAuthTokens, url: string, options?: RequestOptions): Promise<unknown>tokens— The current token set.url— The resource URL.options— Optional request parameters.
Returns: The parsed response body.
revokeToken(config, token)
Revokes an access or refresh token.
function revokeToken(config: OAuthConfig, token: string): Promise<void>config— The OAuth provider configuration.token— The token to revoke.
Returns: Resolves when the token is revoked.
setProvider(provider)
Registers an OAuth client provider as the active singleton. Called by bond packages during application startup.
function setProvider(provider: OAuthClientProvider): voidprovider— The OAuth client provider implementation to bond.
Available Providers
| Provider | Package |
| ------------ | ------------------------------------ |
| Oauth Client | @molecule/api-oauth-client-generic |
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1@molecule/api-i18n^1.0.1
Runtime Dependencies
@molecule/api-bond@molecule/api-i18n
This package CONSUMES external OAuth APIs on a user's behalf (calendar,
repo, CRM integrations). For "Log in with X" use @molecule/api-oauth +
@molecule/api-resource-user's logInOAuth, which already implement the
login flow's security checks — don't rebuild login on this client.
- The full
OAuthConfig— especiallyclientSecret— is SERVER-SIDE only, built from env/secrets (never literals in code). The browser only ever receives the authorization URL and returns thecodeto your API, which performs the exchange. stateand PKCE are optional parameters but NOT optional practice: send a per-session randomstate(and acodeChallenge, method'S256') ongetAuthorizationUrl, REJECT the callback unless the returnedstatematches the stored one, then pass the matchingcodeVerifieringetToken's options.- Persist the returned
OAuthTokensper user server-side (encrypted at rest).refreshTokenis only present when the provider grants one (e.g. offline scopes); trackexpiresAtand refresh before use or on auth failure — access tokens are short-lived. request(tokens, url, opts)attaches the token for you but does NOT auto-refresh — it throws on a non-2xx response; refresh-and-retry on auth failure is the caller's loop.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt
each item to this app's actual "connect account" screens/flows, and check
every box off one by one. A box you can't check is an integration bug to
fix — not a skip. The third-party CONSENT SCREEN cannot be driven in-sandbox,
so verify the token lifecycle + API-call wiring you own (authorize →
callback → getToken → token store → refreshToken → request), stubbing
the provider bond or the token endpoint where the real grant would occur:
- [ ] Connecting a third-party account from the UI runs
authorize→callback→
getTokenand STORES the returnedOAuthTokens(accessToken+refreshToken) server-side keyed to the authenticated user; the connection then shows as "connected" in the UI. - [ ] An authenticated call to the third-party API via
request(tokens, url)using the STORED token succeeds and its result appears in the app (bond a stub/test provider if available, else assertrequestis invoked with the storedaccessToken— never a hardcoded or browser-supplied one). - [ ] Token REFRESH works: an expired
accessToken(force/simulate expiry viaexpiresAt) is transparently refreshed withrefreshTokenand the call is RETRIED — confirm exactly ONE refresh + a stored-token update, not an auth error surfaced to the user (requestdoes not auto-refresh; the caller's refresh-and-retry loop must). - [ ] Disconnecting revokes/removes the stored tokens (
revokeToken+ delete from the store) and the connection no longer works — a subsequent API call fails until the account is reconnected. - [ ] SECURITY:
accessToken/refreshToken+clientSecretlive server-side only (encrypted at rest ideally) and are NEVER sent to the browser — the client only ever receives the authorization URL and returns thecode. - [ ] Tokens are scoped per user: user A's stored connection cannot be used to act as user B (the store is keyed by user id; handlers load only the caller's own tokens).
- [ ] The callback verifies
state(CSRF): a per-session randomstatesent ongetAuthorizationUrlmust match on the callback, and a missing or mismatchedstateis rejected BEFORE any token exchange.
