diff --git a/package.json b/package.json index 4a574c1a..ec257be5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@internxt/sdk", "author": "Internxt ", - "version": "1.24.0", + "version": "1.25.0", "description": "An sdk for interacting with Internxt's services", "repository": { "type": "git", diff --git a/src/auth/index.ts b/src/auth/index.ts index a27914ca..e3bb4146 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1,6 +1,7 @@ import { ApiSecurity, ApiUrl, AppDetails } from '../shared'; import { basicHeaders, headersWithToken } from '../shared/headers'; import { HttpClient } from '../shared/http/client'; +import { HttpClientOptions } from '../shared/http/types'; import { TeamsSettings } from '../shared/types/teams'; import { UserSettings } from '../shared/types/userSettings'; import { @@ -29,12 +30,12 @@ export class Auth { private readonly apiSecurity?: ApiSecurity; private readonly apiUrl: string; - public static client(apiUrl: ApiUrl, appDetails: AppDetails, apiSecurity?: ApiSecurity) { - return new Auth(apiUrl, appDetails, apiSecurity); + public static client(apiUrl: ApiUrl, appDetails: AppDetails, apiSecurity?: ApiSecurity, options?: HttpClientOptions) { + return new Auth(apiUrl, appDetails, apiSecurity, options); } - private constructor(apiUrl: ApiUrl, appDetails: AppDetails, apiSecurity?: ApiSecurity) { - this.client = HttpClient.create(apiUrl, apiSecurity?.unauthorizedCallback, apiSecurity?.retryOptions); + private constructor(apiUrl: ApiUrl, appDetails: AppDetails, apiSecurity?: ApiSecurity, options?: HttpClientOptions) { + this.client = HttpClient.create(apiUrl, apiSecurity?.unauthorizedCallback, apiSecurity?.retryOptions, options); this.appDetails = appDetails; this.apiSecurity = apiSecurity; this.apiUrl = apiUrl; diff --git a/src/shared/http/client.ts b/src/shared/http/client.ts index 0eee6e38..08180417 100644 --- a/src/shared/http/client.ts +++ b/src/shared/http/client.ts @@ -1,6 +1,6 @@ import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios'; import { AxiosResponseError, AxiosUnknownError } from '../types/errors'; -import { Headers, Parameters, RequestCanceler, URL, UnauthorizedCallback } from './types'; +import { Headers, HttpClientOptions, Parameters, RequestCanceler, URL, UnauthorizedCallback } from './types'; import { RetryOptions, retryWithBackoff } from './retryWithBackoff'; export { RequestCanceler } from './types'; @@ -46,16 +46,27 @@ export class HttpClient { HttpClient.globalRetryOptions = (options ?? {}) as RetryOptions; } - public static create(baseURL: URL, unauthorizedCallback?: UnauthorizedCallback, retryOptions?: RetryOptions) { + public static create( + baseURL: URL, + unauthorizedCallback?: UnauthorizedCallback, + retryOptions?: RetryOptions, + options?: HttpClientOptions, + ) { if (unauthorizedCallback === undefined) { unauthorizedCallback = () => null; } - return new HttpClient(baseURL, unauthorizedCallback, retryOptions); + return new HttpClient(baseURL, unauthorizedCallback, retryOptions, options); } - private constructor(baseURL: URL, unauthorizedCallback: UnauthorizedCallback, retryOptions?: RetryOptions) { + private constructor( + baseURL: URL, + unauthorizedCallback: UnauthorizedCallback, + retryOptions?: RetryOptions, + options?: HttpClientOptions, + ) { this.axios = axios.create({ baseURL: baseURL, + withCredentials: options?.withCredentials ?? false, }); this.unauthorizedCallback = unauthorizedCallback; this.retryOptions = retryOptions; diff --git a/src/shared/http/types.ts b/src/shared/http/types.ts index 1280a08f..023ba347 100644 --- a/src/shared/http/types.ts +++ b/src/shared/http/types.ts @@ -7,3 +7,10 @@ export interface RequestCanceler { } export type UnauthorizedCallback = () => void; + +export interface HttpClientOptions { + /** + * Sends and stores cookies on cross-origin browser requests. The API must allow credentials on CORS + */ + withCredentials?: boolean; +} diff --git a/test/auth/index.test.ts b/test/auth/index.test.ts index 7db6e128..f58f59c2 100644 --- a/test/auth/index.test.ts +++ b/test/auth/index.test.ts @@ -23,6 +23,22 @@ describe('# auth service tests', () => { vi.restoreAllMocks(); }); + describe('-> client options', () => { + it('when credentials are enabled, then requests are sent with credentials', () => { + const auth = Auth.client('url', { clientName: 'c-name', clientVersion: '0.1' }, undefined, { + withCredentials: true, + }); + + expect(getAxiosDefaults(auth).withCredentials).toBe(true); + }); + + it('when no options are given, then requests are sent without credentials', () => { + const auth = Auth.client('url', { clientName: 'c-name', clientVersion: '0.1' }); + + expect(getAxiosDefaults(auth).withCredentials).toBe(false); + }); + }); + describe('-> register use case', () => { it('Should have all the correct params on call', async () => { // Arrange @@ -1158,3 +1174,8 @@ function forbiddenResponseError(responseBody: object): AxiosResponseError { const response = { status: 403, data: responseBody, headers: {} } as AxiosResponse; return new AxiosResponseError('Request failed with status code 403', 'post /auth/login', response); } + +function getAxiosDefaults(auth: Auth) { + const httpClient = (auth as unknown as { client: HttpClient }).client; + return (httpClient as unknown as { axios: { defaults: { withCredentials?: boolean } } }).axios.defaults; +} diff --git a/test/shared/http/client.test.ts b/test/shared/http/client.test.ts index bd4d4d6b..5796b04a 100644 --- a/test/shared/http/client.test.ts +++ b/test/shared/http/client.test.ts @@ -22,6 +22,17 @@ describe('HttpClient', () => { // Assert expect(createSpy).toHaveBeenCalledWith({ baseURL: 'my-url', + withCredentials: false, + }); + }); + it('when credentials are enabled, then the axios instance is created with withCredentials', () => { + const createSpy = vi.spyOn(axios, 'create'); + + HttpClient.create('my-url', undefined, undefined, { withCredentials: true }); + + expect(createSpy).toHaveBeenCalledWith({ + baseURL: 'my-url', + withCredentials: true, }); }); });