@mcp-z/oauth 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/account-utils.js.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/jwt-auth.js.map +1 -1
- package/dist/cjs/key-utils.js.map +1 -1
- package/dist/cjs/lib/account-server/index.js.map +1 -1
- package/dist/cjs/lib/account-server/loopback.js +27 -19
- package/dist/cjs/lib/account-server/loopback.js.map +1 -1
- package/dist/cjs/lib/account-server/me.js.map +1 -1
- package/dist/cjs/lib/account-server/shared-utils.js.map +1 -1
- package/dist/cjs/lib/account-server/stateless.js.map +1 -1
- package/dist/cjs/lib/account-server/types.d.cts +2 -2
- package/dist/cjs/lib/account-server/types.d.ts +2 -2
- package/dist/cjs/lib/account-server/types.js.map +1 -1
- package/dist/cjs/lib/dcr-types.js.map +1 -1
- package/dist/cjs/lib/rfc-metadata-types.js.map +1 -1
- package/dist/cjs/pkce.js.map +1 -1
- package/dist/cjs/sanitizer.js.map +1 -1
- package/dist/cjs/schemas/index.js.map +1 -1
- package/dist/cjs/session-auth.js.map +1 -1
- package/dist/cjs/templates.js.map +1 -1
- package/dist/cjs/types.d.cts +3 -2
- package/dist/cjs/types.d.ts +3 -2
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/account-utils.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/jwt-auth.js.map +1 -1
- package/dist/esm/key-utils.js.map +1 -1
- package/dist/esm/lib/account-server/index.js.map +1 -1
- package/dist/esm/lib/account-server/loopback.js +8 -5
- package/dist/esm/lib/account-server/loopback.js.map +1 -1
- package/dist/esm/lib/account-server/me.js.map +1 -1
- package/dist/esm/lib/account-server/shared-utils.js.map +1 -1
- package/dist/esm/lib/account-server/stateless.js.map +1 -1
- package/dist/esm/lib/account-server/types.d.ts +2 -2
- package/dist/esm/lib/account-server/types.js.map +1 -1
- package/dist/esm/lib/dcr-types.js.map +1 -1
- package/dist/esm/lib/rfc-metadata-types.js.map +1 -1
- package/dist/esm/pkce.js.map +1 -1
- package/dist/esm/sanitizer.js.map +1 -1
- package/dist/esm/schemas/index.js.map +1 -1
- package/dist/esm/session-auth.js.map +1 -1
- package/dist/esm/templates.js.map +1 -1
- package/dist/esm/types.d.ts +3 -2
- package/dist/esm/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth/src/account-utils.ts"],"sourcesContent":["/**\n * Account management utilities for OAuth token storage\n *\n * Provides account lifecycle operations (add, remove, activate) and account data\n * access (tokens, metadata). Uses named parameters consistent with key-utils.ts.\n */\n\nimport type { Keyv } from 'keyv';\nimport { type AccountKeyParams, createAccountKey, createServiceKey, type ServiceKeyParams } from './key-utils.ts';\nimport type { AccountInfo } from './types.ts';\n\n// ============================================================================\n// Account Lifecycle Operations\n// ============================================================================\n\n/**\n * Add account to linked accounts list and set as active if first account.\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (service, accountId)\n *\n * @example\n * await addAccount(tokenStore, {\n * service: 'gmail',\n * accountId: 'alice@gmail.com'\n * });\n */\nexport async function addAccount(store: Keyv, params: AccountKeyParams): Promise<void> {\n const linked = await getLinkedAccounts(store, { service: params.service });\n\n if (!linked.includes(params.accountId)) {\n linked.push(params.accountId);\n const linkedKey = createServiceKey('linked', { service: params.service });\n await store.set(linkedKey, linked);\n }\n\n const active = await getActiveAccount(store, { service: params.service });\n if (!active) {\n await setActiveAccount(store, params);\n }\n}\n\n/**\n * Remove account: delete token, metadata, update linked list, and active account.\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (service, accountId)\n *\n * @example\n * await removeAccount(tokenStore, {\n * service: 'gmail',\n * accountId: 'alice@gmail.com'\n * });\n */\nexport async function removeAccount(store: Keyv, params: AccountKeyParams): Promise<void> {\n const tokenKey = createAccountKey('token', params);\n await store.delete(tokenKey);\n\n const infoKey = createAccountKey('metadata', params);\n await store.delete(infoKey);\n\n const linked = await getLinkedAccounts(store, { service: params.service });\n const filtered = linked.filter((id) => id !== params.accountId);\n const linkedKey = createServiceKey('linked', { service: params.service });\n await store.set(linkedKey, filtered);\n\n // Set new active account if we're removing the currently active one\n const active = await getActiveAccount(store, { service: params.service });\n if (active === params.accountId) {\n const newActive = filtered[0];\n if (newActive) {\n await setActiveAccount(store, { service: params.service, accountId: newActive });\n } else {\n const activeKey = createServiceKey('active', { service: params.service });\n await store.delete(activeKey);\n }\n }\n}\n\n// ============================================================================\n// Service-Scoped Account Operations\n// ============================================================================\n\n/**\n * Get active account ID for a service.\n *\n * Key: {service}:active\n *\n * @param store - Keyv storage instance\n * @param params - Service identification (service)\n * @returns Active account ID or undefined if none set\n */\nexport async function getActiveAccount(store: Keyv, params: ServiceKeyParams): Promise<string | undefined> {\n const key = createServiceKey('active', params);\n return await store.get(key);\n}\n\n/**\n * Set active account ID for a service.\n * Pass null as accountId to deactivate (clear active account).\n *\n * Key: {service}:active\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (service, accountId). Pass accountId: null to deactivate.\n */\nexport async function setActiveAccount(store: Keyv, params: AccountKeyParams | (ServiceKeyParams & { accountId: null })): Promise<void> {\n const key = createServiceKey('active', { service: params.service });\n if ('accountId' in params && params.accountId === null) {\n // accountId: null signals deactivation per API contract\n await store.delete(key);\n } else {\n await store.set(key, (params as AccountKeyParams).accountId);\n }\n}\n\n/**\n * Get list of linked account IDs for a service.\n *\n * Key: {service}:linked\n *\n * @param store - Keyv storage instance\n * @param params - Service identification (service)\n * @returns Array of account IDs (empty array if none)\n */\nexport async function getLinkedAccounts(store: Keyv, params: ServiceKeyParams): Promise<string[]> {\n const key = createServiceKey('linked', params);\n const accounts = await store.get(key);\n return accounts || [];\n}\n\n// ============================================================================\n// Account Data Operations\n// ============================================================================\n\n/**\n * Get account metadata (alias, lastUsed, etc).\n *\n * Key: {accountId}:{service}:metadata\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (accountId, service)\n * @returns Account info or undefined if not found\n */\nexport async function getAccountInfo(store: Keyv, params: AccountKeyParams): Promise<AccountInfo | undefined> {\n const key = createAccountKey('metadata', params);\n return await store.get(key);\n}\n\n/**\n * Set account metadata (alias, lastUsed, etc).\n *\n * Key: {accountId}:{service}:metadata\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (accountId, service)\n * @param info - Account metadata to store\n */\nexport async function setAccountInfo(store: Keyv, params: AccountKeyParams, info: AccountInfo): Promise<void> {\n const key = createAccountKey('metadata', params);\n await store.set(key, info);\n}\n\n/**\n * Get OAuth token for an account.\n *\n * Key: {accountId}:{service}:token\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (accountId, service)\n * @returns Token or undefined if not found\n */\nexport async function getToken<T>(store: Keyv, params: AccountKeyParams): Promise<T | undefined> {\n const key = createAccountKey('token', params);\n return await store.get(key);\n}\n\n/**\n * Set OAuth token for an account.\n *\n * Key: {accountId}:{service}:token\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (accountId, service)\n * @param token - OAuth token data to store\n */\nexport async function setToken<T>(store: Keyv, params: AccountKeyParams, token: T): Promise<void> {\n const key = createAccountKey('token', params);\n await store.set(key, token);\n}\n"],"names":["addAccount","getAccountInfo","getActiveAccount","getLinkedAccounts","getToken","removeAccount","setAccountInfo","setActiveAccount","setToken","store","params","linked","linkedKey","active","service","includes","accountId","push","createServiceKey","set","tokenKey","infoKey","filtered","newActive","activeKey","createAccountKey","delete","filter","id","key","get","accounts","info","token"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/account-utils.ts"],"sourcesContent":["/**\n * Account management utilities for OAuth token storage\n *\n * Provides account lifecycle operations (add, remove, activate) and account data\n * access (tokens, metadata). Uses named parameters consistent with key-utils.ts.\n */\n\nimport type { Keyv } from 'keyv';\nimport { type AccountKeyParams, createAccountKey, createServiceKey, type ServiceKeyParams } from './key-utils.ts';\nimport type { AccountInfo } from './types.ts';\n\n// ============================================================================\n// Account Lifecycle Operations\n// ============================================================================\n\n/**\n * Add account to linked accounts list and set as active if first account.\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (service, accountId)\n *\n * @example\n * await addAccount(tokenStore, {\n * service: 'gmail',\n * accountId: 'alice@gmail.com'\n * });\n */\nexport async function addAccount(store: Keyv, params: AccountKeyParams): Promise<void> {\n const linked = await getLinkedAccounts(store, { service: params.service });\n\n if (!linked.includes(params.accountId)) {\n linked.push(params.accountId);\n const linkedKey = createServiceKey('linked', { service: params.service });\n await store.set(linkedKey, linked);\n }\n\n const active = await getActiveAccount(store, { service: params.service });\n if (!active) {\n await setActiveAccount(store, params);\n }\n}\n\n/**\n * Remove account: delete token, metadata, update linked list, and active account.\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (service, accountId)\n *\n * @example\n * await removeAccount(tokenStore, {\n * service: 'gmail',\n * accountId: 'alice@gmail.com'\n * });\n */\nexport async function removeAccount(store: Keyv, params: AccountKeyParams): Promise<void> {\n const tokenKey = createAccountKey('token', params);\n await store.delete(tokenKey);\n\n const infoKey = createAccountKey('metadata', params);\n await store.delete(infoKey);\n\n const linked = await getLinkedAccounts(store, { service: params.service });\n const filtered = linked.filter((id) => id !== params.accountId);\n const linkedKey = createServiceKey('linked', { service: params.service });\n await store.set(linkedKey, filtered);\n\n // Set new active account if we're removing the currently active one\n const active = await getActiveAccount(store, { service: params.service });\n if (active === params.accountId) {\n const newActive = filtered[0];\n if (newActive) {\n await setActiveAccount(store, { service: params.service, accountId: newActive });\n } else {\n const activeKey = createServiceKey('active', { service: params.service });\n await store.delete(activeKey);\n }\n }\n}\n\n// ============================================================================\n// Service-Scoped Account Operations\n// ============================================================================\n\n/**\n * Get active account ID for a service.\n *\n * Key: {service}:active\n *\n * @param store - Keyv storage instance\n * @param params - Service identification (service)\n * @returns Active account ID or undefined if none set\n */\nexport async function getActiveAccount(store: Keyv, params: ServiceKeyParams): Promise<string | undefined> {\n const key = createServiceKey('active', params);\n return await store.get(key);\n}\n\n/**\n * Set active account ID for a service.\n * Pass null as accountId to deactivate (clear active account).\n *\n * Key: {service}:active\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (service, accountId). Pass accountId: null to deactivate.\n */\nexport async function setActiveAccount(store: Keyv, params: AccountKeyParams | (ServiceKeyParams & { accountId: null })): Promise<void> {\n const key = createServiceKey('active', { service: params.service });\n if ('accountId' in params && params.accountId === null) {\n // accountId: null signals deactivation per API contract\n await store.delete(key);\n } else {\n await store.set(key, (params as AccountKeyParams).accountId);\n }\n}\n\n/**\n * Get list of linked account IDs for a service.\n *\n * Key: {service}:linked\n *\n * @param store - Keyv storage instance\n * @param params - Service identification (service)\n * @returns Array of account IDs (empty array if none)\n */\nexport async function getLinkedAccounts(store: Keyv, params: ServiceKeyParams): Promise<string[]> {\n const key = createServiceKey('linked', params);\n const accounts = await store.get(key);\n return accounts || [];\n}\n\n// ============================================================================\n// Account Data Operations\n// ============================================================================\n\n/**\n * Get account metadata (alias, lastUsed, etc).\n *\n * Key: {accountId}:{service}:metadata\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (accountId, service)\n * @returns Account info or undefined if not found\n */\nexport async function getAccountInfo(store: Keyv, params: AccountKeyParams): Promise<AccountInfo | undefined> {\n const key = createAccountKey('metadata', params);\n return await store.get(key);\n}\n\n/**\n * Set account metadata (alias, lastUsed, etc).\n *\n * Key: {accountId}:{service}:metadata\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (accountId, service)\n * @param info - Account metadata to store\n */\nexport async function setAccountInfo(store: Keyv, params: AccountKeyParams, info: AccountInfo): Promise<void> {\n const key = createAccountKey('metadata', params);\n await store.set(key, info);\n}\n\n/**\n * Get OAuth token for an account.\n *\n * Key: {accountId}:{service}:token\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (accountId, service)\n * @returns Token or undefined if not found\n */\nexport async function getToken<T>(store: Keyv, params: AccountKeyParams): Promise<T | undefined> {\n const key = createAccountKey('token', params);\n return await store.get(key);\n}\n\n/**\n * Set OAuth token for an account.\n *\n * Key: {accountId}:{service}:token\n *\n * @param store - Keyv storage instance\n * @param params - Account identification (accountId, service)\n * @param token - OAuth token data to store\n */\nexport async function setToken<T>(store: Keyv, params: AccountKeyParams, token: T): Promise<void> {\n const key = createAccountKey('token', params);\n await store.set(key, token);\n}\n"],"names":["addAccount","getAccountInfo","getActiveAccount","getLinkedAccounts","getToken","removeAccount","setAccountInfo","setActiveAccount","setToken","store","params","linked","linkedKey","active","service","includes","accountId","push","createServiceKey","set","tokenKey","infoKey","filtered","newActive","activeKey","createAccountKey","delete","filter","id","key","get","accounts","info","token"],"mappings":"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"}
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -15,5 +15,5 @@ export { sanitizeForLogging, sanitizeForLoggingFormatter } from './sanitizer.js'
|
|
|
15
15
|
export * as schemas from './schemas/index.js';
|
|
16
16
|
export { SessionUserAuth } from './session-auth.js';
|
|
17
17
|
export { getErrorTemplate, getSuccessTemplate } from './templates.js';
|
|
18
|
-
export type { AccountInfo, AuthEmailProvider, AuthFlowDescriptor, AuthMiddlewareWrapper, CachedToken, Credentials, JWTUserAuthConfig, Logger, McpPrompt, McpTool, OAuth2TokenStorageProvider, SessionUserAuthConfig, ToolConfig, ToolHandler, ToolModule, UserAuthProvider, } from './types.js';
|
|
18
|
+
export type { AccountAuthProvider, AccountInfo, AuthEmailProvider, AuthFlowDescriptor, AuthMiddlewareWrapper, CachedToken, Credentials, JWTUserAuthConfig, Logger, McpPrompt, McpTool, OAuth2TokenStorageProvider, SessionUserAuthConfig, ToolConfig, ToolHandler, ToolModule, UserAuthProvider, } from './types.js';
|
|
19
19
|
export { AccountManagerError, AccountNotFoundError, AuthRequiredError, ConfigurationError, RequiresAuthenticationError } from './types.js';
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -15,5 +15,5 @@ export { sanitizeForLogging, sanitizeForLoggingFormatter } from './sanitizer.js'
|
|
|
15
15
|
export * as schemas from './schemas/index.js';
|
|
16
16
|
export { SessionUserAuth } from './session-auth.js';
|
|
17
17
|
export { getErrorTemplate, getSuccessTemplate } from './templates.js';
|
|
18
|
-
export type { AccountInfo, AuthEmailProvider, AuthFlowDescriptor, AuthMiddlewareWrapper, CachedToken, Credentials, JWTUserAuthConfig, Logger, McpPrompt, McpTool, OAuth2TokenStorageProvider, SessionUserAuthConfig, ToolConfig, ToolHandler, ToolModule, UserAuthProvider, } from './types.js';
|
|
18
|
+
export type { AccountAuthProvider, AccountInfo, AuthEmailProvider, AuthFlowDescriptor, AuthMiddlewareWrapper, CachedToken, Credentials, JWTUserAuthConfig, Logger, McpPrompt, McpTool, OAuth2TokenStorageProvider, SessionUserAuthConfig, ToolConfig, ToolHandler, ToolModule, UserAuthProvider, } from './types.js';
|
|
19
19
|
export { AccountManagerError, AccountNotFoundError, AuthRequiredError, ConfigurationError, RequiresAuthenticationError } from './types.js';
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/index.ts"],"sourcesContent":["/**\n * @mcp-z/oauth - Multi-account OAuth orchestration and secure token storage for MCP servers\n *\n * Provides account management functions, account tools generation, and secure logging utilities.\n * Designed to work with any storage backend (file, Redis, DuckDB) via Keyv interface.\n */\n\n// Core account management - Public API\n// Internal functions - For provider implementations and testing\nexport { addAccount, getActiveAccount, getToken, removeAccount, setAccountInfo, setActiveAccount, setToken } from './account-utils.ts';\n// Auth classes - For multi-tenant testing\nexport { JWTUserAuth } from './jwt-auth.ts';\nexport { type AccountKeyParams, type AccountKeyType, createAccountKey, createServiceKey, listAccountIds, type ServiceKeyParams, type ServiceKeyType } from './key-utils.ts';\n// Account server and factory functions - Public API\nexport { type AccountLoopbackConfig, AccountServer, type AccountStatelessConfig, createLoopback, createStateless } from './lib/account-server/index.ts';\n// DCR types - Public API\nexport type { DcrClientInformation, DcrClientMetadata, DcrConfig, DcrErrorResponse, ProviderTokens } from './lib/dcr-types.ts';\n// RFC Metadata Types - Public API\nexport type { RFC8414Metadata, RFC9728Metadata } from './lib/rfc-metadata-types.ts';\nexport { generatePKCE, type PKCEPair } from './pkce.ts';\n// Logging utilities - Public API\nexport { sanitizeForLogging, sanitizeForLoggingFormatter } from './sanitizer.ts';\n// Schemas\nexport * as schemas from './schemas/index.ts';\nexport { SessionUserAuth } from './session-auth.ts';\nexport { getErrorTemplate, getSuccessTemplate } from './templates.ts';\n\n// Public types - core interfaces that consumers use\nexport type {\n // Provider interfaces\n AccountAuthProvider,\n // Account management types\n AccountInfo,\n AuthEmailProvider,\n AuthFlowDescriptor,\n AuthMiddlewareWrapper,\n CachedToken,\n Credentials,\n // Auth config types\n JWTUserAuthConfig,\n // Utility types\n Logger,\n McpPrompt,\n McpTool,\n OAuth2TokenStorageProvider,\n SessionUserAuthConfig,\n ToolConfig,\n ToolHandler,\n ToolModule,\n UserAuthProvider,\n} from './types.ts';\n\n// Public error classes\nexport { AccountManagerError, AccountNotFoundError, AuthRequiredError, ConfigurationError, RequiresAuthenticationError } from './types.ts';\n"],"names":["AccountManagerError","AccountNotFoundError","AccountServer","AuthRequiredError","ConfigurationError","JWTUserAuth","RequiresAuthenticationError","SessionUserAuth","addAccount","createAccountKey","createLoopback","createServiceKey","createStateless","generatePKCE","getActiveAccount","getErrorTemplate","getSuccessTemplate","getToken","listAccountIds","removeAccount","sanitizeForLogging","sanitizeForLoggingFormatter","schemas","setAccountInfo","setActiveAccount","setToken"],"mappings":"AAAA;;;;;CAKC,GAED,uCAAuC;AACvC,gEAAgE;;;;;;;;;;;;QA6CvDA;eAAAA,4BAAmB;;QAAEC;eAAAA,6BAAoB;;QAvCbC;eAAAA,sBAAa;;QAuCEC;eAAAA,0BAAiB;;QAAEC;eAAAA,2BAAkB;;QA1ChFC;eAAAA,sBAAW;;QA0CuEC;eAAAA,oCAA2B;;QA7B7GC;eAAAA,8BAAe;;QAffC;eAAAA,0BAAU;;QAGkCC;eAAAA,4BAAgB;;QAEYC;eAAAA,uBAAc;;QAFxBC;eAAAA,4BAAgB;;QAEUC;eAAAA,wBAAe;;QAKvGC;eAAAA,oBAAY;;QAVAC;eAAAA,gCAAgB;;QAgB5BC;eAAAA,6BAAgB;;QAAEC;eAAAA,+BAAkB;;QAhBNC;eAAAA,wBAAQ;;QAG0CC;eAAAA,0BAAc;;QAHtDC;eAAAA,6BAAa;;QAYrDC;eAAAA,+BAAkB;;QAAEC;eAAAA,wCAA2B;;QAE5CC;;;QAdoDC;eAAAA,8BAAc;;QAAEC;eAAAA,gCAAgB;;QAAEC;eAAAA,wBAAQ;;;8BAAQ;yBAEtF;0BAC+H;uBAEnC;sBAK5E;2BAEoB;gEAEvC;6BACO;2BACqB;uBA4ByE"}
|
package/dist/cjs/jwt-auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth/src/jwt-auth.ts"],"sourcesContent":["/**\n * JWT-based user authentication for multi-tenant deployments\n *\n * Extracts user ID from JWT tokens with signature and claims verification.\n * Supports HS256, RS256, ES256 algorithms via JOSE library.\n */\n\nimport { createRemoteJWKSet, importSPKI, type JWK, type JWTPayload, type JWTVerifyOptions, type JWTVerifyResult, jwtVerify } from 'jose';\nimport type { JWTUserAuthConfig, UserAuthProvider } from './types.ts';\n\n/**\n * HTTP request interface (subset needed for JWT auth)\n */\ninterface HttpRequest {\n headers?: {\n authorization?: string;\n };\n}\n\n/**\n * JWT-based user authentication provider\n *\n * Verifies JWT tokens and extracts user IDs from claims.\n * Use for multi-tenant deployments where users authenticate via JWT.\n *\n * @example\n * ```typescript\n * // HS256 with shared secret\n * const userAuth = new JWTUserAuth({\n * secret: process.env.JWT_SECRET!,\n * issuer: 'https://auth.example.com',\n * audience: 'api.example.com',\n * });\n *\n * // RS256 with public key\n * const userAuth = new JWTUserAuth({\n * publicKey: process.env.JWT_PUBLIC_KEY!,\n * issuer: 'https://auth.example.com',\n * });\n *\n * // RS256 with JWKS URL (dynamic key rotation)\n * const userAuth = new JWTUserAuth({\n * jwksUrl: 'https://auth.example.com/.well-known/jwks.json',\n * issuer: 'https://auth.example.com',\n * audience: 'api.example.com',\n * });\n * ```\n */\nexport class JWTUserAuth implements UserAuthProvider {\n private readonly config: {\n secret?: string;\n publicKey?: string | JWK;\n jwksUrl?: string;\n issuer?: string | string[];\n audience?: string | string[];\n userIdClaim: string;\n algorithms: string[];\n clockTolerance: number;\n };\n private readonly remoteJWKSet?: ReturnType<typeof createRemoteJWKSet>;\n\n constructor(config: JWTUserAuthConfig) {\n // Validate configuration\n if (!config.secret && !config.publicKey && !config.jwksUrl) {\n throw new Error('JWTUserAuth: Must provide one of: secret (HS256), publicKey (RS256/ES256), or jwksUrl');\n }\n\n if (config.secret && config.secret.length < 32) {\n throw new Error('JWTUserAuth: secret must be at least 32 characters for HS256');\n }\n\n if ((config.secret ? 1 : 0) + (config.publicKey ? 1 : 0) + (config.jwksUrl ? 1 : 0) > 1) {\n throw new Error('JWTUserAuth: Provide only one of: secret, publicKey, or jwksUrl');\n }\n\n // Store configuration with defaults\n this.config = {\n ...(config.secret !== undefined && { secret: config.secret }),\n ...(config.publicKey !== undefined && { publicKey: config.publicKey }),\n ...(config.jwksUrl !== undefined && { jwksUrl: config.jwksUrl }),\n ...(config.issuer !== undefined && { issuer: config.issuer }),\n ...(config.audience !== undefined && { audience: config.audience }),\n userIdClaim: config.userIdClaim ?? 'sub',\n algorithms: config.algorithms ?? [],\n clockTolerance: config.clockTolerance ?? 0,\n };\n\n // Create remote JWK set if using JWKS URL\n if (config.jwksUrl) {\n this.remoteJWKSet = createRemoteJWKSet(new URL(config.jwksUrl));\n }\n }\n\n /**\n * Extract and verify user ID from JWT token\n *\n * @param req - HTTP request object with Authorization header\n * @returns User ID from verified JWT claims\n * @throws Error if token missing, invalid, expired, or claims invalid\n */\n async getUserId(req: unknown): Promise<string> {\n const httpReq = req as HttpRequest;\n\n // Extract Authorization header\n const authHeader = httpReq.headers?.authorization;\n if (!authHeader) {\n throw new Error('JWTUserAuth: No Authorization header found');\n }\n\n // Parse Bearer token\n const match = /^Bearer\\s+(.+)$/i.exec(authHeader.trim());\n if (!match) {\n throw new Error('JWTUserAuth: Invalid Authorization header format (expected \"Bearer <token>\")');\n }\n\n const token = match[1];\n if (!token) {\n throw new Error('JWTUserAuth: Empty JWT token');\n }\n\n // Verify JWT and extract payload\n const payload = await this.verifyToken(token);\n\n // Extract user ID from configured claim\n const userId = payload[this.config.userIdClaim];\n if (!userId || typeof userId !== 'string') {\n throw new Error(`JWTUserAuth: JWT missing or invalid '${this.config.userIdClaim}' claim`);\n }\n\n return userId;\n }\n\n /**\n * Verify JWT signature and claims\n */\n private async verifyToken(token: string): Promise<JWTPayload> {\n try {\n // Build verification options\n const options: JWTVerifyOptions = {\n ...(this.config.issuer && { issuer: this.config.issuer }),\n ...(this.config.audience && { audience: this.config.audience }),\n ...(this.config.clockTolerance && { clockTolerance: this.config.clockTolerance }),\n };\n\n // Verify with appropriate key type\n let result: JWTVerifyResult;\n\n if (this.config.secret) {\n // HS256 verification with shared secret\n const secret = new TextEncoder().encode(this.config.secret);\n result = await jwtVerify(token, secret, {\n ...options,\n algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : ['HS256'],\n });\n } else if (this.remoteJWKSet) {\n // RS256/ES256 verification with remote JWKS\n result = await jwtVerify(token, this.remoteJWKSet, {\n ...options,\n algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : ['RS256', 'ES256'],\n });\n } else if (this.config.publicKey) {\n // RS256/ES256 verification with provided public key\n // If string (PEM), import it first; if JWK, use directly\n const key = typeof this.config.publicKey === 'string' ? await importSPKI(this.config.publicKey, 'RS256') : this.config.publicKey;\n\n result = await jwtVerify(token, key, {\n ...options,\n algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : ['RS256', 'ES256'],\n });\n } else {\n throw new Error('JWTUserAuth: No verification key configured');\n }\n\n return result.payload;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`JWTUserAuth: JWT verification failed: ${error.message}`);\n }\n throw new Error('JWTUserAuth: JWT verification failed');\n }\n }\n}\n"],"names":["JWTUserAuth","config","secret","publicKey","jwksUrl","Error","length","undefined","issuer","audience","userIdClaim","algorithms","clockTolerance","remoteJWKSet","createRemoteJWKSet","URL","getUserId","req","httpReq","authHeader","match","token","payload","userId","headers","authorization","exec","trim","verifyToken","options","result","key","error","TextEncoder","encode","jwtVerify","importSPKI","message"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/jwt-auth.ts"],"sourcesContent":["/**\n * JWT-based user authentication for multi-tenant deployments\n *\n * Extracts user ID from JWT tokens with signature and claims verification.\n * Supports HS256, RS256, ES256 algorithms via JOSE library.\n */\n\nimport { createRemoteJWKSet, importSPKI, type JWK, type JWTPayload, type JWTVerifyOptions, type JWTVerifyResult, jwtVerify } from 'jose';\nimport type { JWTUserAuthConfig, UserAuthProvider } from './types.ts';\n\n/**\n * HTTP request interface (subset needed for JWT auth)\n */\ninterface HttpRequest {\n headers?: {\n authorization?: string;\n };\n}\n\n/**\n * JWT-based user authentication provider\n *\n * Verifies JWT tokens and extracts user IDs from claims.\n * Use for multi-tenant deployments where users authenticate via JWT.\n *\n * @example\n * ```typescript\n * // HS256 with shared secret\n * const userAuth = new JWTUserAuth({\n * secret: process.env.JWT_SECRET!,\n * issuer: 'https://auth.example.com',\n * audience: 'api.example.com',\n * });\n *\n * // RS256 with public key\n * const userAuth = new JWTUserAuth({\n * publicKey: process.env.JWT_PUBLIC_KEY!,\n * issuer: 'https://auth.example.com',\n * });\n *\n * // RS256 with JWKS URL (dynamic key rotation)\n * const userAuth = new JWTUserAuth({\n * jwksUrl: 'https://auth.example.com/.well-known/jwks.json',\n * issuer: 'https://auth.example.com',\n * audience: 'api.example.com',\n * });\n * ```\n */\nexport class JWTUserAuth implements UserAuthProvider {\n private readonly config: {\n secret?: string;\n publicKey?: string | JWK;\n jwksUrl?: string;\n issuer?: string | string[];\n audience?: string | string[];\n userIdClaim: string;\n algorithms: string[];\n clockTolerance: number;\n };\n private readonly remoteJWKSet?: ReturnType<typeof createRemoteJWKSet>;\n\n constructor(config: JWTUserAuthConfig) {\n // Validate configuration\n if (!config.secret && !config.publicKey && !config.jwksUrl) {\n throw new Error('JWTUserAuth: Must provide one of: secret (HS256), publicKey (RS256/ES256), or jwksUrl');\n }\n\n if (config.secret && config.secret.length < 32) {\n throw new Error('JWTUserAuth: secret must be at least 32 characters for HS256');\n }\n\n if ((config.secret ? 1 : 0) + (config.publicKey ? 1 : 0) + (config.jwksUrl ? 1 : 0) > 1) {\n throw new Error('JWTUserAuth: Provide only one of: secret, publicKey, or jwksUrl');\n }\n\n // Store configuration with defaults\n this.config = {\n ...(config.secret !== undefined && { secret: config.secret }),\n ...(config.publicKey !== undefined && { publicKey: config.publicKey }),\n ...(config.jwksUrl !== undefined && { jwksUrl: config.jwksUrl }),\n ...(config.issuer !== undefined && { issuer: config.issuer }),\n ...(config.audience !== undefined && { audience: config.audience }),\n userIdClaim: config.userIdClaim ?? 'sub',\n algorithms: config.algorithms ?? [],\n clockTolerance: config.clockTolerance ?? 0,\n };\n\n // Create remote JWK set if using JWKS URL\n if (config.jwksUrl) {\n this.remoteJWKSet = createRemoteJWKSet(new URL(config.jwksUrl));\n }\n }\n\n /**\n * Extract and verify user ID from JWT token\n *\n * @param req - HTTP request object with Authorization header\n * @returns User ID from verified JWT claims\n * @throws Error if token missing, invalid, expired, or claims invalid\n */\n async getUserId(req: unknown): Promise<string> {\n const httpReq = req as HttpRequest;\n\n // Extract Authorization header\n const authHeader = httpReq.headers?.authorization;\n if (!authHeader) {\n throw new Error('JWTUserAuth: No Authorization header found');\n }\n\n // Parse Bearer token\n const match = /^Bearer\\s+(.+)$/i.exec(authHeader.trim());\n if (!match) {\n throw new Error('JWTUserAuth: Invalid Authorization header format (expected \"Bearer <token>\")');\n }\n\n const token = match[1];\n if (!token) {\n throw new Error('JWTUserAuth: Empty JWT token');\n }\n\n // Verify JWT and extract payload\n const payload = await this.verifyToken(token);\n\n // Extract user ID from configured claim\n const userId = payload[this.config.userIdClaim];\n if (!userId || typeof userId !== 'string') {\n throw new Error(`JWTUserAuth: JWT missing or invalid '${this.config.userIdClaim}' claim`);\n }\n\n return userId;\n }\n\n /**\n * Verify JWT signature and claims\n */\n private async verifyToken(token: string): Promise<JWTPayload> {\n try {\n // Build verification options\n const options: JWTVerifyOptions = {\n ...(this.config.issuer && { issuer: this.config.issuer }),\n ...(this.config.audience && { audience: this.config.audience }),\n ...(this.config.clockTolerance && { clockTolerance: this.config.clockTolerance }),\n };\n\n // Verify with appropriate key type\n let result: JWTVerifyResult;\n\n if (this.config.secret) {\n // HS256 verification with shared secret\n const secret = new TextEncoder().encode(this.config.secret);\n result = await jwtVerify(token, secret, {\n ...options,\n algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : ['HS256'],\n });\n } else if (this.remoteJWKSet) {\n // RS256/ES256 verification with remote JWKS\n result = await jwtVerify(token, this.remoteJWKSet, {\n ...options,\n algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : ['RS256', 'ES256'],\n });\n } else if (this.config.publicKey) {\n // RS256/ES256 verification with provided public key\n // If string (PEM), import it first; if JWK, use directly\n const key = typeof this.config.publicKey === 'string' ? await importSPKI(this.config.publicKey, 'RS256') : this.config.publicKey;\n\n result = await jwtVerify(token, key, {\n ...options,\n algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : ['RS256', 'ES256'],\n });\n } else {\n throw new Error('JWTUserAuth: No verification key configured');\n }\n\n return result.payload;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`JWTUserAuth: JWT verification failed: ${error.message}`);\n }\n throw new Error('JWTUserAuth: JWT verification failed');\n }\n }\n}\n"],"names":["JWTUserAuth","config","secret","publicKey","jwksUrl","Error","length","undefined","issuer","audience","userIdClaim","algorithms","clockTolerance","remoteJWKSet","createRemoteJWKSet","URL","getUserId","req","httpReq","authHeader","match","token","payload","userId","headers","authorization","exec","trim","verifyToken","options","result","key","error","TextEncoder","encode","jwtVerify","importSPKI","message"],"mappings":"AAAA;;;;;CAKC;;;;+BA2CYA;;;eAAAA;;;oBAzCqH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyC3H,IAAA,AAAMA,4BAAN;;aAAMA,YAaCC,MAAyB;gCAb1BD;YAkCMC,qBACDA,oBACIA;QAtBlB,yBAAyB;QACzB,IAAI,CAACA,OAAOC,MAAM,IAAI,CAACD,OAAOE,SAAS,IAAI,CAACF,OAAOG,OAAO,EAAE;YAC1D,MAAM,IAAIC,MAAM;QAClB;QAEA,IAAIJ,OAAOC,MAAM,IAAID,OAAOC,MAAM,CAACI,MAAM,GAAG,IAAI;YAC9C,MAAM,IAAID,MAAM;QAClB;QAEA,IAAI,AAACJ,CAAAA,OAAOC,MAAM,GAAG,IAAI,CAAA,IAAMD,CAAAA,OAAOE,SAAS,GAAG,IAAI,CAAA,IAAMF,CAAAA,OAAOG,OAAO,GAAG,IAAI,CAAA,IAAK,GAAG;YACvF,MAAM,IAAIC,MAAM;QAClB;QAEA,oCAAoC;QACpC,IAAI,CAACJ,MAAM,GAAG,wCACRA,OAAOC,MAAM,KAAKK,aAAa;YAAEL,QAAQD,OAAOC,MAAM;QAAC,GACvDD,OAAOE,SAAS,KAAKI,aAAa;YAAEJ,WAAWF,OAAOE,SAAS;QAAC,GAChEF,OAAOG,OAAO,KAAKG,aAAa;YAAEH,SAASH,OAAOG,OAAO;QAAC,GAC1DH,OAAOO,MAAM,KAAKD,aAAa;YAAEC,QAAQP,OAAOO,MAAM;QAAC,GACvDP,OAAOQ,QAAQ,KAAKF,aAAa;YAAEE,UAAUR,OAAOQ,QAAQ;QAAC;YACjEC,WAAW,GAAET,sBAAAA,OAAOS,WAAW,cAAlBT,iCAAAA,sBAAsB;YACnCU,UAAU,GAAEV,qBAAAA,OAAOU,UAAU,cAAjBV,gCAAAA,qBAAqB,EAAE;YACnCW,cAAc,GAAEX,yBAAAA,OAAOW,cAAc,cAArBX,oCAAAA,yBAAyB;;QAG3C,0CAA0C;QAC1C,IAAIA,OAAOG,OAAO,EAAE;YAClB,IAAI,CAACS,YAAY,GAAGC,IAAAA,wBAAkB,EAAC,IAAIC,IAAId,OAAOG,OAAO;QAC/D;;iBA1CSJ;IA6CX;;;;;;GAMC,GACD,OAAMgB,SA8BL,GA9BD,SAAMA,UAAUC,GAAY;;gBAIPC,kBAHbA,SAGAC,YAMAC,OAKAC,OAMAC,SAGAC;;;;wBAvBAL,UAAUD;wBAEhB,+BAA+B;wBACzBE,cAAaD,mBAAAA,QAAQM,OAAO,cAAfN,uCAAAA,iBAAiBO,aAAa;wBACjD,IAAI,CAACN,YAAY;4BACf,MAAM,IAAId,MAAM;wBAClB;wBAEA,qBAAqB;wBACfe,QAAQ,mBAAmBM,IAAI,CAACP,WAAWQ,IAAI;wBACrD,IAAI,CAACP,OAAO;4BACV,MAAM,IAAIf,MAAM;wBAClB;wBAEMgB,QAAQD,KAAK,CAAC,EAAE;wBACtB,IAAI,CAACC,OAAO;4BACV,MAAM,IAAIhB,MAAM;wBAClB;wBAGgB;;4BAAM,IAAI,CAACuB,WAAW,CAACP;;;wBAAjCC,UAAU;wBAEhB,wCAAwC;wBAClCC,SAASD,OAAO,CAAC,IAAI,CAACrB,MAAM,CAACS,WAAW,CAAC;wBAC/C,IAAI,CAACa,UAAU,OAAOA,WAAW,UAAU;4BACzC,MAAM,IAAIlB,MAAM,AAAC,wCAA+D,OAAxB,IAAI,CAACJ,MAAM,CAACS,WAAW,EAAC;wBAClF;wBAEA;;4BAAOa;;;;QACT;;IAEA;;GAEC,GACD,OAAcK,WA6Cb,GA7CD,SAAcA,YAAYP,KAAa;;gBAG7BQ,SAOFC,QAII5B,QAcA6B,WAWDC;;;;;;;;;;wBArCP,6BAA6B;wBACvBH,UAA4B,mBAC5B,IAAI,CAAC5B,MAAM,CAACO,MAAM,IAAI;4BAAEA,QAAQ,IAAI,CAACP,MAAM,CAACO,MAAM;wBAAC,GACnD,IAAI,CAACP,MAAM,CAACQ,QAAQ,IAAI;4BAAEA,UAAU,IAAI,CAACR,MAAM,CAACQ,QAAQ;wBAAC,GACzD,IAAI,CAACR,MAAM,CAACW,cAAc,IAAI;4BAAEA,gBAAgB,IAAI,CAACX,MAAM,CAACW,cAAc;wBAAC;6BAM7E,IAAI,CAACX,MAAM,CAACC,MAAM,EAAlB;;;;wBACF,wCAAwC;wBAClCA,SAAS,IAAI+B,cAAcC,MAAM,CAAC,IAAI,CAACjC,MAAM,CAACC,MAAM;wBACjD;;4BAAMiC,IAAAA,eAAS,EAACd,OAAOnB,QAAQ,wCACnC2B;gCACHlB,YAAY,IAAI,CAACV,MAAM,CAACU,UAAU,CAACL,MAAM,GAAG,IAAI,IAAI,CAACL,MAAM,CAACU,UAAU;oCAAI;;;;;wBAF5EmB,SAAS;;;;;;6BAIA,IAAI,CAACjB,YAAY,EAAjB;;;;wBAEA;;4BAAMsB,IAAAA,eAAS,EAACd,OAAO,IAAI,CAACR,YAAY,EAAE,wCAC9CgB;gCACHlB,YAAY,IAAI,CAACV,MAAM,CAACU,UAAU,CAACL,MAAM,GAAG,IAAI,IAAI,CAACL,MAAM,CAACU,UAAU;oCAAI;oCAAS;;;;;wBAHrF,4CAA4C;wBAC5CmB,SAAS;;;;;;6BAIA,IAAI,CAAC7B,MAAM,CAACE,SAAS,EAArB;;;;6BAGG,CAAA,OAAO,IAAI,CAACF,MAAM,CAACE,SAAS,KAAK,QAAO,GAAxC;;;;wBAA4C;;4BAAMiC,IAAAA,gBAAU,EAAC,IAAI,CAACnC,MAAM,CAACE,SAAS,EAAE;;;+BAAxC;;;;;;+BAAmD,IAAI,CAACF,MAAM,CAACE,SAAS;;;wBAA1H4B;wBAEG;;4BAAMI,IAAAA,eAAS,EAACd,OAAOU,KAAK,wCAChCF;gCACHlB,YAAY,IAAI,CAACV,MAAM,CAACU,UAAU,CAACL,MAAM,GAAG,IAAI,IAAI,CAACL,MAAM,CAACU,UAAU;oCAAI;oCAAS;;;;;wBAFrFmB,SAAS;;;;;;wBAKT,MAAM,IAAIzB,MAAM;;wBAGlB;;4BAAOyB,OAAOR,OAAO;;;wBACdU;wBACP,IAAIA,AAAK,YAALA,OAAiB3B,QAAO;4BAC1B,MAAM,IAAIA,MAAM,AAAC,yCAAsD,OAAd2B,MAAMK,OAAO;wBACxE;wBACA,MAAM,IAAIhC,MAAM;;;;;;;QAEpB;;WApIWL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/key-utils.ts"],"sourcesContent":["/**\n * Key generation utilities for consistent storage key format\n *\n * Key format: {accountId}:{service}:{type}\n * Example: work@gmail.com:gmail:token\n */\n\nimport type { Keyv } from 'keyv';\n\n/**\n * Key types for account-scoped data (requires accountId)\n */\nexport type AccountKeyType = 'token' | 'metadata' | 'dcr-client';\n\n/**\n * Key types for service-scoped data (no accountId)\n */\nexport type ServiceKeyType = 'active' | 'linked';\n\n/**\n * Parameters for account-scoped keys.\n * All fields are required - no silent defaults.\n */\nexport interface AccountKeyParams {\n /** Account identifier - typically an email address */\n accountId: string;\n\n /** Service name (e.g., 'gmail', 'sheets', 'drive', 'outlook') */\n service: string;\n}\n\n/**\n * Parameters for service-scoped keys.\n * These keys don't include accountId.\n */\nexport interface ServiceKeyParams {\n /** Service name */\n service: string;\n}\n\n/**\n * Validate key parameters don't contain colon delimiter\n */\nfunction validateKeyParams(params: AccountKeyParams | ServiceKeyParams): void {\n for (const [key, value] of Object.entries(params)) {\n if (typeof value !== 'string') {\n throw new Error(`Key parameter '${key}' must be a string, got: ${typeof value}`);\n }\n if (value.includes(':')) {\n throw new Error(`Key parameter '${key}' cannot contain colon character: ${value}`);\n }\n }\n}\n\n/**\n * Create account-scoped storage key.\n *\n * These keys are scoped to a specific account (email address) and store\n * account-specific data like OAuth tokens and account metadata.\n *\n * @param type - Key type ('token' for OAuth tokens, 'metadata' for account details, 'dcr-client' for DCR registration)\n * @param params - Account key parameters with explicit field names\n * @returns Storage key in format: \"{accountId}:{service}:{type}\"\n *\n * @example\n * ```typescript\n * // Store OAuth token\n * const tokenKey = createAccountKey('token', {\n * accountId: 'alice@gmail.com',\n * service: 'gmail'\n * });\n * // Returns: \"alice@gmail.com:gmail:token\"\n *\n * // Store account metadata (alias, timestamps, profile)\n * const metadataKey = createAccountKey('metadata', {\n * accountId: 'alice@gmail.com',\n * service: 'gmail'\n * });\n * // Returns: \"alice@gmail.com:gmail:metadata\"\n *\n * // Store DCR client registration info\n * const dcrKey = createAccountKey('dcr-client', {\n * accountId: 'alice@outlook.com',\n * service: 'outlook'\n * });\n * // Returns: \"alice@outlook.com:outlook:dcr-client\"\n * ```\n */\nexport function createAccountKey(type: AccountKeyType, params: AccountKeyParams): string {\n validateKeyParams(params);\n return `${params.accountId}:${params.service}:${type}`;\n}\n\n/**\n * Create service-scoped storage key.\n *\n * These keys are scoped to a service (not a specific account) and store\n * service-level data like which account is active or the list of linked accounts.\n *\n * @param type - Key type ('active' for active account, 'linked' for account list)\n * @param params - Service key parameters\n * @returns Storage key in format: \"{service}:{type}\"\n *\n * @example\n * ```typescript\n * // Track active account\n * const activeKey = createServiceKey('active', {\n * service: 'gmail'\n * });\n * // Returns: \"gmail:active\"\n *\n * // Store list of linked accounts\n * const linkedKey = createServiceKey('linked', {\n * service: 'gmail'\n * });\n * // Returns: \"gmail:linked\"\n * ```\n */\nexport function createServiceKey(type: ServiceKeyType, params: ServiceKeyParams): string {\n validateKeyParams(params);\n return `${params.service}:${type}`;\n}\n\n/**\n * Parse token key to extract components\n *\n * @param key - Storage key to parse\n * @returns Object with accountId and service, or undefined if invalid format\n *\n * @example\n * const parsed = parseTokenKey('user@gmail.com:gmail:token');\n * // Returns: { accountId: 'user@gmail.com', service: 'gmail' }\n *\n * const invalid = parseTokenKey('invalid-key');\n * // Returns: undefined\n */\nexport function parseTokenKey(key: string): { accountId: string; service: string } | undefined {\n const parts = key.split(':');\n if (parts.length !== 3 || parts[2] !== 'token' || !parts[0] || !parts[1]) {\n return undefined;\n }\n return {\n accountId: parts[0],\n service: parts[1],\n };\n}\n\n/**\n * List all account IDs for a service\n *\n * Iterates token keys and returns all accountIds that match the service.\n * Encapsulates key format details for forward compatibility.\n *\n * @param store - Keyv store to iterate\n * @param service - Service name\n * @returns Array of account IDs (e.g., email addresses)\n *\n * @example\n * const accounts = await listAccountIds(store, 'gmail');\n * // Returns: ['alice@gmail.com', 'bob@gmail.com']\n *\n * @example\n * // Empty array if no accounts found\n * const empty = await listAccountIds(store, 'unknown-service');\n * // Returns: []\n */\nexport async function listAccountIds(store: Keyv, service: string): Promise<string[]> {\n const accountIds: string[] = [];\n\n try {\n const iterator = store.iterator?.(undefined);\n if (!iterator) {\n return accountIds;\n }\n\n for await (const [key] of iterator) {\n const parsed = parseTokenKey(key);\n if (parsed && parsed.service === service) {\n accountIds.push(parsed.accountId);\n }\n }\n } catch (_error) {\n // If iteration fails, return empty array (fail gracefully)\n // This handles stores that don't support iteration\n return accountIds;\n }\n\n return accountIds;\n}\n"],"names":["createAccountKey","createServiceKey","listAccountIds","parseTokenKey","validateKeyParams","params","Object","entries","key","value","Error","includes","type","accountId","service","parts","split","length","undefined","store","accountIds","iterator","parsed","_error","push"],"mappings":"AAAA;;;;;CAKC;;;;;;;;;;;QAmFeA;eAAAA;;QA8BAC;eAAAA;;QAgDMC;eAAAA;;QA9BNC;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAhGhB;;CAEC,GACD,SAASC,kBAAkBC,MAA2C;QAC/D,kCAAA,2BAAA;;QAAL,QAAK,YAAsBC,OAAOC,OAAO,CAACF,4BAArC,SAAA,6BAAA,QAAA,yBAAA,iCAA8C;YAA9C,mCAAA,iBAAOG,sBAAKC;YACf,IAAI,OAAOA,UAAU,UAAU;gBAC7B,MAAM,IAAIC,MAAM,AAAC,kBAAgD,OAA/BF,KAAI,6BAAwC,OAAb,OAAOC,sCAAP,SAAOA;YAC1E;YACA,IAAIA,MAAME,QAAQ,CAAC,MAAM;gBACvB,MAAM,IAAID,MAAM,AAAC,kBAAyDD,OAAxCD,KAAI,sCAA0C,OAANC;YAC5E;QACF;;QAPK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;AAQP;AAoCO,SAAST,iBAAiBY,IAAoB,EAAEP,MAAwB;IAC7ED,kBAAkBC;IAClB,OAAO,AAAC,GAAsBA,OAApBA,OAAOQ,SAAS,EAAC,KAAqBD,OAAlBP,OAAOS,OAAO,EAAC,KAAQ,OAALF;AAClD;AA2BO,SAASX,iBAAiBW,IAAoB,EAAEP,MAAwB;IAC7ED,kBAAkBC;IAClB,OAAO,AAAC,GAAoBO,OAAlBP,OAAOS,OAAO,EAAC,KAAQ,OAALF;AAC9B;AAeO,SAAST,cAAcK,GAAW;IACvC,IAAMO,QAAQP,IAAIQ,KAAK,CAAC;IACxB,IAAID,MAAME,MAAM,KAAK,KAAKF,KAAK,CAAC,EAAE,KAAK,WAAW,CAACA,KAAK,CAAC,EAAE,IAAI,CAACA,KAAK,CAAC,EAAE,EAAE;QACxE,OAAOG;IACT;IACA,OAAO;QACLL,WAAWE,KAAK,CAAC,EAAE;QACnBD,SAASC,KAAK,CAAC,EAAE;IACnB;AACF;AAqBO,SAAeb,eAAeiB,KAAW,EAAEL,OAAe;;YACzDM,YAGaD,iBAAXE,2GAKYb,KACVc,aAKDC;;;;oBAdHH;;;;;;;;;oBAGEC,YAAWF,kBAAAA,MAAME,QAAQ,cAAdF,sCAAAA,qBAAAA,OAAiBD;oBAClC,IAAI,CAACG,UAAU;wBACb;;4BAAOD;;oBACT;;;;;;;;;;gDAE0BC;;;;;;;;;;;;;2DAARb;oBACVc,SAASnB,cAAcK;oBAC7B,IAAIc,UAAUA,OAAOR,OAAO,KAAKA,SAAS;wBACxCM,WAAWI,IAAI,CAACF,OAAOT,SAAS;oBAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAEKU;oBACP,2DAA2D;oBAC3D,mDAAmD;oBACnD;;wBAAOH;;;oBAGT;;wBAAOA;;;;IACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/lib/account-server/index.ts"],"sourcesContent":["/**\n * Unified account management API for MCP servers.\n *\n * Provides two account management modes:\n * - Loopback: Server-managed multi-account OAuth (LoopbackOAuthProvider)\n * - Stateless: MCP client-managed OAuth (read-only status)\n *\n * @example\n * // Loopback OAuth account management\n * const {tools, prompts} = AccountServer.createLoopback({\n * service: 'gmail',\n * store: tokenStore,\n * logger,\n * auth: authProvider\n * });\n *\n * @example\n * // Stateless mode (MCP OAuth)\n * const {tools, prompts} = AccountServer.createStateless({\n * service: 'gmail'\n * });\n */\n\nimport { createLoopback } from './loopback.ts';\nimport { createStateless } from './stateless.ts';\n\nexport const AccountServer = {\n /**\n * Create loopback OAuth account management tools.\n * Server manages multiple accounts with stored tokens (LoopbackOAuthProvider).\n * Returns 4 tools: account-me, account-switch, account-remove, account-list.\n * No prompts.\n */\n createLoopback,\n\n /**\n * Create stateless mode tools.\n * MCP client manages authentication. Server extracts user identity from bearer token.\n * Returns 1 tool: account-me.\n * No prompts.\n */\n createStateless,\n};\n\nexport { createLoopback } from './loopback.ts';\nexport { createAccountMe } from './me.ts';\nexport { findAccountByEmailOrAlias } from './shared-utils.ts';\nexport { createStateless } from './stateless.ts';\nexport type { AccountLoopbackConfig, AccountMeConfig, AccountStatelessConfig } from './types.ts';\n"],"names":["AccountServer","createAccountMe","createLoopback","createStateless","findAccountByEmailOrAlias"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;CAqBC;;;;;;;;;;;QAKYA;eAAAA;;QAmBJC;eAAAA,qBAAe;;QADfC;eAAAA,0BAAc;;QAGdC;eAAAA,4BAAe;;QADfC;eAAAA,wCAAyB;;;0BAvBH;2BACC;oBAqBA;6BACU;AApBnC,IAAMJ,gBAAgB;IAC3B;;;;;GAKC,GACDE,gBAAAA,0BAAc;IAEd;;;;;GAKC,GACDC,iBAAAA,4BAAe;AACjB"}
|
|
@@ -20,6 +20,7 @@ Object.defineProperty(exports, "createLoopback", {
|
|
|
20
20
|
}
|
|
21
21
|
});
|
|
22
22
|
var _types = require("@modelcontextprotocol/sdk/types.js");
|
|
23
|
+
var _crypto = require("crypto");
|
|
23
24
|
var _zod = require("zod");
|
|
24
25
|
var _accountutilsts = require("../../account-utils.js");
|
|
25
26
|
var _mets = require("./me.js");
|
|
@@ -278,9 +279,9 @@ function createLoopback(config) {
|
|
|
278
279
|
case 1:
|
|
279
280
|
_state.trys.push([
|
|
280
281
|
1,
|
|
281
|
-
|
|
282
|
+
17,
|
|
282
283
|
,
|
|
283
|
-
|
|
284
|
+
18
|
|
284
285
|
]);
|
|
285
286
|
logger.info("Starting account switch for ".concat(service), {
|
|
286
287
|
email: params.email,
|
|
@@ -374,22 +375,29 @@ function createLoopback(config) {
|
|
|
374
375
|
];
|
|
375
376
|
case 8:
|
|
376
377
|
// Not linked or no email provided - trigger OAuth flow for new account
|
|
377
|
-
//
|
|
378
|
-
if (!auth.authenticateNewAccount) {
|
|
379
|
-
throw new Error('Account switching requires interactive authentication. ' + 'The current auth provider does not support authenticateNewAccount().');
|
|
380
|
-
}
|
|
378
|
+
// Force an OAuth flow by passing a unique accountId to bypass any active account.
|
|
381
379
|
return [
|
|
382
380
|
4,
|
|
383
|
-
auth.
|
|
381
|
+
auth.getAccessToken("new:".concat((0, _crypto.randomUUID)()))
|
|
384
382
|
];
|
|
385
383
|
case 9:
|
|
386
|
-
|
|
384
|
+
_state.sent();
|
|
385
|
+
return [
|
|
386
|
+
4,
|
|
387
|
+
(0, _accountutilsts.getActiveAccount)(store, {
|
|
388
|
+
service: service
|
|
389
|
+
})
|
|
390
|
+
];
|
|
391
|
+
case 10:
|
|
387
392
|
email = _state.sent();
|
|
393
|
+
if (!email) {
|
|
394
|
+
throw new Error('OAuth flow completed without setting an active account');
|
|
395
|
+
}
|
|
388
396
|
// Check if account already exists (in case OAuth returned different email than requested)
|
|
389
397
|
isNew = !existingAccounts.includes(email);
|
|
390
398
|
if (!isNew) return [
|
|
391
399
|
3,
|
|
392
|
-
|
|
400
|
+
12
|
|
393
401
|
];
|
|
394
402
|
// Add new account
|
|
395
403
|
return [
|
|
@@ -399,19 +407,19 @@ function createLoopback(config) {
|
|
|
399
407
|
accountId: email
|
|
400
408
|
})
|
|
401
409
|
];
|
|
402
|
-
case
|
|
410
|
+
case 11:
|
|
403
411
|
_state.sent();
|
|
404
412
|
logger.info("Added new ".concat(service, " account"), {
|
|
405
413
|
email: email
|
|
406
414
|
});
|
|
407
415
|
return [
|
|
408
416
|
3,
|
|
409
|
-
|
|
417
|
+
13
|
|
410
418
|
];
|
|
411
|
-
case 11:
|
|
412
|
-
logger.info("Account already linked: ".concat(email));
|
|
413
|
-
_state.label = 12;
|
|
414
419
|
case 12:
|
|
420
|
+
logger.info("Account already linked: ".concat(email));
|
|
421
|
+
_state.label = 13;
|
|
422
|
+
case 13:
|
|
415
423
|
return [
|
|
416
424
|
4,
|
|
417
425
|
(0, _accountutilsts.getAccountInfo)(store, {
|
|
@@ -419,7 +427,7 @@ function createLoopback(config) {
|
|
|
419
427
|
service: service
|
|
420
428
|
})
|
|
421
429
|
];
|
|
422
|
-
case
|
|
430
|
+
case 14:
|
|
423
431
|
existingInfo1 = _state.sent();
|
|
424
432
|
accountInfo1 = _object_spread_props(_object_spread({
|
|
425
433
|
email: email
|
|
@@ -435,7 +443,7 @@ function createLoopback(config) {
|
|
|
435
443
|
service: service
|
|
436
444
|
}, accountInfo1)
|
|
437
445
|
];
|
|
438
|
-
case
|
|
446
|
+
case 15:
|
|
439
447
|
_state.sent();
|
|
440
448
|
// Set as active account
|
|
441
449
|
return [
|
|
@@ -445,7 +453,7 @@ function createLoopback(config) {
|
|
|
445
453
|
accountId: email
|
|
446
454
|
})
|
|
447
455
|
];
|
|
448
|
-
case
|
|
456
|
+
case 16:
|
|
449
457
|
_state.sent();
|
|
450
458
|
totalAccounts = isNew ? existingAccounts.length + 1 : existingAccounts.length;
|
|
451
459
|
result1 = {
|
|
@@ -469,13 +477,13 @@ function createLoopback(config) {
|
|
|
469
477
|
}
|
|
470
478
|
}
|
|
471
479
|
];
|
|
472
|
-
case
|
|
480
|
+
case 17:
|
|
473
481
|
error = _state.sent();
|
|
474
482
|
message = _instanceof(error, Error) ? error.message : String(error);
|
|
475
483
|
throw new _types.McpError(_types.ErrorCode.InternalError, "Error switching ".concat(service, " account: ").concat(message), {
|
|
476
484
|
stack: _instanceof(error, Error) ? error.stack : undefined
|
|
477
485
|
});
|
|
478
|
-
case
|
|
486
|
+
case 18:
|
|
479
487
|
return [
|
|
480
488
|
2
|
|
481
489
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth/src/lib/account-server/loopback.ts"],"sourcesContent":["/**\n * Loopback OAuth account management tools.\n *\n * Provides account management for LoopbackOAuthProvider (server-managed multi-account).\n * Users can add multiple accounts, switch between them, and manage identities.\n *\n * Tools:\n * - account-me: Show current user identity (email, alias, session expiry)\n * - account-switch: Use account (add if needed, switch if already linked)\n * - account-remove: Remove account and delete tokens\n * - account-list: Show all linked accounts (returns empty array if none)\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport { addAccount, getAccountInfo, getActiveAccount, getLinkedAccounts, removeAccount, setAccountInfo, setActiveAccount } from '../../account-utils.ts';\nimport type { AccountInfo, McpPrompt, McpTool } from '../../types.ts';\nimport { createAccountMe } from './me.ts';\nimport { findAccountByEmailOrAlias } from './shared-utils.ts';\nimport type { AccountLoopbackConfig } from './types.ts';\n\n/**\n * Create loopback OAuth account management tools.\n * Returns 4 tools: account-me, account-switch, account-remove, account-list.\n */\nexport function createLoopback(config: AccountLoopbackConfig): { tools: McpTool[]; prompts: McpPrompt[] } {\n const { service, store, logger, auth } = config;\n\n // Create account-me tool\n const meTools = createAccountMe({ service, store, logger, mode: 'loopback' });\n\n const tools: McpTool[] = [\n ...meTools.tools,\n // account-switch\n {\n name: 'account-switch',\n config: {\n description: `Use ${service} account (smart mode). If email/alias provided and already linked, switches to it without triggering OAuth. If not linked or no email provided, triggers OAuth browser flow to add account. Returns account email, whether it was newly added, and total account count.`,\n inputSchema: {\n email: z.string().optional().describe('Email address to link (if already linked, switches without OAuth)'),\n alias: z.string().optional().describe('Optional alias for easy identification'),\n } as const,\n outputSchema: {\n result: z.discriminatedUnion('type', [\n z.object({\n type: z.literal('success'),\n email: z.string(),\n isNew: z.boolean(),\n totalAccounts: z.number(),\n message: z.string(),\n }),\n ]),\n } as const,\n },\n handler: async (args: unknown): Promise<CallToolResult> => {\n const params = args as { email?: string; alias?: string };\n try {\n logger.info(`Starting account switch for ${service}`, { email: params.email, alias: params.alias });\n\n // Get existing accounts\n const existingAccounts = await getLinkedAccounts(store, { service });\n\n let email: string;\n let isNew: boolean;\n\n // Smart behavior: check if email provided and already linked\n if (params.email) {\n // Find account by email or alias\n const accountId = await findAccountByEmailOrAlias(store, service, params.email);\n\n if (accountId) {\n // Account already linked - just switch to it\n email = accountId;\n isNew = false;\n logger.info(`Account already linked: ${email}, switching without OAuth`);\n\n // Set as active account\n await setActiveAccount(store, { service, accountId: email });\n\n // Update alias if provided\n if (params.alias) {\n const existingInfo = await getAccountInfo(store, { accountId: email, service });\n const accountInfo: AccountInfo = {\n email,\n alias: params.alias,\n addedAt: existingInfo?.addedAt ?? new Date().toISOString(),\n };\n await setAccountInfo(store, { accountId: email, service }, accountInfo);\n }\n\n const result = {\n type: 'success' as const,\n email,\n isNew: false,\n totalAccounts: existingAccounts.length,\n message: `Account already linked: ${email}. Set as active account (no OAuth needed).`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n }\n }\n\n // Not linked or no email provided - trigger OAuth flow for new account\n // Check if provider supports interactive authentication\n if (!auth.authenticateNewAccount) {\n throw new Error('Account switching requires interactive authentication. ' + 'The current auth provider does not support authenticateNewAccount().');\n }\n\n // Trigger new authentication with account selection\n email = await auth.authenticateNewAccount();\n\n // Check if account already exists (in case OAuth returned different email than requested)\n isNew = !existingAccounts.includes(email);\n\n if (isNew) {\n // Add new account\n await addAccount(store, { service, accountId: email });\n logger.info(`Added new ${service} account`, { email });\n } else {\n logger.info(`Account already linked: ${email}`);\n }\n\n // Set/update account info\n const existingInfo = await getAccountInfo(store, { accountId: email, service });\n const accountInfo: AccountInfo = {\n email,\n ...(params.alias ? { alias: params.alias } : {}),\n addedAt: isNew ? new Date().toISOString() : (existingInfo?.addedAt ?? new Date().toISOString()),\n };\n await setAccountInfo(store, { accountId: email, service }, accountInfo);\n\n // Set as active account\n await setActiveAccount(store, { service, accountId: email });\n\n const totalAccounts = isNew ? existingAccounts.length + 1 : existingAccounts.length;\n\n const result = {\n type: 'success' as const,\n email,\n isNew,\n totalAccounts,\n message: isNew ? `Successfully added ${service} account: ${email} (${totalAccounts} total)` : `Account already linked: ${email}. Set as active account.`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new McpError(ErrorCode.InternalError, `Error switching ${service} account: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n },\n },\n\n // account-remove\n {\n name: 'account-remove',\n config: {\n description: `Remove ${service} account and delete stored tokens permanently. If removing the active account, the first remaining account becomes active. Requires email or alias parameter.`,\n inputSchema: {\n accountId: z.string().min(1).describe('Email address or alias of account to remove'),\n } as const,\n outputSchema: {\n result: z.discriminatedUnion('type', [\n z.object({\n type: z.literal('success'),\n service: z.string(),\n removed: z.string(),\n remainingAccounts: z.number(),\n newActiveAccount: z.string().optional(),\n message: z.string(),\n }),\n ]),\n } as const,\n },\n handler: async (args: unknown): Promise<CallToolResult> => {\n const params = args as { accountId: string };\n try {\n const linkedAccounts = await getLinkedAccounts(store, { service });\n if (linkedAccounts.length === 0) {\n throw new Error(`No ${service} accounts to remove`);\n }\n\n // Find account by email or alias\n const accountId = await findAccountByEmailOrAlias(store, service, params.accountId);\n\n if (!accountId) {\n throw new Error(`Account not found: ${params.accountId}`);\n }\n\n // Get current active account\n const activeAccount = await getActiveAccount(store, { service });\n const removingActive = activeAccount === accountId;\n\n // Remove the account\n await removeAccount(store, { service, accountId });\n const remainingAccounts = linkedAccounts.filter((id) => id !== accountId);\n\n // If we removed the active account, set first remaining as active\n let newActiveAccount: string | undefined;\n if (removingActive && remainingAccounts.length > 0) {\n const firstRemaining = remainingAccounts[0];\n if (firstRemaining) {\n newActiveAccount = firstRemaining;\n await setActiveAccount(store, { service, accountId: newActiveAccount });\n }\n }\n\n logger.info(`Successfully removed ${service} account`, { accountId, remainingAccounts: remainingAccounts.length });\n\n const result = {\n type: 'success' as const,\n service,\n removed: accountId,\n remainingAccounts: remainingAccounts.length,\n ...(newActiveAccount && { newActiveAccount }),\n message: `Removed ${service} account: ${accountId}${newActiveAccount ? `. Active account is now: ${newActiveAccount}` : ''}`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new McpError(ErrorCode.InternalError, `Error removing ${service} account: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n },\n },\n\n // account-list\n {\n name: 'account-list',\n config: {\n description: `List all linked ${service} accounts with their aliases and active status.`,\n inputSchema: {} as const,\n outputSchema: {\n result: z.discriminatedUnion('type', [\n z.object({\n type: z.literal('success'),\n service: z.string(),\n accounts: z.array(\n z.object({\n email: z.string(),\n alias: z.string().optional(),\n isActive: z.boolean(),\n })\n ),\n totalAccounts: z.number(),\n message: z.string(),\n }),\n ]),\n } as const,\n },\n handler: async (): Promise<CallToolResult> => {\n try {\n const linkedAccounts = await getLinkedAccounts(store, { service });\n\n // Return empty array gracefully (no error when no accounts)\n if (linkedAccounts.length === 0) {\n const result = {\n type: 'success' as const,\n service,\n accounts: [],\n totalAccounts: 0,\n message: `No ${service} accounts linked. Use account-switch to add an account.`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n }\n\n const activeAccountId = await getActiveAccount(store, { service });\n\n // Get account info for each linked account\n const accounts = await Promise.all(\n linkedAccounts.map(async (email) => {\n const accountInfo = await getAccountInfo(store, { accountId: email, service });\n return {\n email,\n alias: accountInfo?.alias,\n isActive: email === activeAccountId,\n };\n })\n );\n\n const result = {\n type: 'success' as const,\n service,\n accounts,\n totalAccounts: linkedAccounts.length,\n message: `Found ${linkedAccounts.length} ${service} account(s)`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new McpError(ErrorCode.InternalError, `Error listing ${service} accounts: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n },\n },\n ];\n\n const prompts: McpPrompt[] = [];\n\n return { tools, prompts };\n}\n"],"names":["createLoopback","config","service","store","logger","auth","meTools","createAccountMe","mode","tools","name","description","inputSchema","email","z","string","optional","describe","alias","outputSchema","result","discriminatedUnion","object","type","literal","isNew","boolean","totalAccounts","number","message","handler","args","params","existingAccounts","accountId","existingInfo","accountInfo","error","info","getLinkedAccounts","findAccountByEmailOrAlias","setActiveAccount","getAccountInfo","addedAt","Date","toISOString","setAccountInfo","length","content","text","JSON","stringify","structuredContent","authenticateNewAccount","Error","includes","addAccount","String","McpError","ErrorCode","InternalError","stack","undefined","min","removed","remainingAccounts","newActiveAccount","linkedAccounts","activeAccount","removingActive","firstRemaining","getActiveAccount","removeAccount","filter","id","accounts","array","isActive","activeAccountId","Promise","all","map","prompts"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/lib/account-server/loopback.ts"],"sourcesContent":["/**\n * Loopback OAuth account management tools.\n *\n * Provides account management for LoopbackOAuthProvider (server-managed multi-account).\n * Users can add multiple accounts, switch between them, and manage identities.\n *\n * Tools:\n * - account-me: Show current user identity (email, alias, session expiry)\n * - account-switch: Use account (add if needed, switch if already linked)\n * - account-remove: Remove account and delete tokens\n * - account-list: Show all linked accounts (returns empty array if none)\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { randomUUID } from 'crypto';\nimport { z } from 'zod';\nimport { addAccount, getAccountInfo, getActiveAccount, getLinkedAccounts, removeAccount, setAccountInfo, setActiveAccount } from '../../account-utils.ts';\nimport type { AccountInfo, McpPrompt, McpTool } from '../../types.ts';\nimport { createAccountMe } from './me.ts';\nimport { findAccountByEmailOrAlias } from './shared-utils.ts';\nimport type { AccountLoopbackConfig } from './types.ts';\n\n/**\n * Create loopback OAuth account management tools.\n * Returns 4 tools: account-me, account-switch, account-remove, account-list.\n */\nexport function createLoopback(config: AccountLoopbackConfig): { tools: McpTool[]; prompts: McpPrompt[] } {\n const { service, store, logger, auth } = config;\n\n // Create account-me tool\n const meTools = createAccountMe({ service, store, logger, mode: 'loopback' });\n\n const tools: McpTool[] = [\n ...meTools.tools,\n // account-switch\n {\n name: 'account-switch',\n config: {\n description: `Use ${service} account (smart mode). If email/alias provided and already linked, switches to it without triggering OAuth. If not linked or no email provided, triggers OAuth browser flow to add account. Returns account email, whether it was newly added, and total account count.`,\n inputSchema: {\n email: z.string().optional().describe('Email address to link (if already linked, switches without OAuth)'),\n alias: z.string().optional().describe('Optional alias for easy identification'),\n } as const,\n outputSchema: {\n result: z.discriminatedUnion('type', [\n z.object({\n type: z.literal('success'),\n email: z.string(),\n isNew: z.boolean(),\n totalAccounts: z.number(),\n message: z.string(),\n }),\n ]),\n } as const,\n },\n handler: async (args: unknown): Promise<CallToolResult> => {\n const params = args as { email?: string; alias?: string };\n try {\n logger.info(`Starting account switch for ${service}`, { email: params.email, alias: params.alias });\n\n // Get existing accounts\n const existingAccounts = await getLinkedAccounts(store, { service });\n\n let email: string;\n let isNew: boolean;\n\n // Smart behavior: check if email provided and already linked\n if (params.email) {\n // Find account by email or alias\n const accountId = await findAccountByEmailOrAlias(store, service, params.email);\n\n if (accountId) {\n // Account already linked - just switch to it\n email = accountId;\n isNew = false;\n logger.info(`Account already linked: ${email}, switching without OAuth`);\n\n // Set as active account\n await setActiveAccount(store, { service, accountId: email });\n\n // Update alias if provided\n if (params.alias) {\n const existingInfo = await getAccountInfo(store, { accountId: email, service });\n const accountInfo: AccountInfo = {\n email,\n alias: params.alias,\n addedAt: existingInfo?.addedAt ?? new Date().toISOString(),\n };\n await setAccountInfo(store, { accountId: email, service }, accountInfo);\n }\n\n const result = {\n type: 'success' as const,\n email,\n isNew: false,\n totalAccounts: existingAccounts.length,\n message: `Account already linked: ${email}. Set as active account (no OAuth needed).`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n }\n }\n\n // Not linked or no email provided - trigger OAuth flow for new account\n // Force an OAuth flow by passing a unique accountId to bypass any active account.\n await auth.getAccessToken(`new:${randomUUID()}`);\n\n email = await getActiveAccount(store, { service });\n if (!email) {\n throw new Error('OAuth flow completed without setting an active account');\n }\n\n // Check if account already exists (in case OAuth returned different email than requested)\n isNew = !existingAccounts.includes(email);\n\n if (isNew) {\n // Add new account\n await addAccount(store, { service, accountId: email });\n logger.info(`Added new ${service} account`, { email });\n } else {\n logger.info(`Account already linked: ${email}`);\n }\n\n // Set/update account info\n const existingInfo = await getAccountInfo(store, { accountId: email, service });\n const accountInfo: AccountInfo = {\n email,\n ...(params.alias ? { alias: params.alias } : {}),\n addedAt: isNew ? new Date().toISOString() : (existingInfo?.addedAt ?? new Date().toISOString()),\n };\n await setAccountInfo(store, { accountId: email, service }, accountInfo);\n\n // Set as active account\n await setActiveAccount(store, { service, accountId: email });\n\n const totalAccounts = isNew ? existingAccounts.length + 1 : existingAccounts.length;\n\n const result = {\n type: 'success' as const,\n email,\n isNew,\n totalAccounts,\n message: isNew ? `Successfully added ${service} account: ${email} (${totalAccounts} total)` : `Account already linked: ${email}. Set as active account.`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new McpError(ErrorCode.InternalError, `Error switching ${service} account: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n },\n },\n\n // account-remove\n {\n name: 'account-remove',\n config: {\n description: `Remove ${service} account and delete stored tokens permanently. If removing the active account, the first remaining account becomes active. Requires email or alias parameter.`,\n inputSchema: {\n accountId: z.string().min(1).describe('Email address or alias of account to remove'),\n } as const,\n outputSchema: {\n result: z.discriminatedUnion('type', [\n z.object({\n type: z.literal('success'),\n service: z.string(),\n removed: z.string(),\n remainingAccounts: z.number(),\n newActiveAccount: z.string().optional(),\n message: z.string(),\n }),\n ]),\n } as const,\n },\n handler: async (args: unknown): Promise<CallToolResult> => {\n const params = args as { accountId: string };\n try {\n const linkedAccounts = await getLinkedAccounts(store, { service });\n if (linkedAccounts.length === 0) {\n throw new Error(`No ${service} accounts to remove`);\n }\n\n // Find account by email or alias\n const accountId = await findAccountByEmailOrAlias(store, service, params.accountId);\n\n if (!accountId) {\n throw new Error(`Account not found: ${params.accountId}`);\n }\n\n // Get current active account\n const activeAccount = await getActiveAccount(store, { service });\n const removingActive = activeAccount === accountId;\n\n // Remove the account\n await removeAccount(store, { service, accountId });\n const remainingAccounts = linkedAccounts.filter((id) => id !== accountId);\n\n // If we removed the active account, set first remaining as active\n let newActiveAccount: string | undefined;\n if (removingActive && remainingAccounts.length > 0) {\n const firstRemaining = remainingAccounts[0];\n if (firstRemaining) {\n newActiveAccount = firstRemaining;\n await setActiveAccount(store, { service, accountId: newActiveAccount });\n }\n }\n\n logger.info(`Successfully removed ${service} account`, { accountId, remainingAccounts: remainingAccounts.length });\n\n const result = {\n type: 'success' as const,\n service,\n removed: accountId,\n remainingAccounts: remainingAccounts.length,\n ...(newActiveAccount && { newActiveAccount }),\n message: `Removed ${service} account: ${accountId}${newActiveAccount ? `. Active account is now: ${newActiveAccount}` : ''}`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new McpError(ErrorCode.InternalError, `Error removing ${service} account: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n },\n },\n\n // account-list\n {\n name: 'account-list',\n config: {\n description: `List all linked ${service} accounts with their aliases and active status.`,\n inputSchema: {} as const,\n outputSchema: {\n result: z.discriminatedUnion('type', [\n z.object({\n type: z.literal('success'),\n service: z.string(),\n accounts: z.array(\n z.object({\n email: z.string(),\n alias: z.string().optional(),\n isActive: z.boolean(),\n })\n ),\n totalAccounts: z.number(),\n message: z.string(),\n }),\n ]),\n } as const,\n },\n handler: async (): Promise<CallToolResult> => {\n try {\n const linkedAccounts = await getLinkedAccounts(store, { service });\n\n // Return empty array gracefully (no error when no accounts)\n if (linkedAccounts.length === 0) {\n const result = {\n type: 'success' as const,\n service,\n accounts: [],\n totalAccounts: 0,\n message: `No ${service} accounts linked. Use account-switch to add an account.`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n }\n\n const activeAccountId = await getActiveAccount(store, { service });\n\n // Get account info for each linked account\n const accounts = await Promise.all(\n linkedAccounts.map(async (email) => {\n const accountInfo = await getAccountInfo(store, { accountId: email, service });\n return {\n email,\n alias: accountInfo?.alias,\n isActive: email === activeAccountId,\n };\n })\n );\n\n const result = {\n type: 'success' as const,\n service,\n accounts,\n totalAccounts: linkedAccounts.length,\n message: `Found ${linkedAccounts.length} ${service} account(s)`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new McpError(ErrorCode.InternalError, `Error listing ${service} accounts: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n },\n },\n ];\n\n const prompts: McpPrompt[] = [];\n\n return { tools, prompts };\n}\n"],"names":["createLoopback","config","service","store","logger","auth","meTools","createAccountMe","mode","tools","name","description","inputSchema","email","z","string","optional","describe","alias","outputSchema","result","discriminatedUnion","object","type","literal","isNew","boolean","totalAccounts","number","message","handler","args","params","existingAccounts","accountId","existingInfo","accountInfo","error","info","getLinkedAccounts","findAccountByEmailOrAlias","setActiveAccount","getAccountInfo","addedAt","Date","toISOString","setAccountInfo","length","content","text","JSON","stringify","structuredContent","getAccessToken","randomUUID","getActiveAccount","Error","includes","addAccount","String","McpError","ErrorCode","InternalError","stack","undefined","min","removed","remainingAccounts","newActiveAccount","linkedAccounts","activeAccount","removingActive","firstRemaining","removeAccount","filter","id","accounts","array","isActive","activeAccountId","Promise","all","map","prompts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth/src/lib/account-server/me.ts"],"sourcesContent":["/**\n * Account \"me\" tool - Who am I currently authenticated as?\n *\n * Provides current user identity across all auth modes:\n * - Loopback: email, alias, sessionExpiresIn from stored tokens\n * - DCR/Stateless: email from bearer token context, sessionExpiresIn=null\n * - Device Code: email, sessionExpiresIn from stored tokens\n * - Service Account: email, sessionExpiresIn=\"never\" (JWT-based)\n *\n * Tool: {service}-account-me\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport { getAccountInfo, getActiveAccount, getToken } from '../../account-utils.ts';\nimport type { CachedToken, McpPrompt, McpTool } from '../../types.ts';\nimport type { AccountMeConfig } from './types.ts';\n\n/**\n * Format milliseconds as human-readable duration\n * Examples: \"2h 15m\", \"45m\", \"30s\"\n */\nfunction formatDuration(ms: number): string {\n const totalSeconds = Math.floor(ms / 1000);\n\n if (totalSeconds < 60) {\n return `${totalSeconds}s`;\n }\n\n const minutes = Math.floor(totalSeconds / 60);\n if (minutes < 60) {\n return `${minutes}m`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n\n if (remainingMinutes === 0) {\n return `${hours}h`;\n }\n\n return `${hours}h ${remainingMinutes}m`;\n}\n\n/**\n * Create account-me tool for current user identity.\n *\n * Returns email, optional alias (loopback only), and session expiry info.\n * Throws error if no active account in loopback mode.\n */\nexport function createAccountMe(config: AccountMeConfig): { tools: McpTool[]; prompts: McpPrompt[] } {\n const { service, store, logger, mode } = config;\n\n const tools: McpTool[] = [\n {\n name: 'account-me',\n config: {\n description: `Show current ${service} user identity. Returns email, alias (if set), and session expiry information.`,\n inputSchema: {} as const,\n outputSchema: {\n result: z.discriminatedUnion('type', [\n z.object({\n type: z.literal('success'),\n service: z.string(),\n email: z.string(),\n alias: z.string().optional(),\n sessionExpiresIn: z.string().nullable().optional(),\n message: z.string(),\n }),\n ]),\n } as const,\n },\n handler: async (_args: unknown, extra?: unknown): Promise<CallToolResult> => {\n try {\n // Mode-specific implementation\n if (mode === 'stateless') {\n // DCR/Stateless: Extract email from auth context\n const authContext = (extra as { authContext?: { accountId?: string } })?.authContext;\n\n if (!authContext?.accountId) {\n throw new Error('No authentication context available. DCR mode requires bearer token.');\n }\n\n const result = {\n type: 'success' as const,\n service,\n email: authContext.accountId,\n sessionExpiresIn: null, // Client-managed\n message: `Authenticated as ${authContext.accountId}. Session managed by MCP client.`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n }\n\n // Loopback/Device Code/Service Account: Use store\n if (!store) {\n throw new Error('Store is required for non-stateless mode');\n }\n\n // Get active account\n const activeAccountId = await getActiveAccount(store, { service });\n if (!activeAccountId) {\n throw new Error(`No active ${service} account found. Use account-switch to add an account.`);\n }\n\n // Get account info (email, alias)\n const accountInfo = await getAccountInfo(store, { accountId: activeAccountId, service });\n const email = accountInfo?.email ?? activeAccountId;\n const alias = accountInfo?.alias;\n\n // Calculate session expiry\n let sessionExpiresIn: string | null = null;\n try {\n const token = await getToken<CachedToken>(store, { accountId: activeAccountId, service });\n if (token?.expiresAt) {\n const now = Date.now();\n if (token.expiresAt > now) {\n sessionExpiresIn = formatDuration(token.expiresAt - now);\n } else {\n sessionExpiresIn = 'expired';\n }\n } else {\n // No expiry = JWT-based service account or no token info\n sessionExpiresIn = 'never';\n }\n } catch {\n // Token not found or error reading - treat as \"never\" (service account pattern)\n sessionExpiresIn = 'never';\n }\n\n const result = {\n type: 'success' as const,\n service,\n email,\n ...(alias && { alias }),\n ...(sessionExpiresIn && { sessionExpiresIn }),\n message: `Authenticated as ${email}${alias ? ` (${alias})` : ''}${sessionExpiresIn ? `. Session expires in ${sessionExpiresIn}` : ''}.`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger?.error?.('account-me.error', { service, error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error getting ${service} account info: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n },\n },\n ];\n\n const prompts: McpPrompt[] = [];\n\n return { tools, prompts };\n}\n"],"names":["createAccountMe","formatDuration","ms","totalSeconds","Math","floor","minutes","hours","remainingMinutes","config","service","store","logger","mode","tools","name","description","inputSchema","outputSchema","result","z","discriminatedUnion","object","type","literal","string","email","alias","optional","sessionExpiresIn","nullable","message","handler","_args","extra","authContext","activeAccountId","accountInfo","token","now","error","accountId","Error","content","text","JSON","stringify","structuredContent","getActiveAccount","getAccountInfo","getToken","expiresAt","Date","String","McpError","ErrorCode","InternalError","stack","undefined","prompts"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/lib/account-server/me.ts"],"sourcesContent":["/**\n * Account \"me\" tool - Who am I currently authenticated as?\n *\n * Provides current user identity across all auth modes:\n * - Loopback: email, alias, sessionExpiresIn from stored tokens\n * - DCR/Stateless: email from bearer token context, sessionExpiresIn=null\n * - Device Code: email, sessionExpiresIn from stored tokens\n * - Service Account: email, sessionExpiresIn=\"never\" (JWT-based)\n *\n * Tool: {service}-account-me\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport { getAccountInfo, getActiveAccount, getToken } from '../../account-utils.ts';\nimport type { CachedToken, McpPrompt, McpTool } from '../../types.ts';\nimport type { AccountMeConfig } from './types.ts';\n\n/**\n * Format milliseconds as human-readable duration\n * Examples: \"2h 15m\", \"45m\", \"30s\"\n */\nfunction formatDuration(ms: number): string {\n const totalSeconds = Math.floor(ms / 1000);\n\n if (totalSeconds < 60) {\n return `${totalSeconds}s`;\n }\n\n const minutes = Math.floor(totalSeconds / 60);\n if (minutes < 60) {\n return `${minutes}m`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n\n if (remainingMinutes === 0) {\n return `${hours}h`;\n }\n\n return `${hours}h ${remainingMinutes}m`;\n}\n\n/**\n * Create account-me tool for current user identity.\n *\n * Returns email, optional alias (loopback only), and session expiry info.\n * Throws error if no active account in loopback mode.\n */\nexport function createAccountMe(config: AccountMeConfig): { tools: McpTool[]; prompts: McpPrompt[] } {\n const { service, store, logger, mode } = config;\n\n const tools: McpTool[] = [\n {\n name: 'account-me',\n config: {\n description: `Show current ${service} user identity. Returns email, alias (if set), and session expiry information.`,\n inputSchema: {} as const,\n outputSchema: {\n result: z.discriminatedUnion('type', [\n z.object({\n type: z.literal('success'),\n service: z.string(),\n email: z.string(),\n alias: z.string().optional(),\n sessionExpiresIn: z.string().nullable().optional(),\n message: z.string(),\n }),\n ]),\n } as const,\n },\n handler: async (_args: unknown, extra?: unknown): Promise<CallToolResult> => {\n try {\n // Mode-specific implementation\n if (mode === 'stateless') {\n // DCR/Stateless: Extract email from auth context\n const authContext = (extra as { authContext?: { accountId?: string } })?.authContext;\n\n if (!authContext?.accountId) {\n throw new Error('No authentication context available. DCR mode requires bearer token.');\n }\n\n const result = {\n type: 'success' as const,\n service,\n email: authContext.accountId,\n sessionExpiresIn: null, // Client-managed\n message: `Authenticated as ${authContext.accountId}. Session managed by MCP client.`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n }\n\n // Loopback/Device Code/Service Account: Use store\n if (!store) {\n throw new Error('Store is required for non-stateless mode');\n }\n\n // Get active account\n const activeAccountId = await getActiveAccount(store, { service });\n if (!activeAccountId) {\n throw new Error(`No active ${service} account found. Use account-switch to add an account.`);\n }\n\n // Get account info (email, alias)\n const accountInfo = await getAccountInfo(store, { accountId: activeAccountId, service });\n const email = accountInfo?.email ?? activeAccountId;\n const alias = accountInfo?.alias;\n\n // Calculate session expiry\n let sessionExpiresIn: string | null = null;\n try {\n const token = await getToken<CachedToken>(store, { accountId: activeAccountId, service });\n if (token?.expiresAt) {\n const now = Date.now();\n if (token.expiresAt > now) {\n sessionExpiresIn = formatDuration(token.expiresAt - now);\n } else {\n sessionExpiresIn = 'expired';\n }\n } else {\n // No expiry = JWT-based service account or no token info\n sessionExpiresIn = 'never';\n }\n } catch {\n // Token not found or error reading - treat as \"never\" (service account pattern)\n sessionExpiresIn = 'never';\n }\n\n const result = {\n type: 'success' as const,\n service,\n email,\n ...(alias && { alias }),\n ...(sessionExpiresIn && { sessionExpiresIn }),\n message: `Authenticated as ${email}${alias ? ` (${alias})` : ''}${sessionExpiresIn ? `. Session expires in ${sessionExpiresIn}` : ''}.`,\n };\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger?.error?.('account-me.error', { service, error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error getting ${service} account info: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n },\n },\n ];\n\n const prompts: McpPrompt[] = [];\n\n return { tools, prompts };\n}\n"],"names":["createAccountMe","formatDuration","ms","totalSeconds","Math","floor","minutes","hours","remainingMinutes","config","service","store","logger","mode","tools","name","description","inputSchema","outputSchema","result","z","discriminatedUnion","object","type","literal","string","email","alias","optional","sessionExpiresIn","nullable","message","handler","_args","extra","authContext","activeAccountId","accountInfo","token","now","error","accountId","Error","content","text","JSON","stringify","structuredContent","getActiveAccount","getAccountInfo","getToken","expiresAt","Date","String","McpError","ErrorCode","InternalError","stack","undefined","prompts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/lib/account-server/shared-utils.ts"],"sourcesContent":["import type { Keyv } from 'keyv';\nimport { getAccountInfo, getLinkedAccounts } from '../../account-utils.ts';\n\n/**\n * Find account ID by email or alias lookup.\n * Returns accountId if found, otherwise null.\n */\nexport async function findAccountByEmailOrAlias(store: Keyv, service: string, emailOrAlias: string): Promise<string | null> {\n const linkedAccountIds = await getLinkedAccounts(store, { service });\n\n // Try exact email match first\n if (linkedAccountIds.includes(emailOrAlias)) {\n return emailOrAlias;\n }\n\n // Search by alias\n for (const accountId of linkedAccountIds) {\n const info = await getAccountInfo(store, { accountId, service });\n if (info?.alias === emailOrAlias) {\n return accountId;\n }\n }\n\n return null;\n}\n"],"names":["findAccountByEmailOrAlias","store","service","emailOrAlias","linkedAccountIds","accountId","info","getLinkedAccounts","includes","getAccountInfo","alias"],"mappings":";;;;+BAOsBA;;;eAAAA;;;8BAN4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAM3C,SAAeA,0BAA0BC,KAAW,EAAEC,OAAe,EAAEC,YAAoB;;YAC1FC,kBAQD,2BAAA,mBAAA,gBAAA,WAAA,OAAMC,WACHC;;;;oBATiB;;wBAAMC,IAAAA,iCAAiB,EAACN,OAAO;4BAAEC,SAAAA;wBAAQ;;;oBAA5DE,mBAAmB;oBAEzB,8BAA8B;oBAC9B,IAAIA,iBAAiBI,QAAQ,CAACL,eAAe;wBAC3C;;4BAAOA;;oBACT;oBAGK,kCAAA,2BAAA;;;;;;;;;oBAAA,YAAmBC;;;2BAAnB,6BAAA,QAAA;;;;oBAAMC,YAAN;oBACU;;wBAAMI,IAAAA,8BAAc,EAACR,OAAO;4BAAEI,WAAAA;4BAAWH,SAAAA;wBAAQ;;;oBAAxDI,OAAO;oBACb,IAAIA,CAAAA,iBAAAA,2BAAAA,KAAMI,KAAK,MAAKP,cAAc;wBAChC;;4BAAOE;;oBACT;;;oBAJG;;;;;;;;;;;;oBAAA;oBAAA;;;;;;;6BAAA,6BAAA;4BAAA;;;4BAAA;kCAAA;;;;;;;oBAOL;;wBAAO;;;;IACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/lib/account-server/stateless.ts"],"sourcesContent":["/**\n * Stateless tool set for MCP OAuth mode (DCR).\n *\n * Use this when authentication is managed by the MCP client.\n * Tokens are provided per-request and not stored by the server.\n *\n * Tools:\n * - {service}-account-me: Show current user identity from bearer token\n */\n\nimport type { McpPrompt, McpTool } from '../../types.ts';\nimport { createAccountMe } from './me.ts';\nimport type { AccountStatelessConfig } from './types.ts';\n\n/**\n * Create stateless mode tools.\n * MCP client manages authentication. Server provides user identity from bearer token.\n * Returns 1 tool: account-me.\n */\nexport function createStateless(config: AccountStatelessConfig): { tools: McpTool[]; prompts: McpPrompt[] } {\n const { service } = config;\n\n // Create account-me tool for stateless mode\n const meTools = createAccountMe({ service, mode: 'stateless' });\n\n return { tools: meTools.tools, prompts: [] };\n}\n"],"names":["createStateless","config","service","meTools","createAccountMe","mode","tools","prompts"],"mappings":"AAAA;;;;;;;;CAQC;;;;+BAWeA;;;eAAAA;;;oBARgB;AAQzB,SAASA,gBAAgBC,MAA8B;IAC5D,IAAM,AAAEC,UAAYD,OAAZC;IAER,4CAA4C;IAC5C,IAAMC,UAAUC,IAAAA,qBAAe,EAAC;QAAEF,SAAAA;QAASG,MAAM;IAAY;IAE7D,OAAO;QAAEC,OAAOH,QAAQG,KAAK;QAAEC,SAAS,EAAE;IAAC;AAC7C"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Configuration types for account tool factories.
|
|
3
3
|
*/
|
|
4
4
|
import type { Keyv } from 'keyv';
|
|
5
|
-
import type {
|
|
5
|
+
import type { AccountAuthProvider, Logger } from '../../types.js';
|
|
6
6
|
/**
|
|
7
7
|
* Configuration for loopback OAuth account management.
|
|
8
8
|
* Supports multiple accounts with server-managed tokens (LoopbackOAuthProvider).
|
|
@@ -11,7 +11,7 @@ export interface AccountLoopbackConfig {
|
|
|
11
11
|
service: string;
|
|
12
12
|
store: Keyv;
|
|
13
13
|
logger: Logger;
|
|
14
|
-
auth:
|
|
14
|
+
auth: AccountAuthProvider;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Configuration for stateless mode.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Configuration types for account tool factories.
|
|
3
3
|
*/
|
|
4
4
|
import type { Keyv } from 'keyv';
|
|
5
|
-
import type {
|
|
5
|
+
import type { AccountAuthProvider, Logger } from '../../types.js';
|
|
6
6
|
/**
|
|
7
7
|
* Configuration for loopback OAuth account management.
|
|
8
8
|
* Supports multiple accounts with server-managed tokens (LoopbackOAuthProvider).
|
|
@@ -11,7 +11,7 @@ export interface AccountLoopbackConfig {
|
|
|
11
11
|
service: string;
|
|
12
12
|
store: Keyv;
|
|
13
13
|
logger: Logger;
|
|
14
|
-
auth:
|
|
14
|
+
auth: AccountAuthProvider;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Configuration for stateless mode.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth/src/lib/account-server/types.ts"],"sourcesContent":["/**\n * Configuration types for account tool factories.\n */\n\nimport type { Keyv } from 'keyv';\nimport type { AccountAuthProvider, Logger } from '../../types.ts';\n\n/**\n * Configuration for loopback OAuth account management.\n * Supports multiple accounts with server-managed tokens (LoopbackOAuthProvider).\n */\nexport interface AccountLoopbackConfig {\n service: string;\n store: Keyv;\n logger: Logger;\n auth: AccountAuthProvider;\n}\n\n/**\n * Configuration for stateless mode.\n * MCP client manages authentication. Server provides read-only status.\n */\nexport interface AccountStatelessConfig {\n service: string;\n}\n\n/**\n * Configuration for account-me tool.\n * Works across all auth modes: loopback, stateless, device code, service account.\n */\nexport interface AccountMeConfig {\n service: string;\n store?: Keyv;\n logger?: Logger;\n mode: 'loopback' | 'stateless' | 'device-code' | 'service-account';\n}\n"],"names":[],"mappings":"AAAA;;CAEC"}
|