/** * OAuth2 for Outlook.com / Hotmail / Live accounts * * Microsoft has disabled Basic Auth for Outlook/Hotmail/Live accounts (September 2024). * This module implements Device Code Grant (RFC 8628) for CLI-based auth, * persistent token storage, and automatic token refresh. */ import type { CredStoreLike } from '../../auth/in-memory-cred-store.js'; /** * Tenant used for the device-code and token endpoints. ``OUTLOOK_TENANT`` wins * when it is set and well-formed; otherwise the caller's default applies (the * two call sites differ: this module historically signs in personal accounts, * the HTTP delegated flow uses "common"). */ export declare function getOutlookTenant(fallback?: string): string; /** * Scopes requested at sign-in and re-sent on refresh. ``OUTLOOK_SCOPES`` * (space-separated) overrides the default set — a grant consented with a * narrower scope (IMAP-only, no SMTP) is legitimate for read-only deployments, * and refreshing such a grant against the full list is rejected. */ export declare function getOutlookScopes(fallback?: string[]): string[]; export interface OAuth2Tokens { accessToken: string; refreshToken: string; expiresAt: number; clientId: string; } export interface EnsureValidTokenOptions { allowInteractive?: boolean; } export type OAuth2AuthErrorCode = 'OAUTH_AUTH_REQUIRED' | 'OAUTH_REFRESH_FAILED'; export declare class OAuth2AuthError extends Error { readonly code: OAuth2AuthErrorCode; constructor(code: OAuth2AuthErrorCode, message: string); } interface TokenStore { [email: string]: OAuth2Tokens; } interface TokenResponse { access_token: string; refresh_token: string; expires_in: number; token_type: string; error?: string; error_description?: string; } /** * Type guard to validate OAuth2Tokens structure */ export declare function isValidTokens(data: unknown): data is OAuth2Tokens; /** * Type guard to validate TokenStore structure */ export declare function isValidTokenStore(data: unknown): data is TokenStore; /** Outlook/Hotmail/Live domains that require OAuth2 */ /** * Extract the "sub" claim from an OIDC id_token without verifying signatures. * id_token is a JWT (header.payload.signature). */ export declare function decodeIdTokenSubject(idToken: unknown): string | null; /** * Check if an email address belongs to an Outlook/Hotmail/Live domain, or to a * domain the deployment declared as OAuth-backed via ``OUTLOOK_EXTRA_DOMAINS``. */ export declare function isOutlookDomain(email: string): boolean; /** * Get the Azure AD client ID for OAuth2. * Uses bundled client ID by default, can be overridden via OUTLOOK_CLIENT_ID env var. */ export declare function getClientId(): string; /** Inject the per-sub credential store used for embedding Outlook tokens. * * When the store is set (HTTP multi-user mode), device-code sessions are * persisted to KV so the background poll survives container sleep/recreate. * On container wake, the KV entry is checked in ``ensureValidToken()`` before * starting a fresh device-code flow, and the poll is resumed automatically. */ export declare function setOutlookTokenStore(store: CredStoreLike | null): void; /** Exposed for testing */ export declare function _resetTokenCache(): void; /** * Load stored OAuth2 tokens for an email account (optionally for an explicit * ``sub``; absent => the current request scope). Returns null if none stored. */ export declare function loadStoredTokens(email: string, sub?: string | null): Promise; /** * Emails (keys) of the stored Outlook token map for the given sub * (``null`` = single-user / file store). Used at startup by credential-state to * synthesize the ``email:oauth2`` credential string without a raw FS read. */ export declare function loadOutlookEmails(sub: string | null): Promise; /** * Persist OAuth2 tokens. Embed path (a sub + an injected store): load the * current per-sub blob fresh, merge the token into ``outlookTokens``, write it * back — preserving ``accounts``. The per-sub Container DO is single-threaded so * this load-then-save is atomic enough, and R1 (``sleepAfter >= 20m``) keeps the * device-code poll's target instance alive. File path (single-user / stdio): * the legacy 0600 ``tokens.json`` write. */ export declare function saveTokens(email: string, tokens: OAuth2Tokens, sub?: string | null): Promise; /** * Delete stored OAuth2 token(s) from the local file store (single-user / * stdio CLI scope only -- HTTP multi-user tokens live in the per-sub KV blob * and are cleared via account-level ``config`` actions, not this CLI path). * Omitting ``email`` clears every stored token. Returns the email keys that * were actually removed (empty array if there was nothing to delete). */ export declare function deleteStoredTokens(email?: string): Promise; /** * Refresh an access token using the stored refresh token. * Microsoft may rotate the refresh token on each use. */ export declare function refreshAccessToken(clientId: string, refreshToken: string): Promise; /** * Track pending Device Code auth flows so we don't request new codes on every retry. * Maps email → { verificationUri, userCode, expiresAt } */ interface PendingAuth { verificationUri: string; userCode: string; expiresAt: number; } /** Exposed for testing */ export declare function _getPendingAuths(): Map; /** * Dedupe + browser-open logic is delegated to ``mcp-core``'s * ``tryOpenBrowser``: it validates the URL (only http/https), uses * ``execFile`` to avoid shell injection, detects WSL, and dedupes repeat calls * for the same URL within a 5-minute window. Exported for backward compat * with tests that reset test state. */ /** Exposed for testing — no-op after mcp-core consolidation. */ export declare function _resetBrowserOpenDedupe(): void; /** * Initiate the Device Code OAuth flow for an Outlook account without * throwing. Used by the HTTP /authorize callback to surface the sign-in * URL + user code to the custom credential form. * * If a pending auth already exists for this email (e.g. user resubmitted the * form), returns the existing codes instead of requesting new ones. The * background poll saves tokens on success and invokes ``onComplete`` so the * form can mark setup complete via GET /setup-status. * * ``sub`` is threaded EXPLICITLY (not read from ``currentSub()``) because the * /authorize callback that initiates this flow does not run inside the per-mcp * request scope — the JWT sub comes from ``onCredentialsSaved``'s context. The * detached background poll uses it to write the token to the right per-sub blob. */ export declare function initiateOutlookDeviceCode(email: string, onComplete?: () => void, sub?: string | null): Promise<{ verificationUri: string; userCode: string; expiresIn: number; interval: number; }>; /** * Ensure the account has a valid (non-expired) access token. * * Stored tokens are loaded when ``account.oauth2`` is absent. If no tokens are * available, the default is non-interactive: no Device Code request, * background poll, or browser launch is started. Instead, the function throws * ``OAuth2AuthError`` with the stable ``OAUTH_AUTH_REQUIRED`` code. Pass * ``{ allowInteractive: true }`` explicitly to start or resume the Device * Code flow; that path may open the verification URL in a browser and throws * sign-in instructions while the background poll runs. On a later retry, the * function picks up tokens saved by that poll. * * If tokens exist but are expired or within the five-minute safety buffer, the * access token is refreshed automatically. Refreshed tokens are persisted and * ``account.oauth2`` is updated in place. A refresh failure throws * ``OAuth2AuthError`` with the stable ``OAUTH_REFRESH_FAILED`` code. * * @param account Account whose OAuth2 token should be validated and updated. * @param options Controls whether missing-token authentication may start the * interactive Device Code flow. Interactive authentication is disabled by * default. * @throws ``OAuth2AuthError`` with ``OAUTH_AUTH_REQUIRED`` when authentication * is missing in non-interactive mode, or ``OAUTH_REFRESH_FAILED`` when token * refresh fails. */ export declare function ensureValidToken(account: { email: string; oauth2?: OAuth2Tokens; }, options?: EnsureValidTokenOptions): Promise; /** * Save Outlook tokens received via mcp-core delegated OAuth callback. * * mcp-core's ``TokenCallback`` delivers ``OAuthTokens`` (Record). * Adapts that to the existing ``saveTokens(email, OAuth2Tokens)`` format so * remote-relay mode shares the same token file as local-relay / CLI auth flows. * * The email is extracted from the token's ``email`` field (set by the upstream * form) or from ``OUTLOOK_EMAIL`` env var if the caller knows the account. * When neither is present, tokens are stored under the ``id_token`` subject * claim as a fallback (uncommon in device-code flows). */ export declare function saveOutlookTokens(tokens: Record): Promise; /** * Interactive Device Code flow for CLI-based OAuth2 authentication. * Prints instructions to stderr and polls until user authorizes or timeout. * Used by `npx @n24q02m/better-email-mcp auth `. */ export declare function deviceCodeAuth(email: string, clientId?: string): Promise; export {}; //# sourceMappingURL=oauth2.d.ts.map