diff --git a/src/client.ts b/src/client.ts index 5a8038b..500d70c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -3,7 +3,10 @@ import { createEntitiesModule } from "./modules/entities.js"; import { createIntegrationsModule } from "./modules/integrations.js"; import { createAuthModule } from "./modules/auth.js"; import { createSsoModule } from "./modules/sso.js"; -import { createConnectorsModule } from "./modules/connectors.js"; +import { + createConnectorsModule, + createUserConnectorsModule, +} from "./modules/connectors.js"; import { getAccessToken } from "./utils/auth-utils.js"; import { createFunctionsModule } from "./modules/functions.js"; import { createAgentsModule } from "./modules/agents.js"; @@ -150,6 +153,7 @@ export function createClient(config: CreateClientConfig): Base44Client { getSocket, }), integrations: createIntegrationsModule(axiosClient, appId), + connectors: createUserConnectorsModule(axiosClient, appId), auth: userAuthModule, functions: createFunctionsModule(functionsAxiosClient, appId), agents: createAgentsModule({ diff --git a/src/client.types.ts b/src/client.types.ts index 6a52d18..6b4c9c5 100644 --- a/src/client.types.ts +++ b/src/client.types.ts @@ -2,7 +2,10 @@ import type { EntitiesModule } from "./modules/entities.types.js"; import type { IntegrationsModule } from "./modules/integrations.types.js"; import type { AuthModule } from "./modules/auth.types.js"; import type { SsoModule } from "./modules/sso.types.js"; -import type { ConnectorsModule } from "./modules/connectors.types.js"; +import type { + ConnectorsModule, + UserConnectorsModule, +} from "./modules/connectors.types.js"; import type { FunctionsModule } from "./modules/functions.types.js"; import type { AgentsModule } from "./modules/agents.types.js"; import type { AppLogsModule } from "./modules/app-logs.types.js"; @@ -90,6 +93,8 @@ export interface Base44Client { appLogs: AppLogsModule; /** {@link AuthModule | Auth module} for user authentication and management. */ auth: AuthModule; + /** {@link UserConnectorsModule | Connectors module} for app-user OAuth flows. */ + connectors: UserConnectorsModule; /** {@link EntitiesModule | Entities module} for CRUD operations on your data models. */ entities: EntitiesModule; /** {@link FunctionsModule | Functions module} for invoking custom backend functions. */ diff --git a/src/index.ts b/src/index.ts index ec58617..9437f26 100644 --- a/src/index.ts +++ b/src/index.ts @@ -101,7 +101,10 @@ export type { AppLogsModule } from "./modules/app-logs.types.js"; export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js"; -export type { ConnectorsModule } from "./modules/connectors.types.js"; +export type { + ConnectorsModule, + UserConnectorsModule, +} from "./modules/connectors.types.js"; export type { CustomIntegrationsModule, diff --git a/src/modules/connectors.ts b/src/modules/connectors.ts index 2d3ab19..6f518ac 100644 --- a/src/modules/connectors.ts +++ b/src/modules/connectors.ts @@ -4,6 +4,7 @@ import { ConnectorAccessTokenResponse, ConnectorConnectionResponse, ConnectorsModule, + UserConnectorsModule, } from "./connectors.types.js"; /** @@ -58,3 +59,56 @@ export function createConnectorsModule( }, }; } + +/** + * Creates the user-scoped Connectors module (app-user OAuth flows). + * + * @param axios - Axios instance (user-scoped client) + * @param appId - Application ID + * @returns User connectors module with app-user OAuth methods + * @internal + */ +export function createUserConnectorsModule( + axios: AxiosInstance, + appId: string +): UserConnectorsModule { + return { + async getCurrentAppUserAccessToken( + connectorId: string + ): Promise { + if (!connectorId || typeof connectorId !== "string") { + throw new Error("Connector ID is required and must be a string"); + } + + const response = await axios.get( + `/apps/${appId}/app-user-auth/connectors/${connectorId}/token` + ); + + const data = response as unknown as { access_token: string }; + return data.access_token; + }, + + async connectAppUser(connectorId: string): Promise { + if (!connectorId || typeof connectorId !== "string") { + throw new Error("Connector ID is required and must be a string"); + } + + const response = await axios.post( + `/apps/${appId}/app-user-auth/connectors/${connectorId}/initiate` + ); + + const data = response as unknown as { redirect_url: string }; + return data.redirect_url; + }, + + async disconnectAppUser(connectorId: string): Promise { + if (!connectorId || typeof connectorId !== "string") { + throw new Error("Connector ID is required and must be a string"); + } + + await axios.delete( + `/apps/${appId}/app-user-auth/connectors/${connectorId}` + ); + }, + }; +} diff --git a/src/modules/connectors.types.ts b/src/modules/connectors.types.ts index ff403ef..0c5a2c1 100644 --- a/src/modules/connectors.types.ts +++ b/src/modules/connectors.types.ts @@ -39,7 +39,7 @@ export interface ConnectorConnectionResponse { } /** - * Connectors module for managing OAuth tokens for external services. + * Connectors module for managing app-scoped OAuth tokens for external services. * * This module allows you to retrieve OAuth access tokens for external services that the app has connected to. Connectors are app-scoped. When an app builder connects an integration like Google Calendar, Slack, or GitHub, all users of the app share that same connection. * @@ -233,3 +233,76 @@ export interface ConnectorsModule { integrationType: ConnectorIntegrationType, ): Promise; } + +/** + * User-scoped connectors module for managing app-user OAuth connections. + * + * This module provides methods for app-user OAuth flows: initiating an OAuth connection, + * retrieving the end user's access token, and disconnecting the end user's connection. + * + * Unlike {@link ConnectorsModule | ConnectorsModule} which manages app-scoped tokens, + * this module manages tokens scoped to individual end users. Methods are keyed on + * the connector ID (the OrgConnector's database ID) rather than the integration type. + * + * Available via `base44.connectors`. + */ +export interface UserConnectorsModule { + /** + * Retrieves an OAuth access token for an end user's connection to a specific connector. + * + * Returns the OAuth token string that belongs to the currently authenticated end user + * for the specified connector. + * + * @param connectorId - The connector ID (OrgConnector database ID). + * @returns Promise resolving to the access token string. + * + * @example + * ```typescript + * // Get the end user's access token for a connector + * const token = await base44.connectors.getCurrentAppUserAccessToken('abc123def'); + * + * const response = await fetch('https://www.googleapis.com/calendar/v3/calendars/primary/events', { + * headers: { 'Authorization': `Bearer ${token}` } + * }); + * ``` + */ + getCurrentAppUserAccessToken(connectorId: string): Promise; + + /** + * Initiates the app-user OAuth flow for a specific connector. + * + * Returns a redirect URL that the end user should be navigated to in order to + * authenticate with the external service. The scopes and integration type are + * derived from the connector configuration server-side. + * + * @param connectorId - The connector ID (OrgConnector database ID). + * @returns Promise resolving to the redirect URL string. + * + * @example + * ```typescript + * // Start OAuth for the end user + * const redirectUrl = await base44.connectors.connectAppUser('abc123def'); + * + * // Redirect the user to the OAuth provider + * window.location.href = redirectUrl; + * ``` + */ + connectAppUser(connectorId: string): Promise; + + /** + * Disconnects an end user's OAuth connection for a specific connector. + * + * Removes the stored OAuth credentials for the currently authenticated end user's + * connection to the specified connector. + * + * @param connectorId - The connector ID (OrgConnector database ID). + * @returns Promise resolving when the connection has been removed. + * + * @example + * ```typescript + * // Disconnect the end user's connection + * await base44.connectors.disconnectAppUser('abc123def'); + * ``` + */ + disconnectAppUser(connectorId: string): Promise; +}