diff --git a/apps/webapp/app/services/externalApis/apiAuthenticationRepository.server.ts b/apps/webapp/app/services/externalApis/apiAuthenticationRepository.server.ts index 449d03d77..ef26d6935 100644 --- a/apps/webapp/app/services/externalApis/apiAuthenticationRepository.server.ts +++ b/apps/webapp/app/services/externalApis/apiAuthenticationRepository.server.ts @@ -191,16 +191,19 @@ export class APIAuthenticationRepository { ? process.env[authenticationMethod.config.appHostEnvName] : env.APP_ORIGIN; - const token = await grantOAuth2Token({ + const params = { tokenUrl: authenticationMethod.config.token.url, clientId: getClientConfig.id, clientSecret: getClientConfig.secret, code, callbackUrl: `${callbackHostName}/resources/connection/oauth2/callback`, - scopes, + requestedScopes: scopes, scopeSeparator: authenticationMethod.config.authorization.scopeSeparator, - }); + }; + const token = await (authenticationMethod.config.token.grantToken + ? authenticationMethod.config.token.grantToken(params) + : grantOAuth2Token(params)); console.log("token", token); diff --git a/apps/webapp/app/services/externalApis/apiCatalog.ts b/apps/webapp/app/services/externalApis/apiCatalog.ts index 1577e7860..d49d620bc 100644 --- a/apps/webapp/app/services/externalApis/apiCatalog.ts +++ b/apps/webapp/app/services/externalApis/apiCatalog.ts @@ -48,7 +48,7 @@ const slack: ExternalAPI = { clientSecret, code, callbackUrl, - scopes, + requestedScopes, }) => { return grantOAuth2Token({ tokenUrl, @@ -56,7 +56,7 @@ const slack: ExternalAPI = { clientSecret, code, callbackUrl, - scopes, + requestedScopes, scopeSeparator: " ", }); }, diff --git a/apps/webapp/app/services/externalApis/oauth2.server.ts b/apps/webapp/app/services/externalApis/oauth2.server.ts index d1788013a..262961a07 100644 --- a/apps/webapp/app/services/externalApis/oauth2.server.ts +++ b/apps/webapp/app/services/externalApis/oauth2.server.ts @@ -78,16 +78,24 @@ export async function grantOAuth2Token({ clientSecret, code, callbackUrl, - scopes, + requestedScopes, scopeSeparator, + accessTokenKey = "access_token", + refreshTokenKey = "refresh_token", + expiresAtKey = "expires_at", + scopeKey = "scope", }: { tokenUrl: string; clientId: string; clientSecret: string; code: string; callbackUrl: string; - scopes: string[]; + requestedScopes: string[]; scopeSeparator: string; + accessTokenKey?: string; + refreshTokenKey?: string; + expiresAtKey?: string; + scopeKey?: string; }): Promise { //create the oauth2 client const tokenUrlObj = new URL(tokenUrl); @@ -109,22 +117,29 @@ export async function grantOAuth2Token({ const token = await simpleOAuthClient.getToken({ code, redirect_uri: callbackUrl, - scope: scopes.join(scopeSeparator), + scope: requestedScopes.join(scopeSeparator), }); - if (typeof token.token.access_token !== "string") { + const accessTokenValue = token.token[accessTokenKey]; + if (typeof accessTokenValue !== "string") { throw new Error("Invalid access token"); } - let actualScopes = scopes; - if (typeof token.token.scope === "string") { - actualScopes = token.token.scope.split(scopeSeparator); + let actualScopes = requestedScopes; + if (typeof token.token[scopeKey] === "string") { + actualScopes = (token.token[scopeKey] as string).split(scopeSeparator); } + const refreshToken = token.token[refreshTokenKey] as string | undefined; + const expiresAt = token.token[expiresAtKey] as string | undefined; + const accessToken: AccessToken = { type: "oauth2", - access_token: token.token.access_token, + accessToken: accessTokenValue, + refreshToken, + expiresAt, scopes: actualScopes, + raw: token.token, }; return accessToken; diff --git a/apps/webapp/app/services/externalApis/types.ts b/apps/webapp/app/services/externalApis/types.ts index bb47f27e0..78e905365 100644 --- a/apps/webapp/app/services/externalApis/types.ts +++ b/apps/webapp/app/services/externalApis/types.ts @@ -1,3 +1,5 @@ +import { z } from "zod"; + export type ExternalAPI = { /** Used to uniquely identify an API */ identifier: string; @@ -57,7 +59,7 @@ export type APIAuthenticationMethodOAuth2 = { clientSecret: string; code: string; callbackUrl: string; - scopes: string[]; + requestedScopes: string[]; }) => Promise; }; /** Refresh is how a token is refreshed */ @@ -89,10 +91,14 @@ type Scope = { paramName?: string; }; -export type AccessToken = OAuth2AccessToken; +const OAuth2AccessTokenSchema = z.object({ + type: z.literal("oauth2"), + accessToken: z.string(), + expiresAt: z.string().optional(), + refreshToken: z.string().optional(), + scopes: z.array(z.string()).optional(), + raw: z.any(), +}); -type OAuth2AccessToken = { - type: "oauth2"; - access_token: string; - scopes?: string[]; -}; +export const AccessTokenSchema = OAuth2AccessTokenSchema; +export type AccessToken = z.infer;