/** * Token Endpoint — POST /oauth/token * * Who calls: Client (server-to-server). * * When: After getting the code (or for refresh). * * Purpose: Exchange authorization code + PKCE verifier for access token (and optional refresh token), or refresh an access token. */ /** * Typical parameter shapes * * /oauth/token (POST, application/x-www-form-urlencoded) * * For code exchange: grant_type=authorization_code, code, redirect_uri, client_id (and auth), code_verifier * * For refresh: grant_type=refresh_token, refresh_token, client_id (and auth) */ /** * Quick checklist (security & correctness) * - PKCE (S256) required for public clients (and basically for all). * - Use authorization code grant only (no implicit/hybrid). * - Rotate refresh tokens and bind them to client + user + scopes. * - Prefer private_key_jwt or mTLS for confidential clients. * - PAR + JAR recommended for higher security. * - Consider DPoP (proof-of-possession) to reduce token replay. * - Keep codes very short-lived (e.g., ≤60 s) and single-use. * - Publish discovery and JWKS, rotate keys safely. * - Decide JWT vs opaque access tokens; provide introspection if opaque. */ /** * * OAuth 2.0 Device Authorization Grant (“device code flow”) * Who does what (at a glance) * * Device/TV/CLI (no browser) * Calls POST /oauth/device_authorization, shows the user a code + URL, and polls POST /oauth/token. * * User (on phone/laptop browser) * Visits the given verification_uri and authenticates using your normal OAuth login (whatever you already have). No new UI required beyond two tiny endpoints. * * Auth Server (you) * Stores the device transaction and, after the user authenticates, marks it as approved so the device’s /oauth/token polling succeeds. * * Endpoints you need (only two “new” ones) * * POST /oauth/device_authorization ✅ (device calls) * * POST /oauth/token with grant urn:ietf:params:oauth:grant-type:device_code ✅ (device polls) * * GET /activate ➜ “UI handler” (user lands here from verification_uri — this just redirects into your existing /oauth/authorize) * * GET /activate/callback ➜ “UI handler” (your existing flow returns here after the user logs in; you flip the device record to approved and show a basic “All set” page) * * That’s it. No pages with complex consent screens are required; reuse your normal /oauth/authorize */ import { FlowBase, FlowRunOptions } from "@frontmcp/sdk"; import { z } from "zod"; declare const inputSchema: z.ZodObject<{ request: z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>; response: z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>; next: z.ZodOptional, z.ZodUnknown>>; }, "strip", z.ZodTypeAny, { request: {} & { [k: string]: unknown; }; response: {} & { [k: string]: unknown; }; next?: ((...args: unknown[]) => unknown) | undefined; }, { request: {} & { [k: string]: unknown; }; response: {} & { [k: string]: unknown; }; next?: ((...args: unknown[]) => unknown) | undefined; }>; declare const stateSchema: z.ZodObject<{ body: z.ZodDiscriminatedUnion<"grant_type", [z.ZodObject<{ grant_type: z.ZodLiteral<"anonymous">; /** Public client identifier; UUID in your example */ client_id: z.ZodString; /** Target resource/audience is required for this custom flow */ resource: z.ZodString; }, "strip", z.ZodTypeAny, { resource: string; grant_type: "anonymous"; client_id: string; }, { resource: string; grant_type: "anonymous"; client_id: string; }>, z.ZodObject<{ grant_type: z.ZodLiteral<"authorization_code">; /** Authorization code returned from the /authorize step */ code: z.ZodString; /** Must exactly match the redirect URI used when obtaining the code */ redirect_uri: z.ZodString; /** Public client identifier; UUID in your example */ client_id: z.ZodString; /** PKCE verifier bound to the code */ code_verifier: z.ZodString; /** Optional resource/audience (used by some providers like AAD v1) */ resource: z.ZodString; }, "strip", z.ZodTypeAny, { code: string; resource: string; grant_type: "authorization_code"; client_id: string; redirect_uri: string; code_verifier: string; }, { code: string; resource: string; grant_type: "authorization_code"; client_id: string; redirect_uri: string; code_verifier: string; }>]>; isDefaultAuthProvider: z.ZodBoolean; }, "strip", z.ZodTypeAny, { body: { resource: string; grant_type: "anonymous"; client_id: string; } | { code: string; resource: string; grant_type: "authorization_code"; client_id: string; redirect_uri: string; code_verifier: string; }; isDefaultAuthProvider: boolean; }, { body: { resource: string; grant_type: "anonymous"; client_id: string; } | { code: string; resource: string; grant_type: "authorization_code"; client_id: string; redirect_uri: string; code_verifier: string; }; isDefaultAuthProvider: boolean; }>; declare const outputSchema: z.ZodObject<{ kind: z.ZodLiteral<"json">; status: z.ZodEffects; body: z.ZodUnion<[z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>, z.ZodArray, z.ZodRecord]>; contentType: z.ZodDefault; } & { headers: z.ZodOptional]>]>>>>; cookies: z.ZodOptional; domain: z.ZodOptional; httpOnly: z.ZodDefault; secure: z.ZodOptional; sameSite: z.ZodOptional>; maxAge: z.ZodOptional; expires: z.ZodOptional; }, "strip", z.ZodTypeAny, { name: string; value: string; path: string; httpOnly: boolean; domain?: string | undefined; secure?: boolean | undefined; sameSite?: "lax" | "strict" | "none" | undefined; maxAge?: number | undefined; expires?: Date | undefined; }, { name: string; value: string; path?: string | undefined; domain?: string | undefined; httpOnly?: boolean | undefined; secure?: boolean | undefined; sameSite?: "lax" | "strict" | "none" | undefined; maxAge?: number | undefined; expires?: Date | undefined; }>, "many">>>; }, "strip", z.ZodTypeAny, { status: number; kind: "json"; body: any[] | z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | Record; contentType: string; headers?: Record | undefined; cookies?: { name: string; value: string; path: string; httpOnly: boolean; domain?: string | undefined; secure?: boolean | undefined; sameSite?: "lax" | "strict" | "none" | undefined; maxAge?: number | undefined; expires?: Date | undefined; }[] | undefined; }, { status: number; kind: "json"; body: any[] | z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | Record; headers?: Record | undefined; cookies?: { name: string; value: string; path?: string | undefined; domain?: string | undefined; httpOnly?: boolean | undefined; secure?: boolean | undefined; sameSite?: "lax" | "strict" | "none" | undefined; maxAge?: number | undefined; expires?: Date | undefined; }[] | undefined; contentType?: string | undefined; }>; declare const plan: { readonly pre: ["parseInput", "validateInput"]; readonly execute: ["generateJWT", "buildAuthorizeOutput"]; readonly post: ["validateOutput"]; }; declare global { export interface ExtendFlows { 'oauth:token': FlowRunOptions; } } declare const name: "oauth:token"; export default class OauthTokenFlow extends FlowBase { parseInput(): Promise; validateInput(): Promise; buildAuthorizeOutput(): Promise; validateOutput(): Promise; } export {};