diff --git a/src/management/__generated/managers/users-manager.ts b/src/management/__generated/managers/users-manager.ts index 03ab9dbf4d..182e8a4486 100644 --- a/src/management/__generated/managers/users-manager.ts +++ b/src/management/__generated/managers/users-manager.ts @@ -16,6 +16,7 @@ import type { GetUserOrganizations200Response, GetUsers200Response, GetUsers200ResponseOneOfInner, + ListUserConnectedAccountsResponseContent, PatchAuthenticationMethodsByAuthenticationMethodIdRequest, PostAuthenticationMethods201Response, PostAuthenticationMethodsRequest, @@ -52,6 +53,7 @@ import type { DeleteUsersByIdRequest, GetAuthenticationMethodsRequest, GetAuthenticationMethodsByAuthenticationMethodIdRequest, + GetConnectedAccountsRequest, GetEnrollmentsRequest, GetFederatedConnectionsTokensetsRequest, GetLogsByUserRequest, @@ -447,6 +449,44 @@ export class UsersManager extends BaseAPI { return runtime.JSONApiResponse.fromResponse(response); } + /** + * Retrieve all connected accounts associated with the user. + * Get a User's Connected Accounts + * + * @throws {RequiredError} + */ + async getConnectedAccounts( + requestParameters: GetConnectedAccountsRequest, + initOverrides?: InitOverride + ): Promise> { + runtime.validateRequiredRequestParams(requestParameters, ['id']); + + const queryParameters = runtime.applyQueryParams(requestParameters, [ + { + key: 'from', + config: {}, + }, + { + key: 'take', + config: {}, + }, + ]); + + const response = await this.request( + { + path: `/users/{id}/connected-accounts`.replace( + '{id}', + encodeURIComponent(String(requestParameters.id)) + ), + method: 'GET', + query: queryParameters, + }, + initOverrides + ); + + return runtime.JSONApiResponse.fromResponse(response); + } + /** * Retrieve the first confirmed Guardian enrollment for a user. * Get the First Confirmed Multi-factor Authentication Enrollment diff --git a/src/management/__generated/models/index.ts b/src/management/__generated/models/index.ts index 66c620d70b..6535573468 100644 --- a/src/management/__generated/models/index.ts +++ b/src/management/__generated/models/index.ts @@ -3217,6 +3217,59 @@ export interface ClientUpdateSignedRequestObject { */ credentials?: Array; } +/** + * + */ +export interface ConnectedAccount { + /** + * The unique identifier for the connected account. + * + */ + id: string; + /** + * The name of the connection associated with the account. + * + */ + connection: string; + /** + * The unique identifier of the connection associated with the account. + * + */ + connection_id: string; + /** + * The authentication strategy used by the connection. + * + */ + strategy: string; + /** + */ + access_type: ConnectedAccountAccessTypeEnum; + /** + * The scopes granted for this connected account. + * + */ + scopes?: Array; + /** + * ISO 8601 timestamp when the connected account was created. + * + */ + created_at: string; + /** + * ISO 8601 timestamp when the connected account expires. + * + */ + expires_at?: string; +} + +/** + * The access type for the connected account. + */ +export const ConnectedAccountAccessTypeEnum = { + offline: 'offline', +} as const; +export type ConnectedAccountAccessTypeEnum = + (typeof ConnectedAccountAccessTypeEnum)[keyof typeof ConnectedAccountAccessTypeEnum]; + /** * */ @@ -10016,6 +10069,19 @@ export interface ListUserAttributeProfilesPaginatedResponseContent { */ user_attribute_profiles?: Array; } +/** + * + */ +export interface ListUserConnectedAccountsResponseContent { + /** + */ + connected_accounts: Array; + /** + * The token to retrieve the next page of connected accounts (if there is one) + * + */ + next?: string; +} /** * */ @@ -23736,6 +23802,26 @@ export interface GetAuthenticationMethodsByAuthenticationMethodIdRequest { */ authentication_method_id: string; } +/** + * + */ +export interface GetConnectedAccountsRequest { + /** + * ID of the user to list connected accounts for. + * + */ + id: string; + /** + * Optional Id from which to start selection. + * + */ + from?: string; + /** + * Number of results to return. Defaults to 10 with a maximum of 20 + * + */ + take?: number; +} /** * */ diff --git a/test/management/users.test.ts b/test/management/users.test.ts index 5cb75e47d4..1d04e2dcf5 100644 --- a/test/management/users.test.ts +++ b/test/management/users.test.ts @@ -1727,4 +1727,103 @@ describe('UsersManager', () => { expect(request.isDone()).toBe(true); }); }); + + describe('#getConnectedAccounts', () => { + const data = { + id: 'user_id', + }; + + let scope: nock.Scope; + + beforeEach(() => { + scope = nock(API_URL).get(`/users/${data.id}/connected-accounts`).reply(200, []); + }); + + it('should return a promise when no callback is given', (done) => { + expect(usersManager.getConnectedAccounts(data).then(() => done())).toBeInstanceOf(Promise); + }); + + it('should perform a GET request to /api/v2/users/user_id/connected-accounts', async () => { + await usersManager.getConnectedAccounts(data); + expect(scope.isDone()).toBe(true); + }); + + it('should pass any errors to the promise catch handler', async () => { + nock.cleanAll(); + + nock(API_URL).get(`/users/${data.id}/connected-accounts`).reply(500, {}); + + try { + await usersManager.getConnectedAccounts(data); + } catch (err) { + expect(err).toBeDefined(); + } + }); + + it('should include the token in the authorization header', async () => { + nock.cleanAll(); + + const request = nock(API_URL) + .get(`/users/${data.id}/connected-accounts`) + .matchHeader('authorization', `Bearer ${token}`) + .reply(200, []); + + await usersManager.getConnectedAccounts(data); + expect(request.isDone()).toBe(true); + }); + + it('should pass the body of the response to the "then" handler', async () => { + nock.cleanAll(); + + const connectedAccountsData = [ + { + id: 'conn_123', + connection: 'google-oauth2', + connection_id: 'con_abc123', + strategy: 'google-oauth2', + access_type: 'offline', + scopes: ['profile', 'email'], + created_at: '2023-01-01T00:00:00.000Z', + expires_at: '2024-01-01T00:00:00.000Z', + }, + ]; + + const response = { + connected_accounts: connectedAccountsData, + next: null, + }; + + nock(API_URL).get(`/users/${data.id}/connected-accounts`).reply(200, response); + + const connectedAccounts = await usersManager.getConnectedAccounts(data); + expect(connectedAccounts.data.connected_accounts).toBeInstanceOf(Array); + expect(connectedAccounts.data.connected_accounts.length).toBe(connectedAccountsData.length); + expect(connectedAccounts.data.connected_accounts[0].id).toBe(connectedAccountsData[0].id); + expect(connectedAccounts.data.connected_accounts[0].connection).toBe(connectedAccountsData[0].connection); + expect(connectedAccounts.data.connected_accounts[0].strategy).toBe(connectedAccountsData[0].strategy); + }); + + it('should pass the parameters in the query-string', async () => { + nock.cleanAll(); + + const params = { + from: 'conn_123', + take: 10, + }; + const request = nock(API_URL) + .get(`/users/${data.id}/connected-accounts`) + .query(params) + .reply(200, []); + + await usersManager.getConnectedAccounts({ + id: data.id, + ...params, + }); + expect(request.isDone()).toBe(true); + }); + + it('should validate empty id', async () => { + await expect(usersManager.getConnectedAccounts({} as any)).rejects.toThrowError(); + }); + }); });