diff --git a/src/errors/index.ts b/src/errors/index.ts index 7e37953d..8d877d32 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -100,9 +100,9 @@ export class AccessTokenError extends SdkError { } /** - * Enum representing error codes related to federated connection access tokens. + * Enum representing error codes related to access tokens for connections. */ -export enum FederatedConnectionAccessTokenErrorCode { +export enum AccessTokenForConnectionErrorCode { /** * The session is missing. */ @@ -120,24 +120,24 @@ export enum FederatedConnectionAccessTokenErrorCode { } /** - * Error class representing an access token error for federated connections. + * Error class representing an access token for connection error. * Extends the `SdkError` class. */ -export class FederatedConnectionsAccessTokenError extends SdkError { +export class AccessTokenForConnectionError extends SdkError { /** * The error code associated with the access token error. */ public code: string; /** - * Constructs a new `FederatedConnectionsAccessTokenError` instance. + * Constructs a new `AccessTokenForConnectionError` instance. * * @param code - The error code. * @param message - The error message. */ constructor(code: string, message: string) { super(message); - this.name = "FederatedConnectionAccessTokenError"; + this.name = "AccessTokenForConnectionError"; this.code = code; } } diff --git a/src/server/auth-client.test.ts b/src/server/auth-client.test.ts index 9cdecb8f..07571129 100644 --- a/src/server/auth-client.test.ts +++ b/src/server/auth-client.test.ts @@ -4154,8 +4154,8 @@ ca/T0LLtgmbMmxSv/MmzIg== }); }); - describe("getFederatedConnectionTokenSet", async () => { - it("should call for an access token when no federated connection token set in the session", async () => { + describe("getonnectionTokenSet", async () => { + it("should call for an access token when no connection token set in the session", async () => { const secret = await generateSecret(32); const transactionStore = new TransactionStore({ secret @@ -4192,22 +4192,22 @@ ca/T0LLtgmbMmxSv/MmzIg== expiresAt }; - const response = await authClient.getFederatedConnectionTokenSet( + const response = await authClient.getConnectionTokenSet( tokenSet, undefined, { connection: "google-oauth2", login_hint: "000100123" } ); - const [error, federatedConnectionTokenSet] = response; + const [error, connectionTokenSet] = response; expect(error).toBe(null); expect(fetchSpy).toHaveBeenCalled(); - expect(federatedConnectionTokenSet).toEqual({ + expect(connectionTokenSet).toEqual({ accessToken: DEFAULT.accessToken, connection: "google-oauth2", expiresAt: expect.any(Number) }); }); - it("should return access token from the session when federated connection token set in the session is not expired", async () => { + it("should return access token from the session when connection token set in the session is not expired", async () => { const secret = await generateSecret(32); const transactionStore = new TransactionStore({ secret @@ -4237,14 +4237,14 @@ ca/T0LLtgmbMmxSv/MmzIg== expiresAt, }; - const response = await authClient.getFederatedConnectionTokenSet( + const response = await authClient.getConnectionTokenSet( tokenSet, { connection: 'google-oauth2', accessToken: 'fc_at', expiresAt: Math.floor(Date.now() / 1000) + 86400 }, { connection: "google-oauth2", login_hint: "000100123" } ); - const [error, federatedConnectionTokenSet] = response; + const [error, connectionTokenSet] = response; expect(error).toBe(null); - expect(federatedConnectionTokenSet).toEqual({ + expect(connectionTokenSet).toEqual({ accessToken: 'fc_at', connection: "google-oauth2", expiresAt: expect.any(Number) @@ -4252,7 +4252,7 @@ ca/T0LLtgmbMmxSv/MmzIg== expect(fetchSpy).not.toHaveBeenCalled(); }); - it("should call for an access token when federated connection token set in the session is expired", async () => { + it("should call for an access token when connection token set in the session is expired", async () => { const secret = await generateSecret(32); const transactionStore = new TransactionStore({ secret @@ -4288,14 +4288,14 @@ ca/T0LLtgmbMmxSv/MmzIg== expiresAt, }; - const response = await authClient.getFederatedConnectionTokenSet( + const response = await authClient.getConnectionTokenSet( tokenSet, { connection: 'google-oauth2', accessToken: 'fc_at', expiresAt }, { connection: "google-oauth2", login_hint: "000100123" } ); - const [error, federatedConnectionTokenSet] = response; + const [error, connectionTokenSet] = response; expect(error).toBe(null); - expect(federatedConnectionTokenSet).toEqual({ + expect(connectionTokenSet).toEqual({ accessToken: DEFAULT.accessToken, connection: "google-oauth2", expiresAt: expect.any(Number) @@ -4334,12 +4334,12 @@ ca/T0LLtgmbMmxSv/MmzIg== expiresAt }; - const [error, federatedConnectionTokenSet] = - await authClient.getFederatedConnectionTokenSet(tokenSet, undefined, { + const [error, connectionTokenSet] = + await authClient.getConnectionTokenSet(tokenSet, undefined, { connection: "google-oauth2" }); expect(error?.code).toEqual("discovery_error"); - expect(federatedConnectionTokenSet).toBeNull(); + expect(connectionTokenSet).toBeNull(); }); it("should return an error if the token set does not contain a refresh token", async () => { @@ -4370,12 +4370,12 @@ ca/T0LLtgmbMmxSv/MmzIg== expiresAt }; - const [error, federatedConnectionTokenSet] = - await authClient.getFederatedConnectionTokenSet(tokenSet, undefined, { + const [error, connectionTokenSet] = + await authClient.getConnectionTokenSet(tokenSet, undefined, { connection: "google-oauth2" }); expect(error?.code).toEqual("missing_refresh_token"); - expect(federatedConnectionTokenSet).toBeNull(); + expect(connectionTokenSet).toBeNull(); }); }); }); diff --git a/src/server/auth-client.ts b/src/server/auth-client.ts index bc6cce64..f45d1d48 100644 --- a/src/server/auth-client.ts +++ b/src/server/auth-client.ts @@ -9,16 +9,16 @@ import { AuthorizationError, BackchannelLogoutError, DiscoveryError, - FederatedConnectionAccessTokenErrorCode, - FederatedConnectionsAccessTokenError, + AccessTokenForConnectionError, + AccessTokenForConnectionErrorCode, InvalidStateError, MissingStateError, OAuth2Error, SdkError } from "../errors"; import { - FederatedConnectionTokenSet, - GetFederatedConnectionAccessTokenOptions, + ConnectionTokenSet, + AccessTokenForConnectionOptions, LogoutToken, SessionData, TokenSet @@ -975,49 +975,49 @@ export class AuthClient { } /** - * Exchanges a refresh token for a federated connection access token. + * Exchanges a refresh token for an access token for a connection. * * This method performs a token exchange using the provided refresh token and connection details. * It first checks if the refresh token is present in the `tokenSet`. If not, it returns an error. * Then, it constructs the necessary parameters for the token exchange request and performs * the request to the authorization server's token endpoint. * - * @returns {Promise<[SdkError, null] | [null, FederatedConnectionTokenSet]>} A promise that resolves to a tuple. + * @returns {Promise<[SdkError, null] | [null, ConnectionTokenSet]>} A promise that resolves to a tuple. * The first element is either an `SdkError` if an error occurred, or `null` if the request was successful. - * The second element is either `null` if an error occurred, or a `FederatedConnectionTokenSet` object + * The second element is either `null` if an error occurred, or a `ConnectionTokenSet` object * containing the access token, expiration time, and scope if the request was successful. * - * @throws {FederatedConnectionsAccessTokenError} If the refresh token is missing or if there is an error during the token exchange process. + * @throws {AccessTokenForConnectionError} If the refresh token is missing or if there is an error during the token exchange process. */ - async getFederatedConnectionTokenSet( + async getConnectionTokenSet( tokenSet: TokenSet, - federatedConnectionTokenSet: FederatedConnectionTokenSet | undefined, - options: GetFederatedConnectionAccessTokenOptions - ): Promise<[SdkError, null] | [null, FederatedConnectionTokenSet]> { + connectionTokenSet: ConnectionTokenSet | undefined, + options: AccessTokenForConnectionOptions + ): Promise<[SdkError, null] | [null, ConnectionTokenSet]> { // If we do not have a refresh token - // and we do not have a federated connection token set in the cache or the one we have is expired, + // and we do not have a connection token set in the cache or the one we have is expired, // there is noting to retrieve and we return an error. if ( !tokenSet.refreshToken && - (!federatedConnectionTokenSet || - federatedConnectionTokenSet.expiresAt <= Date.now() / 1000) + (!connectionTokenSet || + connectionTokenSet.expiresAt <= Date.now() / 1000) ) { return [ - new FederatedConnectionsAccessTokenError( - FederatedConnectionAccessTokenErrorCode.MISSING_REFRESH_TOKEN, - "A refresh token was not present, Federated Connection Access Token requires a refresh token. The user needs to re-authenticate." + new AccessTokenForConnectionError( + AccessTokenForConnectionErrorCode.MISSING_REFRESH_TOKEN, + "A refresh token was not present, Connection Access Token requires a refresh token. The user needs to re-authenticate." ), null ]; } // If we do have a refresh token, - // and we do not have a federated connection token set in the cache or the one we have is expired, - // we need to exchange the refresh token for a federated connection access token. + // and we do not have a connection token set in the cache or the one we have is expired, + // we need to exchange the refresh token for a connection access token. if ( tokenSet.refreshToken && - (!federatedConnectionTokenSet || - federatedConnectionTokenSet.expiresAt <= Date.now() / 1000) + (!connectionTokenSet || + connectionTokenSet.expiresAt <= Date.now() / 1000) ) { const params = new URLSearchParams(); @@ -1063,9 +1063,9 @@ export class AuthClient { } catch (err) { console.error(err); return [ - new FederatedConnectionsAccessTokenError( - FederatedConnectionAccessTokenErrorCode.FAILED_TO_EXCHANGE, - "There was an error trying to exchange the refresh token for a federated connection access token. Check the server logs for more information." + new AccessTokenForConnectionError( + AccessTokenForConnectionErrorCode.FAILED_TO_EXCHANGE, + "There was an error trying to exchange the refresh token for a connection access token. Check the server logs for more information." ), null ]; @@ -1084,9 +1084,9 @@ export class AuthClient { ]; } - return [null, federatedConnectionTokenSet] as [ + return [null, connectionTokenSet] as [ null, - FederatedConnectionTokenSet + ConnectionTokenSet ]; } } diff --git a/src/server/client.ts b/src/server/client.ts index ea4d60c1..ed5e80e1 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -6,11 +6,11 @@ import { NextApiRequest, NextApiResponse } from "next/types"; import { AccessTokenError, AccessTokenErrorCode, - FederatedConnectionAccessTokenErrorCode, - FederatedConnectionsAccessTokenError + AccessTokenForConnectionError, + AccessTokenForConnectionErrorCode, } from "../errors"; import { - GetFederatedConnectionAccessTokenOptions, + AccessTokenForConnectionOptions, SessionData, SessionDataStore } from "../types"; @@ -364,47 +364,47 @@ export class Auth0Client { } /** - * Retrieves an access token for a federated connection. + * Retrieves an access token for a connection. * * This method can be used in Server Components, Server Actions, and Route Handlers in the **App Router**. * - * NOTE: Server Components cannot set cookies. Calling `getFederatedConnectionAccessToken()` in a Server Component will cause the access token to be refreshed, if it is expired, and the updated token set will not to be persisted. - * It is recommended to call `getFederatedConnectionAccessToken(req, res)` in the middleware if you need to retrieve the access token in a Server Component to ensure the updated token set is persisted. + * NOTE: Server Components cannot set cookies. Calling `getAccessTokenForConnection()` in a Server Component will cause the access token to be refreshed, if it is expired, and the updated token set will not to be persisted. + * It is recommended to call `getAccessTokenForConnection(req, res)` in the middleware if you need to retrieve the access token in a Server Component to ensure the updated token set is persisted. */ - async getFederatedConnectionAccessToken( - options: GetFederatedConnectionAccessTokenOptions + async getAccessTokenForConnection( + options: AccessTokenForConnectionOptions ): Promise<{ token: string; expiresAt: number }>; /** - * Retrieves an access token for a federated connection. + * Retrieves an access token for a connection. * * This method can be used in middleware and `getServerSideProps`, API routes in the **Pages Router**. */ - async getFederatedConnectionAccessToken( - options: GetFederatedConnectionAccessTokenOptions, + async getAccessTokenForConnection( + options: AccessTokenForConnectionOptions, req: PagesRouterRequest | NextRequest | undefined, res: PagesRouterResponse | NextResponse | undefined ): Promise<{ token: string; expiresAt: number }>; /** - * Retrieves an access token for a federated connection. + * Retrieves an access token for a connection. * - * This method attempts to obtain an access token for a specified federated connection. + * This method attempts to obtain an access token for a specified connection. * It first checks if a session exists, either from the provided request or from cookies. - * If no session is found, it throws a `FederatedConnectionsAccessTokenError` indicating + * If no session is found, it throws a `AccessTokenForConnectionError` indicating * that the user does not have an active session. * - * @param {GetFederatedConnectionAccessTokenOptions} options - Options for retrieving a federated connection access token. + * @param {AccessTokenForConnectionOptions} options - Options for retrieving an access token for a connection. * @param {PagesRouterRequest | NextRequest} [req] - An optional request object from which to extract session information. * @param {PagesRouterResponse | NextResponse} [res] - An optional response object from which to extract session information. * - * @throws {FederatedConnectionsAccessTokenError} If the user does not have an active session. + * @throws {AccessTokenForConnectionError} If the user does not have an active session. * @throws {Error} If there is an error during the token exchange process. * * @returns {Promise<{ token: string; expiresAt: number; scope?: string }} An object containing the access token and its expiration time. */ - async getFederatedConnectionAccessToken( - options: GetFederatedConnectionAccessTokenOptions, + async getAccessTokenForConnection( + options: AccessTokenForConnectionOptions, req?: PagesRouterRequest | NextRequest, res?: PagesRouterResponse | NextResponse ): Promise<{ token: string; expiresAt: number; scope?: string }> { @@ -412,19 +412,19 @@ export class Auth0Client { req ? await this.getSession(req) : await this.getSession(); if (!session) { - throw new FederatedConnectionsAccessTokenError( - FederatedConnectionAccessTokenErrorCode.MISSING_SESSION, + throw new AccessTokenForConnectionError( + AccessTokenForConnectionErrorCode.MISSING_SESSION, "The user does not have an active session." ); } - // Find the federated connection token set in the session - const existingTokenSet = session.federatedConnectionTokenSets?.find( + // Find the connection token set in the session + const existingTokenSet = session.connectionTokenSets?.find( (tokenSet) => tokenSet.connection === options.connection ); const [error, retrievedTokenSet] = - await this.authClient.getFederatedConnectionTokenSet( + await this.authClient.getConnectionTokenSet( session.tokenSet, existingTokenSet, options @@ -434,7 +434,7 @@ export class Auth0Client { throw error; } - // If we didnt have a corresponding federated connection token set in the session + // If we didnt have a corresponding connection token set in the session // or if the one we have in the session does not match the one we received // We want to update the store incase we retrieved a token set. if ( @@ -446,18 +446,18 @@ export class Auth0Client { ) { let tokenSets; - // If we already had the federated connection token set in the session + // If we already had the connection token set in the session // we need to update the item in the array // If not, we need to add it. if (existingTokenSet) { - tokenSets = session.federatedConnectionTokenSets?.map((tokenSet) => + tokenSets = session.connectionTokenSets?.map((tokenSet) => tokenSet.connection === options.connection ? retrievedTokenSet : tokenSet ); } else { tokenSets = [ - ...(session.federatedConnectionTokenSets || []), + ...(session.connectionTokenSets || []), retrievedTokenSet ]; } @@ -465,7 +465,7 @@ export class Auth0Client { await this.saveToSession( { ...session, - federatedConnectionTokenSets: tokenSets + connectionTokenSets: tokenSets }, req, res diff --git a/src/server/session/stateless-session-store.test.ts b/src/server/session/stateless-session-store.test.ts index 0a19c3bf..33619ea4 100644 --- a/src/server/session/stateless-session-store.test.ts +++ b/src/server/session/stateless-session-store.test.ts @@ -46,7 +46,7 @@ describe("Stateless Session Store", async () => { expect(await sessionStore.get(requestCookies)).toBeNull(); }); - it("should return the decrypted session cookie if it exists with federated connection", async () => { + it("should return the decrypted session cookie if it exists with connection", async () => { const secret = await generateSecret(32); const session: SessionData = { user: { sub: "user_123" }, diff --git a/src/server/session/stateless-session-store.ts b/src/server/session/stateless-session-store.ts index d564d7c0..7bbbe681 100644 --- a/src/server/session/stateless-session-store.ts +++ b/src/server/session/stateless-session-store.ts @@ -1,6 +1,6 @@ import type { JWTPayload } from "jose"; -import { FederatedConnectionTokenSet, SessionData } from "../../types"; +import { ConnectionTokenSet, SessionData } from "../../types"; import * as cookies from "../cookies"; import { AbstractSessionStore, @@ -18,7 +18,7 @@ interface StatelessSessionStoreOptions { } export class StatelessSessionStore extends AbstractSessionStore { - federatedConnectionTokenSetsCookieName = "__FC"; + connectionTokenSetsCookieName = "__FC"; constructor({ secret, @@ -48,13 +48,13 @@ export class StatelessSessionStore extends AbstractSessionStore { this.secret ); - // As federated connection access tokens are stored in seperate cookies, - // we need to get all cookies and only use those that are prefixed with `this.federatedConnectionTokenSetsCookieName` - const federatedConnectionTokenSets = await Promise.all( - this.getFederatedConnectionTokenSetsCookies(reqCookies).map( - (fcatCookie) => - cookies.decrypt<FederatedConnectionTokenSet>( - fcatCookie.value, + // As connection access tokens are stored in seperate cookies, + // we need to get all cookies and only use those that are prefixed with `this.connectionTokenSetsCookieName` + const connectionTokenSets = await Promise.all( + this.getConnectionTokenSetsCookies(reqCookies).map( + (cookie) => + cookies.decrypt<ConnectionTokenSet>( + cookie.value, this.secret ) ) @@ -62,9 +62,9 @@ export class StatelessSessionStore extends AbstractSessionStore { return { ...originalSession, - // Ensure that when there are no federated connection token sets, we omit the property. - ...(federatedConnectionTokenSets.length - ? { federatedConnectionTokenSets } + // Ensure that when there are no connection token sets, we omit the property. + ...(connectionTokenSets.length + ? { connectionTokenSets } : {}) }; } @@ -77,7 +77,7 @@ export class StatelessSessionStore extends AbstractSessionStore { resCookies: cookies.ResponseCookies, session: SessionData ) { - const { federatedConnectionTokenSets, ...originalSession } = session; + const { connectionTokenSets, ...originalSession } = session; const maxAge = this.calculateMaxAge(session.internal.createdAt); await this.storeInCookie( @@ -88,15 +88,15 @@ export class StatelessSessionStore extends AbstractSessionStore { maxAge ); - // Store federated connection access tokens, each in its own cookie - if (federatedConnectionTokenSets?.length) { + // Store connection access tokens, each in its own cookie + if (connectionTokenSets?.length) { await Promise.all( - federatedConnectionTokenSets.map((federatedConnectionTokenSet, index) => + connectionTokenSets.map((connectionTokenSet, index) => this.storeInCookie( reqCookies, resCookies, - federatedConnectionTokenSet, - `${this.federatedConnectionTokenSetsCookieName}_${index}`, + connectionTokenSet, + `${this.connectionTokenSetsCookieName}_${index}`, maxAge ) ) @@ -109,7 +109,7 @@ export class StatelessSessionStore extends AbstractSessionStore { resCookies: cookies.ResponseCookies ) { resCookies.delete(this.sessionCookieName); - this.getFederatedConnectionTokenSetsCookies(reqCookies).forEach((cookie) => + this.getConnectionTokenSetsCookies(reqCookies).forEach((cookie) => resCookies.delete(cookie.name) ); } @@ -157,13 +157,13 @@ export class StatelessSessionStore extends AbstractSessionStore { } } - private getFederatedConnectionTokenSetsCookies( + private getConnectionTokenSetsCookies( cookies: cookies.RequestCookies | cookies.ResponseCookies ) { return cookies .getAll() .filter((cookie) => - cookie.name.startsWith(this.federatedConnectionTokenSetsCookieName) + cookie.name.startsWith(this.connectionTokenSetsCookieName) ); } } diff --git a/src/types/index.ts b/src/types/index.ts index 3bf9a4e9..a90d2aa8 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -5,7 +5,7 @@ export interface TokenSet { expiresAt: number; // the time at which the access token expires in seconds since epoch } -export interface FederatedConnectionTokenSet { +export interface ConnectionTokenSet { accessToken: string; scope?: string; expiresAt: number; // the time at which the access token expires in seconds since epoch @@ -22,7 +22,7 @@ export interface SessionData { // the time at which the session was created in seconds since epoch createdAt: number; }; - federatedConnectionTokenSets?: FederatedConnectionTokenSet[]; + connectionTokenSets?: ConnectionTokenSet[]; [key: string]: unknown; } @@ -96,9 +96,9 @@ export type { } from "../server/transaction-store"; /** - * Options for retrieving a federated connection access token. + * Options for retrieving a connection access token. */ -export interface GetFederatedConnectionAccessTokenOptions { +export interface AccessTokenForConnectionOptions { /** * The connection name for while you want to retrieve the access token. */