API: @loopstack/oauth
Classes
BuildOAuthUrlTool
Tool that builds an OAuth 2.0 authorization URL for a provider, including a CSRF state parameter.
import { BuildOAuthUrlTool } from '@loopstack/oauth';Provided by: OAuthModule
export class BuildOAuthUrlTool extends BaseTool<BuildOAuthUrlArgs, object, BuildOAuthUrlResult> { protected handle(args: BuildOAuthUrlArgs): Promise<ToolEnvelope<BuildOAuthUrlResult>>;}ExchangeOAuthTokenTool
Tool that exchanges an OAuth 2.0 authorization code for access and refresh tokens and stores them for the user.
import { ExchangeOAuthTokenTool } from '@loopstack/oauth';Provided by: OAuthModule
export class ExchangeOAuthTokenTool extends BaseTool<ExchangeOAuthTokenArgs, object, ExchangeOAuthTokenResult> { protected handle(args: ExchangeOAuthTokenArgs, ctx: RunContext): Promise<ToolEnvelope<ExchangeOAuthTokenResult>>;}OAuthCallbackController
Public OAuth callback endpoints that complete the authorization-code flow
from code + state alone: the state token (unguessable, single-use,
10 minute TTL, re-validated by the workflow’s own expectedState check)
resolves the pending OAuthWorkflow, whose exchangeToken transition is
fired server-side as the user who started the flow.
Two entry points, same completion:
GET /api/v1/oauth/callback— a provider redirect target (*_OAUTH_REDIRECT_URI=<api>/api/v1/oauth/callback); renders a minimal “connected” page. Works without the Studio frontend — CLI-printed auth URLs and headless deployments complete here.POST /api/v1/oauth/complete— JSON variant used by Studio’s callback page when it was opened without a popup opener (e.g. from a CLI link while the redirect URI still points at Studio).
import { OAuthCallbackController } from '@loopstack/oauth';Provided by: OAuthModule
export class OAuthCallbackController { constructor(sessions: OAuthSessionService, workflowRunner: WorkflowRunner); complete(payload: CompleteOAuthPayload): Promise<{ ok: true; provider: string; }>; callback(res: Response, code?: string, state?: string, error?: string, errorDescription?: string): Promise<void>;}OAuthModule
NestJS module that provides the provider-agnostic OAuth 2.0 framework — the
OAuthProviderRegistry and OAuthTokenStore services, the build_oauth_url and
exchange_oauth_token tools, and the generic OAuthWorkflow for the authorization code flow.
Registration:
OAuthModule— a bare import is enough. The module is@Global(), so a single import anywhere makes its services, tools, and workflow available application-wide via DI; no static configuration method exists.
Requires: at least one OAuth provider implementing OAuthProviderInterface must be registered
for authentication to do anything useful — provider modules (e.g. @loopstack/google-workspace
or @loopstack/github) self-register on init via OAuthProviderRegistry. Token persistence
uses Redis (REDIS_HOST/REDIS_PORT/REDIS_PASSWORD) but is optional, falling back to in-memory
storage when Redis is unavailable.
import { OAuthModule } from '@loopstack/oauth';export class OAuthModule {}OAuthPromptDocument
Document that renders the OAuth authorization prompt (provider, auth URL, and connection status) in Studio.
import { OAuthPromptDocument } from '@loopstack/oauth';export class OAuthPromptDocument { provider: string; authUrl: string; state: string; status: 'pending' | 'success' | 'error'; message?: string;}OAuthSessionService
Service that registers pending OAuth flows by their CSRF state and hands
them out exactly once (Redis-backed with in-memory fallback, 10 minute
TTL) — the server-side state → workflow lookup behind the public OAuth
callback.
import { OAuthSessionService } from '@loopstack/oauth';Provided by: OAuthModule
export class OAuthSessionService implements OnModuleDestroy { constructor(); onModuleDestroy(): void; register(state: string, session: OAuthSession): Promise<void>; consume(state: string): Promise<OAuthSession | undefined>;}OAuthTokenStore
Service that stores and retrieves per-user OAuth tokens (Redis-backed, with in-memory fallback) and transparently refreshes expired access tokens; inject it to read a valid access token for a provider.
import { OAuthTokenStore } from '@loopstack/oauth';Provided by: OAuthModule
export class OAuthTokenStore implements OnModuleDestroy { constructor(); onModuleDestroy(): void; storeTokens(userId: string, providerId: string, tokens: StoredTokens): Promise<void>; storeFromTokenSet(userId: string, providerId: string, tokenSet: OAuthTokenSet): Promise<void>; getTokens(userId: string, providerId: string): Promise<StoredTokens | undefined>; getValidAccessToken(userId: string, providerId: string): Promise<string | undefined>;}OAuthWorkflow
Workflow that runs the OAuth 2.0 authorization code flow for a provider: it builds the auth URL, prompts
the user via an OAuthPromptDocument, and exchanges the returned code for stored tokens. Run it standalone
to pre-authenticate or invoke it from another workflow when authentication is required.
import { OAuthWorkflow } from '@loopstack/oauth';Provided by: OAuthModule
export class OAuthWorkflow extends BaseWorkflow<OAuthArgs> { constructor( buildOAuthUrl: BuildOAuthUrlTool, exchangeOAuthToken: ExchangeOAuthTokenTool, sessions: OAuthSessionService, ); initiateOAuth(state: OAuthState, ctx: RunContext<OAuthArgs>): Promise<void>; exchangeToken( state: OAuthState, input: TransitionInput<{ code: string; state: string; }>, ): Promise<void>;}Interfaces
BuildOAuthUrlResult
Result for BuildOAuthUrlTool.
import { BuildOAuthUrlResult } from '@loopstack/oauth';export interface BuildOAuthUrlResult { authUrl: string; state: string;}OAuthProviderInterface
Contract that an OAuth 2.0 provider implements to plug into the OAuth framework; implement it to add a new provider.
import { OAuthProviderInterface } from '@loopstack/oauth';export interface OAuthProviderInterface { readonly providerId: string; readonly defaultScopes: string[]; buildAuthUrl(scopes: string[], state: string): string; exchangeCode(code: string): Promise<OAuthTokenSet>; refreshToken(refreshToken: string): Promise<OAuthTokenSet>;}OAuthSession
Pending OAuth flow registered when the authorization URL is built — the
lookup that lets the public callback complete the exchange from
code + state alone, regardless of who opened the URL (Studio popup,
CLI-printed link, plain browser).
import { OAuthSession } from '@loopstack/oauth';export interface OAuthSession { workflowId: string; userId: string; provider: string;}OAuthTokenSet
Token set returned by an OAuth provider after a code exchange or refresh.
import { OAuthTokenSet } from '@loopstack/oauth';export interface OAuthTokenSet { accessToken: string; refreshToken?: string; expiresIn?: number; scope: string;}StoredTokens
Persisted OAuth token record held by OAuthTokenStore.
import { StoredTokens } from '@loopstack/oauth';export interface StoredTokens { accessToken: string; refreshToken?: string; expiresAt?: number; scope: string;}Type Aliases
BuildOAuthUrlArgs
Args for BuildOAuthUrlTool.
import { BuildOAuthUrlArgs } from '@loopstack/oauth';export type BuildOAuthUrlArgs = z.infer<typeof BuildOAuthUrlSchema>;ExchangeOAuthTokenArgs
Args for ExchangeOAuthTokenTool.
import { ExchangeOAuthTokenArgs } from '@loopstack/oauth';export type ExchangeOAuthTokenArgs = z.infer<typeof ExchangeOAuthTokenSchema>;ExchangeOAuthTokenResult
Result for ExchangeOAuthTokenTool.
import { ExchangeOAuthTokenResult } from '@loopstack/oauth';export type ExchangeOAuthTokenResult = { accessToken: string; refreshToken: string | undefined; expiresIn: number | undefined; scope: string | undefined;};Variables
BuildOAuthUrlResultSchema
Zod schema for BuildOAuthUrlResult — the resultSchema of build_oauth_url.
import { BuildOAuthUrlResultSchema } from '@loopstack/oauth';BuildOAuthUrlResultSchema: z.ZodObject< { authUrl: z.ZodString; state: z.ZodString; }, z.core.$strict>;BuildOAuthUrlSchema
Zod schema for BuildOAuthUrlTool args.
import { BuildOAuthUrlSchema } from '@loopstack/oauth';BuildOAuthUrlSchema: z.ZodObject< { provider: z.ZodString; scopes: z.ZodArray<z.ZodString>; }, z.core.$strict>;ExchangeOAuthTokenResultSchema
Zod schema for ExchangeOAuthTokenResult — the resultSchema of exchange_oauth_token.
import { ExchangeOAuthTokenResultSchema } from '@loopstack/oauth';ExchangeOAuthTokenResultSchema: z.ZodObject< { accessToken: z.ZodString; refreshToken: z.ZodOptional<z.ZodString>; expiresIn: z.ZodOptional<z.ZodNumber>; scope: z.ZodOptional<z.ZodString>; }, z.core.$strict>;ExchangeOAuthTokenSchema
Zod schema for ExchangeOAuthTokenTool args.
import { ExchangeOAuthTokenSchema } from '@loopstack/oauth';ExchangeOAuthTokenSchema: z.ZodObject< { provider: z.ZodString; code: z.ZodString; state: z.ZodString; expectedState: z.ZodString; }, z.core.$strict>;OAuthPromptDocumentSchema
Zod schema for OAuthPromptDocument.
import { OAuthPromptDocumentSchema } from '@loopstack/oauth';OAuthPromptDocumentSchema: z.ZodObject< { provider: z.ZodString; authUrl: z.ZodString; state: z.ZodString; status: z.ZodDefault< z.ZodEnum<{ error: 'error'; success: 'success'; pending: 'pending'; }> >; message: z.ZodOptional<z.ZodString>; }, z.core.$strict>;