From 4380e03b7f965de6ac321027260854932353c06f Mon Sep 17 00:00:00 2001 From: Chuck MANCHUCK Reeves Date: Mon, 17 Jun 2024 17:27:28 -0400 Subject: [PATCH] feat: adding network apis --- jest.config.ts | 20 +- packages/network-client/README.md | 11 + .../__tests__/__dataSets__/get.ts | 119 +++++ .../__tests__/__dataSets__/index.ts | 9 + .../__tests__/networkClient.test.ts | 5 + packages/network-client/lib/enums/index.ts | 2 + packages/network-client/lib/enums/purpose.ts | 9 + packages/network-client/lib/enums/scope.ts | 14 + packages/network-client/lib/errors/index.ts | 4 + .../lib/errors/invalidPurposeError.ts | 8 + .../lib/errors/invalidScopeError.ts | 8 + .../lib/errors/missingPurposeError.ts | 8 + .../lib/errors/missingScopeError.ts | 8 + packages/network-client/lib/index.ts | 4 + packages/network-client/lib/networkClient.ts | 421 ++++++++++++++++++ packages/network-client/lib/types/index.ts | 2 + .../lib/types/parameters/index.ts | 2 + .../types/parameters/networkAuthParameters.ts | 18 + .../parameters/networkConfigParameters.ts | 30 ++ .../lib/types/responses/index.ts | 2 + .../types/responses/networkTokenResponse.ts | 16 + .../lib/types/responses/odicResponse.ts | 16 + packages/network-client/package.json | 46 ++ packages/network-client/tsconfig.json | 25 ++ packages/network-client/typedoc.json | 6 + .../network-number-verification/README.md | 11 + .../network-number-verification/lib/index.ts | 2 + .../lib/numberVerification.ts | 39 ++ .../lib/types/index.ts | 2 + .../lib/types/requests/index.ts | 1 + .../requests/numberVerificationRequest.ts | 6 + .../lib/types/responses/index.ts | 1 + .../responses/numberVerificationResponse.ts | 6 + .../network-number-verification/package.json | 39 ++ .../network-number-verification/tsconfig.json | 23 + .../network-number-verification/typedoc.json | 6 + packages/network-sim-swap/README.md | 11 + packages/network-sim-swap/lib/index.ts | 2 + packages/network-sim-swap/lib/simSwap.ts | 17 + packages/network-sim-swap/lib/types/index.ts | 2 + .../lib/types/parameters/index.ts | 1 + .../lib/types/parameters/simSwapParameters.ts | 13 + .../lib/types/responses/index.ts | 1 + .../lib/types/responses/simSwapResponse.ts | 6 + packages/network-sim-swap/package.json | 39 ++ packages/network-sim-swap/tsconfig.json | 25 ++ packages/network-sim-swap/typedoc.json | 6 + packages/server-client/lib/client.ts | 94 +++- .../lib/enums/AuthenticationType.ts | 20 +- 49 files changed, 1163 insertions(+), 23 deletions(-) create mode 100644 packages/network-client/README.md create mode 100644 packages/network-client/__tests__/__dataSets__/get.ts create mode 100644 packages/network-client/__tests__/__dataSets__/index.ts create mode 100644 packages/network-client/__tests__/networkClient.test.ts create mode 100644 packages/network-client/lib/enums/index.ts create mode 100644 packages/network-client/lib/enums/purpose.ts create mode 100644 packages/network-client/lib/enums/scope.ts create mode 100644 packages/network-client/lib/errors/index.ts create mode 100644 packages/network-client/lib/errors/invalidPurposeError.ts create mode 100644 packages/network-client/lib/errors/invalidScopeError.ts create mode 100644 packages/network-client/lib/errors/missingPurposeError.ts create mode 100644 packages/network-client/lib/errors/missingScopeError.ts create mode 100644 packages/network-client/lib/index.ts create mode 100644 packages/network-client/lib/networkClient.ts create mode 100644 packages/network-client/lib/types/index.ts create mode 100644 packages/network-client/lib/types/parameters/index.ts create mode 100644 packages/network-client/lib/types/parameters/networkAuthParameters.ts create mode 100644 packages/network-client/lib/types/parameters/networkConfigParameters.ts create mode 100644 packages/network-client/lib/types/responses/index.ts create mode 100644 packages/network-client/lib/types/responses/networkTokenResponse.ts create mode 100644 packages/network-client/lib/types/responses/odicResponse.ts create mode 100644 packages/network-client/package.json create mode 100644 packages/network-client/tsconfig.json create mode 100644 packages/network-client/typedoc.json create mode 100644 packages/network-number-verification/README.md create mode 100644 packages/network-number-verification/lib/index.ts create mode 100644 packages/network-number-verification/lib/numberVerification.ts create mode 100644 packages/network-number-verification/lib/types/index.ts create mode 100644 packages/network-number-verification/lib/types/requests/index.ts create mode 100644 packages/network-number-verification/lib/types/requests/numberVerificationRequest.ts create mode 100644 packages/network-number-verification/lib/types/responses/index.ts create mode 100644 packages/network-number-verification/lib/types/responses/numberVerificationResponse.ts create mode 100644 packages/network-number-verification/package.json create mode 100644 packages/network-number-verification/tsconfig.json create mode 100644 packages/network-number-verification/typedoc.json create mode 100644 packages/network-sim-swap/README.md create mode 100644 packages/network-sim-swap/lib/index.ts create mode 100644 packages/network-sim-swap/lib/simSwap.ts create mode 100644 packages/network-sim-swap/lib/types/index.ts create mode 100644 packages/network-sim-swap/lib/types/parameters/index.ts create mode 100644 packages/network-sim-swap/lib/types/parameters/simSwapParameters.ts create mode 100644 packages/network-sim-swap/lib/types/responses/index.ts create mode 100644 packages/network-sim-swap/lib/types/responses/simSwapResponse.ts create mode 100644 packages/network-sim-swap/package.json create mode 100644 packages/network-sim-swap/tsconfig.json create mode 100644 packages/network-sim-swap/typedoc.json diff --git a/jest.config.ts b/jest.config.ts index a4e56867..565d80d3 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -60,6 +60,12 @@ const config: Config.InitialOptions = { testMatch: ['/packages/media/__tests__/**/*.test.ts'], coveragePathIgnorePatterns: ['node_modules', '__tests__'], }, + { + ...projectDefault, + displayName: 'MEETINGS', + testMatch: ['/packages/meetings/__tests__/**/*.test.ts'], + coveragePathIgnorePatterns: ['node_modules', '__tests__'], + }, { ...projectDefault, displayName: 'MESSAGES', @@ -68,10 +74,20 @@ const config: Config.InitialOptions = { }, { ...projectDefault, - displayName: 'MEETINGS', - testMatch: ['/packages/meetings/__tests__/**/*.test.ts'], + displayName: 'NETWORK CLIENT', + testMatch: ['/packages/network-client/__tests__/**/*.test.ts'], coveragePathIgnorePatterns: ['node_modules', '__tests__'], }, + // { + // displayName: 'NETWORK SIM SWAP', + // testMatch: ['/packages/network-sim-swap/__tests__/**/*.test.ts'], + // coveragePathIgnorePatterns: ['node_modules', '__tests__'], + // }, + // { + // displayName: 'NETWORK NUMBER VERIFICATION', + // testMatch: ['/packages/network-sim-swap/__tests__/**/*.test.ts'], + // coveragePathIgnorePatterns: ['node_modules', '__tests__'], + // }, { ...projectDefault, displayName: 'NUMBER INSIGHT V2', diff --git a/packages/network-client/README.md b/packages/network-client/README.md new file mode 100644 index 00000000..9ba54993 --- /dev/null +++ b/packages/network-client/README.md @@ -0,0 +1,11 @@ +# `network-auth` + +> TODO: description + +## Usage + +``` +const networkAuth = require('network-auth'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/network-client/__tests__/__dataSets__/get.ts b/packages/network-client/__tests__/__dataSets__/get.ts new file mode 100644 index 00000000..62fb02ee --- /dev/null +++ b/packages/network-client/__tests__/__dataSets__/get.ts @@ -0,0 +1,119 @@ +import { + NetworkClient, + Purpose, + Scope, +} from '../../lib'; +import { SDKTestCase, testPrivateKey } from '../../../../testHelpers'; +import { AuthenticationType } from '@vonage/server-client'; + +class CIBAClient extends NetworkClient { + protected authType?: AuthenticationType = AuthenticationType.CIBA + + protected _purpose = Purpose.FRAUD_PREVENTION_AND_DETECTION; + protected _scope = Scope.NUMBER_VERIFICATION_VERIFY_READ; +} + +export default [ + { + label: 'can get CIBA request', + baseUrl: 'https://api-eu.vonage.com', + clientMethod: 'getCIBARequest', + reqHeaders: { + authorization: (value) => value.startsWith('Bearer '), + }, + client: new CIBAClient( + { + privateKey: testPrivateKey, + applicationId: 'my-application', + msisdn: '447700900000', + }, + ), + requests: [ + [ + '/oauth2/bc-authorize', + 'POST', + { + login_hint: `tel:+447700900000`, + scope: `openid dpv:${Purpose.FRAUD_PREVENTION_AND_DETECTION}#${Scope.NUMBER_VERIFICATION_VERIFY_READ}`, + }, + ], + ], + responses: [ + [ + 200, + { + auth_req_id: 'auth-req-id', + } + ], + ], + generator: false, + error: false, + expected: 'auth-req-id' + }, +// { +// label: 'error when requesting CIBA token returns 401', +// baseUrl: 'https://api-eu.vonage.com', +// clientMethod: 'getCIBAToken', +// reqHeaders: { +// authorization: (value) => value.startsWith('Bearer '), +// }, +// client: new CIBAClient( +// { +// privateKey: testPrivateKey, +// applicationId: 'my-application', +// msisdn: '447700900000', +// }, +// ), +// requests: [ +// [ +// '/oauth2/bc-authorize', +// 'POST', +// { +// login_hint: `tel:+447700900000`, +// scope: `openid dpv:${Purpose.FRAUD_PREVENTION_AND_DETECTION}#${Scope.NUMBER_VERIFICATION_VERIFY_READ}`, +// }, +// ], +// ], +// responses: [ +// [ +// 401, +// ], +// ], +// generator: false, +// error: 'A 401 was returned when trying to get authorization. Some possible reasons for this:- The application id or private key are invalid. - You have not setup the Network API correctly for your application .', +// expected: {foo: 'bar'} +// }, +// { +// label: 'error when requesting CIBA token returns 400', +// baseUrl: 'https://api-eu.vonage.com', +// clientMethod: 'getCIBAToken', +// reqHeaders: { +// authorization: (value) => value.startsWith('Bearer '), +// }, +// client: new CIBAClient( +// { +// privateKey: testPrivateKey, +// applicationId: 'my-application', +// msisdn: '447700900000', +// }, +// ), +// requests: [ +// [ +// '/oauth2/bc-authorize', +// 'POST', +// { +// login_hint: `tel:+447700900000`, +// scope: `openid dpv:${Purpose.FRAUD_PREVENTION_AND_DETECTION}#${Scope.NUMBER_VERIFICATION_VERIFY_READ}`, +// }, +// ], +// ], +// responses: [ +// [ +// 400, +// ], +// ], +// generator: false, +// error: 'It appears you have not enabled the Network API for your account. Please contact your account manager to enable this feature. ', +// expected: {foo: 'bar'} +// } +] as SDKTestCase[]; diff --git a/packages/network-client/__tests__/__dataSets__/index.ts b/packages/network-client/__tests__/__dataSets__/index.ts new file mode 100644 index 00000000..f2526346 --- /dev/null +++ b/packages/network-client/__tests__/__dataSets__/index.ts @@ -0,0 +1,9 @@ +import getTests from './get'; + +export default [ + { + name: 'GET tests', + tests: getTests, + }, +]; + diff --git a/packages/network-client/__tests__/networkClient.test.ts b/packages/network-client/__tests__/networkClient.test.ts new file mode 100644 index 00000000..45dfaaf7 --- /dev/null +++ b/packages/network-client/__tests__/networkClient.test.ts @@ -0,0 +1,5 @@ +import testDataSets from './__dataSets__'; +import { VonageTest } from '../../../testHelpers'; + +VonageTest(testDataSets); + diff --git a/packages/network-client/lib/enums/index.ts b/packages/network-client/lib/enums/index.ts new file mode 100644 index 00000000..357f60fa --- /dev/null +++ b/packages/network-client/lib/enums/index.ts @@ -0,0 +1,2 @@ +export * from './purpose'; +export * from './scope'; diff --git a/packages/network-client/lib/enums/purpose.ts b/packages/network-client/lib/enums/purpose.ts new file mode 100644 index 00000000..868b37b2 --- /dev/null +++ b/packages/network-client/lib/enums/purpose.ts @@ -0,0 +1,9 @@ +/** + * Netowrk API purposes for generating the scope + */ +export enum Purpose { + /** + * Purpose for Fraud Prevention and Detection + */ + FRAUD_PREVENTION_AND_DETECTION = 'FraudPreventionAndDetection', +} diff --git a/packages/network-client/lib/enums/scope.ts b/packages/network-client/lib/enums/scope.ts new file mode 100644 index 00000000..ae8e277c --- /dev/null +++ b/packages/network-client/lib/enums/scope.ts @@ -0,0 +1,14 @@ +/** + * Scopes for the API + */ +export enum Scope { + /** + * Check for SIM Swap + */ + CHECK_SIM_SWAP = 'check-sim-swap', + + /** + * Number Verification + */ + NUMBER_VERIFICATION_VERIFY_READ = 'number-verification-verify-read', +} diff --git a/packages/network-client/lib/errors/index.ts b/packages/network-client/lib/errors/index.ts new file mode 100644 index 00000000..800c4c28 --- /dev/null +++ b/packages/network-client/lib/errors/index.ts @@ -0,0 +1,4 @@ +export * from './invalidScopeError'; +export * from './missingScopeError'; +export * from './missingPurposeError'; +export * from './invalidPurposeError'; diff --git a/packages/network-client/lib/errors/invalidPurposeError.ts b/packages/network-client/lib/errors/invalidPurposeError.ts new file mode 100644 index 00000000..57cf2636 --- /dev/null +++ b/packages/network-client/lib/errors/invalidPurposeError.ts @@ -0,0 +1,8 @@ +/** + * Error thrown when purpose is invalid + */ +export class InvalidPurposeError extends Error { + constructor() { + super(`You did not set a purpose for this request. Please set a proper purpose for this request.`); + } +} diff --git a/packages/network-client/lib/errors/invalidScopeError.ts b/packages/network-client/lib/errors/invalidScopeError.ts new file mode 100644 index 00000000..3feef820 --- /dev/null +++ b/packages/network-client/lib/errors/invalidScopeError.ts @@ -0,0 +1,8 @@ +/** + * Error thrown when scope is invalid + */ +export class InvalidScopeError extends Error { + constructor() { + super(`You did not set a scope for this request. Please set a proper scope for this request.`); + } +} diff --git a/packages/network-client/lib/errors/missingPurposeError.ts b/packages/network-client/lib/errors/missingPurposeError.ts new file mode 100644 index 00000000..cc0466de --- /dev/null +++ b/packages/network-client/lib/errors/missingPurposeError.ts @@ -0,0 +1,8 @@ +/** + * Error thrown when purpose is invalid + */ +export class MissingPurposeError extends Error { + constructor() { + super(`You did not set a purpose for this request.`); + } +} diff --git a/packages/network-client/lib/errors/missingScopeError.ts b/packages/network-client/lib/errors/missingScopeError.ts new file mode 100644 index 00000000..7c6dbbf7 --- /dev/null +++ b/packages/network-client/lib/errors/missingScopeError.ts @@ -0,0 +1,8 @@ +/** + * Error thrown when no scope is set for a request. + */ +export class MissingScopeError extends Error { + constructor() { + super(`You did not set a scope for this request.`); + } +} diff --git a/packages/network-client/lib/index.ts b/packages/network-client/lib/index.ts new file mode 100644 index 00000000..32c224dd --- /dev/null +++ b/packages/network-client/lib/index.ts @@ -0,0 +1,4 @@ +export * from './errors'; +export * from './networkClient'; +export * from './types'; +export * from './enums'; diff --git a/packages/network-client/lib/networkClient.ts b/packages/network-client/lib/networkClient.ts new file mode 100644 index 00000000..a42e4e83 --- /dev/null +++ b/packages/network-client/lib/networkClient.ts @@ -0,0 +1,421 @@ +import { AuthenticationType, Client } from '@vonage/server-client'; +import { MissingApplicationIdError } from '@vonage/jwt'; +import { Purpose, Scope } from './enums'; +import { + NetworkAuthParameters, + NetworkConfigParameters, + CIBAResponse, + NetworkTokenResponse, +} from './types'; +import { + VetchOptions, + VetchError, + HTTPMethods, + ContentType, +} from '@vonage/vetch'; +import { + InvalidPurposeError, + InvalidScopeError, + MissingPurposeError, + MissingScopeError, +} from './errors'; + +import debug from 'debug'; +const log = debug('vonage:network-client'); + +export class NetworkClient extends Client { + /** + * Flags if we are currently getting a token + */ + protected gettingToken: boolean = false; + + /** + * The current token + */ + protected accessToken?: string; + + /** + * Timestamp until the token expires + */ + protected expires: number = 0; + + /** + * The msisdn that will be used for API calls + */ + protected _msisdn: string; + + /** + * The purpose for the scope + */ + protected _purpose?: Purpose; + + /** + * The scope for the token + */ + protected _scope?: Scope; + + /** + * Configuration settings for the client, including default hosts for various services and other request settings. + */ + protected config: NetworkConfigParameters; + + constructor(auth: NetworkAuthParameters, config?: NetworkConfigParameters) { + super(auth, config); + log('Creating network client', config); + + this._msisdn = auth.msisdn; + this.config = { + ...super.getConfig() || {}, + networkApiHost: config?.networkApiHost || 'https://api-eu.vonage.com', + odicHost: config?.odicHost || 'https://oidc.idp.vonage.com', + redirectUri: config?.redirectUri || '', + autoRefreshToken: config?.autoRefreshToken || true, + } as NetworkConfigParameters; + + log('Network client config', this.config); + + this.accessToken = auth.accessToken; + this.expires = parseInt(`${auth.expiresIn}`) || 0; + } + + getConfig() { + return this.config; + } + + /** + * Get the msisdn + * @return {string} The msisdn + * @throws {Error} If the msisdn is not set + */ + get msisdn(): string { + if (!this._msisdn) { + throw new Error('You must set the msisdn'); + } + + return this._msisdn; + } + + /** + * Check if the token is is expired + */ + get isExpired(): boolean { + const now = new Date().getTime() * 1000; + return now < this.expires; + } + + /** + * Get the purpose + * + * @return {Purpose} The purpose + * @throws {MissingPurposeError} If the purpose is not set + * @throws {InvalidPurposeError} If the purpose is not valid + */ + get purpose(): Purpose { + if (!this._purpose) { + throw new MissingPurposeError(); + } + + if (!Object.values(Purpose).includes(this._purpose)) { + throw new InvalidPurposeError(); + } + + return this._purpose; + } + + /** + * Set the purpose + * @param {Purpose} value The purpose + */ + set purpose(value: Purpose) { + this._purpose = value; + } + + /** + * Get the scope + * + * @return {Scope} The scope + * @throws {MissingScopeError} If the scope is not set + * @throws {InvalidScopeError} If the scope is not valid + */ + get scope(): Scope { + if (!this._scope) { + throw new MissingScopeError(); + } + + if (!Object.values(Scope).includes(this._scope)) { + throw new InvalidScopeError(); + } + + return this._scope; + } + + /** + * Set the scope + * + * @param {Scope} value The scope + * @throws {InvalidScopeError} If the scope is not valid + * @throws {MissingScopeError} If the scope is not set + */ + set scope(value: Scope) { + this._scope = value; + } + + /** + * Add authentication to the auth + * + * This will make the calls to get a network token if required. + * + * @param {VetchOptions} request The request to add authentication to + * @return {Promise} The request with authentication added + */ + async addAuthenticationToRequest( + request: VetchOptions, + ): Promise { + if (this.gettingToken) { + log('Not adding any auth headers as we are getting a token'); + return request; + } + + // Auto refresh the token if it is expired for the CIBA flow + if ( + this.config.autoRefreshToken + && this.authType === AuthenticationType.CIBA + && (!this.accessToken || !this.isExpired) + ) { + log('Getting access token following CIBA flow'); + await this.getCIBAToken(); + } + + if (!this.accessToken) { + throw new Error('No access token is available'); + } + + // No need to log the token, they are very short lived + log('Adding access Token to request'); + request.headers = { + ...request.headers, + Authorization: `Bearer ${this.accessToken}`, + }; + + return request; + } + + /** + * Build the URL for the OIDC flow + * + * @param {string} state The state to use + * @return {string} The URL for the OIDC flow + */ + buildOIDCURL(state: string = ''): string { + if (!this.auth?.applicationId) { + throw new MissingApplicationIdError(); + } + + if (!this.config.redirectUri) { + throw new Error('You must set the redirectUri in the config'); + } + + const odicURL = new URL(`${this.config.odicHost}/oauth2/auth`); + + odicURL.searchParams.append('client_id', this.auth?.applicationId || ''); + odicURL.searchParams.append('response_type', 'code'); + odicURL.searchParams.append( + 'scope', + `openid dpv:${this.purpose}#${this.scope}`, + ); + odicURL.searchParams.append('redirect_uri', this.config.redirectUri || ''); + odicURL.searchParams.append('login_hint', `tel:+${this.msisdn}`); + + if (state) { + odicURL.searchParams.append('state', state); + } + + return odicURL.toString(); + } + + /** + * Exchange the code for a network token + * + * @param {string} code The code to exchange + * @return {Promise} The network token token + */ + async exchangeCodeForToken(code: string): Promise { + try { + log(`Exchange code for access token using ${code}`); + this.gettingToken = true; + const response = await super.sendRequest( + await super.addJWTToRequest({ + url: `${this.config.networkApiHost}/oauth2/token`, + method: HTTPMethods.POST, + type: ContentType.FORM_URLENCODED, + data: { + code: code, + redirect_uri: this.config.redirectUri || '', + grant_type: 'authorization_code', + }, + }), + ); + + log('Got access token', response.data); + return Client.transformers.snakeCaseObjectKeys( + response.data, + true, + ) as NetworkTokenResponse; + } catch (error) { + if (error instanceof VetchError) { + const { status } = error.response || {}; + + // Provide some help + switch (status) { + case 401: + error.message + = `Invalid credentials. Please check that the application id and private key are correct. ` + + `This could also mean that you have not setup the Network API correctly for your application .`; + break; + + case 400: + error.message + = `It appears you have not enabled the Network API for your account. ` + + 'Please contact your account manager to enable this feature. '; + break; + } + } + + log('Error exchanging code for token', error); + throw error; + } finally { + this.gettingToken = false; + } + } + + /** + * Get the access token for the CIBA flow + * + * This is a two step process. First we need to get an CIBA request id, + * then we can use that to get a network token. + */ + async getCIBAToken(): Promise { + log('Getting token'); + // Turn off adding auth headers to the request while we get the token + this.gettingToken = true; + + const cibaRequestId = await this.getCIBARequest(); + log(`CIBA request id: ${cibaRequestId}`); + + const tokenData = await this.getCIBAAccessToken(cibaRequestId); + + this.gettingToken = false; + log('Got token', tokenData); + this.accessToken = tokenData.access_token; + this.expires = new Date().getTime() * 1000 + tokenData.expires_in; + return; + } + + /** + * Get a network token + * + * @param {string} cibaRequestId The CIBA request id + * @return {Promise} The network token data + */ + async getCIBAAccessToken( + cibaRequestId: string, + ): Promise { + try { + log(`Getting Network token for ${cibaRequestId}`); + const response = await super.sendRequest( + await super.addJWTToRequest({ + url: `${this.config.networkApiHost}/oauth2/token`, + method: HTTPMethods.POST, + type: ContentType.FORM_URLENCODED, + data: { + auth_req_id: cibaRequestId, + grant_type: 'urn:openid:params:grant-type:ciba', + }, + }), + ); + + log('Got Network token', response.data); + return response.data; + } catch (error) { + if (error instanceof VetchError) { + const { status } = error.response || {}; + + // Provide some help + switch (status) { + case 401: + error.message + = `Invalid credentials. Please check that the application id and private key are correct. ` + + `This could also mean that you have not setup the Network API correctly for your application .`; + break; + + case 400: + error.message + = `It appears you have not enabled the Network API for your account. ` + + 'Please contact your account manager to enable this feature. '; + break; + } + } + + log('Error getting CIBA token', error); + throw error; + } + } + + /** + * Get an CIBA request id + * + * @return {Promise} The CIBA request id + */ + async getCIBARequest(): Promise { + try { + log(`Making CIBA request for ${this.msisdn}`); + console.log(`${this.config.networkApiHost}/oauth2/bc-authorize`,) + const response = await super.sendRequest( + await super.addJWTToRequest({ + url: `${this.config.networkApiHost}/oauth2/bc-authorize`, + method: HTTPMethods.POST, + type: ContentType.FORM_URLENCODED, + data: { + login_hint: `tel:+${this.msisdn}`, + scope: `openid dpv:${this.purpose}#${this.scope}`, + }, + }), + ); + + log('Got CIBA request', response.data); + return response.data.auth_req_id; + } catch (error) { + if (error instanceof VetchError) { + const { status } = error.response || {}; + + // Provide some help + switch (status) { + case 401: + error.message + = `A 401 was returned when trying to get authorization. ` + + `Some possible reasons for this:` + + `- The application id or private key are invalid. ` + + `- You have not setup the Network API correctly for your application .`; + break; + + case 400: + error.message + = `It appears you have not enabled the Network API for your account. ` + + 'Please contact your account manager to enable this feature. '; + break; + + case 404: + error.message + = `A 404 was returned when trying to get authorization. ` + + `Some possible reasons for this:` + + `- The network application is not setup correctly. ` + + `- The phone number is not associated with this network. `; + break; + } + } + + log('Error getting CIBA token', error); + throw error; + } + } +} diff --git a/packages/network-client/lib/types/index.ts b/packages/network-client/lib/types/index.ts new file mode 100644 index 00000000..94284031 --- /dev/null +++ b/packages/network-client/lib/types/index.ts @@ -0,0 +1,2 @@ +export * from './parameters'; +export * from './responses'; diff --git a/packages/network-client/lib/types/parameters/index.ts b/packages/network-client/lib/types/parameters/index.ts new file mode 100644 index 00000000..14d39d1e --- /dev/null +++ b/packages/network-client/lib/types/parameters/index.ts @@ -0,0 +1,2 @@ +export * from './networkAuthParameters'; +export * from './networkConfigParameters'; diff --git a/packages/network-client/lib/types/parameters/networkAuthParameters.ts b/packages/network-client/lib/types/parameters/networkAuthParameters.ts new file mode 100644 index 00000000..b104af44 --- /dev/null +++ b/packages/network-client/lib/types/parameters/networkAuthParameters.ts @@ -0,0 +1,18 @@ +import { AuthParams } from '@vonage/auth'; + +export type NetworkAuthParameters = AuthParams & { + /** + * The msisdn that will be used for API calls + */ + msisdn: string; + + /** + * The access token to use for API calls + */ + accessToken?: string; + + /** + * The expiration time of the access token (in seconds) + */ + expiresIn?: number; +}; diff --git a/packages/network-client/lib/types/parameters/networkConfigParameters.ts b/packages/network-client/lib/types/parameters/networkConfigParameters.ts new file mode 100644 index 00000000..07a037e9 --- /dev/null +++ b/packages/network-client/lib/types/parameters/networkConfigParameters.ts @@ -0,0 +1,30 @@ +import { ConfigParams } from '@vonage/server-client'; + +export type NetworkConfigParameters = ConfigParams & { + /** + * URL for making calls to get a network token + */ + networkApiHost?: string; + + /** + * URL for making calls to get a network token + */ + odicHost?: string; + + /** + * The URL to redirect to for the OAuth flow + */ + redirectUri?: string; + + /** + * Automatically refresh the token when it expires + * @default true + */ + autoRefreshToken?: boolean; + + /** + * Weather or not to store the token for future requests + * @default true + */ + storeToken?: boolean; +}; diff --git a/packages/network-client/lib/types/responses/index.ts b/packages/network-client/lib/types/responses/index.ts new file mode 100644 index 00000000..886da749 --- /dev/null +++ b/packages/network-client/lib/types/responses/index.ts @@ -0,0 +1,2 @@ +export * from './odicResponse'; +export * from './networkTokenResponse'; diff --git a/packages/network-client/lib/types/responses/networkTokenResponse.ts b/packages/network-client/lib/types/responses/networkTokenResponse.ts new file mode 100644 index 00000000..cda3b610 --- /dev/null +++ b/packages/network-client/lib/types/responses/networkTokenResponse.ts @@ -0,0 +1,16 @@ +export type NetworkTokenResponse = { + /** + * The token + */ + access_token: string; + + /** + * The type of token + */ + token_type: 'Bearer'; + + /** + * The time in seconds from now that the token expires + */ + expires_in: number; +}; diff --git a/packages/network-client/lib/types/responses/odicResponse.ts b/packages/network-client/lib/types/responses/odicResponse.ts new file mode 100644 index 00000000..a231d776 --- /dev/null +++ b/packages/network-client/lib/types/responses/odicResponse.ts @@ -0,0 +1,16 @@ +export type CIBAResponse = { + /** + * CIBA ODIC token + */ + auth_req_id: string; + + /** + * Seconds until the token expires + */ + expires_in: number; + + /** + * This is the minimum polling interval in seconds + */ + interval?: string; +}; diff --git a/packages/network-client/package.json b/packages/network-client/package.json new file mode 100644 index 00000000..2be9fdd5 --- /dev/null +++ b/packages/network-client/package.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "name": "@vonage/network-client", + "version": "1.0.1-6", + "description": "Network API Client package", + "homepage": "https://github.com/vonage/vonage-node-sdk/tree/main/packages/network-client#readme", + "bugs": { + "url": "https://github.com/Vonage/vonage-node-sdk/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Vonage/vonage-node-sdk.git" + }, + "license": "Apache-2.0", + "contributors": [ + { + "name": "Chuck MANCHUCK Reeves", + "url": "https://github.com/manchuck" + } + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "directories": { + "lib": "dist", + "test": "__tests__" + }, + "files": [ + "/dist" + ], + "scripts": { + "build": "npm run clean && npm run compile", + "clean": "npx shx rm -rf dist tsconfig.tsbuildinfo", + "compile": "npx tsc --build --verbose", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@vonage/auth": "1.8.0", + "@vonage/jwt": "1.8.0", + "@vonage/server-client": "1.12.0-1", + "@vonage/vetch": "1.7.1", + "debug": "4.3.4" + }, + "devDependencies": { + "@types/jest": "29.5.12" + } +} diff --git a/packages/network-client/tsconfig.json b/packages/network-client/tsconfig.json new file mode 100644 index 00000000..f16a00ed --- /dev/null +++ b/packages/network-client/tsconfig.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.json", + + "compilerOptions": { + "rootDir": "lib", + "outDir": "dist" + }, + + "exclude": [ + "__tests__", + "jest.config.js", + "dist" + ], + + "references": [ + { "path": "../auth" }, + { "path": "../server-client" }, + { "path": "../jwt" } + ], + + "ts-node": { + "esm": true + } +} diff --git a/packages/network-client/typedoc.json b/packages/network-client/typedoc.json new file mode 100644 index 00000000..bd32751c --- /dev/null +++ b/packages/network-client/typedoc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.base.json"], + "entryPoints": ["lib/index.ts"], + "name": "Vonage Network Auth" +} diff --git a/packages/network-number-verification/README.md b/packages/network-number-verification/README.md new file mode 100644 index 00000000..01e5460e --- /dev/null +++ b/packages/network-number-verification/README.md @@ -0,0 +1,11 @@ +# `network-number-verification` + +> TODO: description + +## Usage + +``` +const networkNumberVerification = require('network-number-verification'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/network-number-verification/lib/index.ts b/packages/network-number-verification/lib/index.ts new file mode 100644 index 00000000..932ba619 --- /dev/null +++ b/packages/network-number-verification/lib/index.ts @@ -0,0 +1,2 @@ +export * from './types'; +export * from './numberVerification'; diff --git a/packages/network-number-verification/lib/numberVerification.ts b/packages/network-number-verification/lib/numberVerification.ts new file mode 100644 index 00000000..375f41bb --- /dev/null +++ b/packages/network-number-verification/lib/numberVerification.ts @@ -0,0 +1,39 @@ +import { + NetworkAuthParameters, + NetworkConfigParameters, + NetworkClient, + Purpose, + Scope, +} from '@vonage/network-client'; +import { NumberVerificationResponse, NumberVerificationRequest } from './types'; + +export class NumberVerificationClient extends NetworkClient { + constructor(auth: NetworkAuthParameters, config?: NetworkConfigParameters) { + super(auth, config); + + this.purpose = Purpose.FRAUD_PREVENTION_AND_DETECTION; + this.scope = Scope.NUMBER_VERIFICATION_VERIFY_READ; + } + + async verifyPhoneNumber( + phoneNumber: string, + accessToken?: string, + ): Promise { + try { + // Allow acces token to be passed in. This is useful when the client is + // being used in a multi-tenant environment + this.accessToken = accessToken || this.accessToken; + const resp = await this.sendPostRequest( + `${this.config.networkApiHost}/camara/number-verification/v031/verify`, + { + phoneNumber: phoneNumber, + } as NumberVerificationRequest, + ); + + return resp.data.devicePhoneNumberVerified; + } finally { + this.accessToken = accessToken || this.accessToken; + } + + } +} diff --git a/packages/network-number-verification/lib/types/index.ts b/packages/network-number-verification/lib/types/index.ts new file mode 100644 index 00000000..0b4b018e --- /dev/null +++ b/packages/network-number-verification/lib/types/index.ts @@ -0,0 +1,2 @@ +export * from './requests'; +export * from './responses'; diff --git a/packages/network-number-verification/lib/types/requests/index.ts b/packages/network-number-verification/lib/types/requests/index.ts new file mode 100644 index 00000000..a0a32905 --- /dev/null +++ b/packages/network-number-verification/lib/types/requests/index.ts @@ -0,0 +1 @@ +export * from './numberVerificationRequest'; diff --git a/packages/network-number-verification/lib/types/requests/numberVerificationRequest.ts b/packages/network-number-verification/lib/types/requests/numberVerificationRequest.ts new file mode 100644 index 00000000..168fd44c --- /dev/null +++ b/packages/network-number-verification/lib/types/requests/numberVerificationRequest.ts @@ -0,0 +1,6 @@ +export type NumberVerificationRequest = { + /** + * The phone number to verify. + */ + phoneNumber: string; +}; diff --git a/packages/network-number-verification/lib/types/responses/index.ts b/packages/network-number-verification/lib/types/responses/index.ts new file mode 100644 index 00000000..b5d6f986 --- /dev/null +++ b/packages/network-number-verification/lib/types/responses/index.ts @@ -0,0 +1 @@ +export * from './numberVerificationResponse'; diff --git a/packages/network-number-verification/lib/types/responses/numberVerificationResponse.ts b/packages/network-number-verification/lib/types/responses/numberVerificationResponse.ts new file mode 100644 index 00000000..d8858686 --- /dev/null +++ b/packages/network-number-verification/lib/types/responses/numberVerificationResponse.ts @@ -0,0 +1,6 @@ +export type NumberVerificationResponse = { + /** + * The phone number that was verified. + */ + devicePhoneNumberVerified: boolean; +}; diff --git a/packages/network-number-verification/package.json b/packages/network-number-verification/package.json new file mode 100644 index 00000000..39ba340f --- /dev/null +++ b/packages/network-number-verification/package.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "name": "@vonage/network-number-verification", + "version": "1.0.1-11", + "description": "Number Verfication for GNP", + "homepage": "https://github.com/vonage/vonage-node-sdk/tree/main/packages/network-number-verification#readme", + "bugs": { + "url": "https://github.com/Vonage/vonage-node-sdk/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Vonage/vonage-node-sdk.git" + }, + "license": "Apache-2.0", + "contributors": [ + { + "name": "Chuck MANCHUCK Reeves", + "url": "https://github.com/manchuck" + } + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "directories": { + "lib": "dist", + "test": "__tests__" + }, + "files": [ + "/dist" + ], + "scripts": { + "build": "npm run clean && npm run compile", + "clean": "npx shx rm -rf dist tsconfig.tsbuildinfo", + "compile": "npx tsc --build --verbose", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@vonage/network-client": "1.0.1-6" + } +} diff --git a/packages/network-number-verification/tsconfig.json b/packages/network-number-verification/tsconfig.json new file mode 100644 index 00000000..826adf5c --- /dev/null +++ b/packages/network-number-verification/tsconfig.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.json", + + "compilerOptions": { + "rootDir": "lib", + "outDir": "dist" + }, + + "exclude": [ + "__tests__", + "jest.config.js", + "dist" + ], + + "references": [ + { "path": "../network-client" } + ], + + "ts-node": { + "esm": true + } +} diff --git a/packages/network-number-verification/typedoc.json b/packages/network-number-verification/typedoc.json new file mode 100644 index 00000000..54cd599d --- /dev/null +++ b/packages/network-number-verification/typedoc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.base.json"], + "entryPoints": ["lib/index.ts"], + "name": "Vonage Network Number Verification", +} diff --git a/packages/network-sim-swap/README.md b/packages/network-sim-swap/README.md new file mode 100644 index 00000000..7de05c52 --- /dev/null +++ b/packages/network-sim-swap/README.md @@ -0,0 +1,11 @@ +# `network-sim-swap` + +> TODO: description + +## Usage + +``` +const networkSimSwap = require('network-sim-swap'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/network-sim-swap/lib/index.ts b/packages/network-sim-swap/lib/index.ts new file mode 100644 index 00000000..cd06e201 --- /dev/null +++ b/packages/network-sim-swap/lib/index.ts @@ -0,0 +1,2 @@ +export * from './simSwap'; +export * from './types'; diff --git a/packages/network-sim-swap/lib/simSwap.ts b/packages/network-sim-swap/lib/simSwap.ts new file mode 100644 index 00000000..17c07866 --- /dev/null +++ b/packages/network-sim-swap/lib/simSwap.ts @@ -0,0 +1,17 @@ +import { Client } from '@vonage/server-client'; +import { NetworkClient, Purpose, Scope } from '@vonage/network-client'; +import { SIMSwapResponse, SIMSwapParameters } from './types'; + +export class SIMSwap extends NetworkClient { + async checkSwapSim(params: SIMSwapParameters): Promise { + this._msisdn = params.phoneNumber; + this.purpose = Purpose.FRAUD_PREVENTION_AND_DETECTION; + this.scope = Scope.CHECK_SIM_SWAP; + const resp = await this.sendPostRequest( + `${this.config.networkApiHost}/camara/sim-swap/v040/check`, + Client.transformers.camelCaseObjectKeys(params, true), + ); + + return resp.data.swapped; + } +} diff --git a/packages/network-sim-swap/lib/types/index.ts b/packages/network-sim-swap/lib/types/index.ts new file mode 100644 index 00000000..b9a52f18 --- /dev/null +++ b/packages/network-sim-swap/lib/types/index.ts @@ -0,0 +1,2 @@ +export * from './responses'; +export * from './parameters'; diff --git a/packages/network-sim-swap/lib/types/parameters/index.ts b/packages/network-sim-swap/lib/types/parameters/index.ts new file mode 100644 index 00000000..c37968db --- /dev/null +++ b/packages/network-sim-swap/lib/types/parameters/index.ts @@ -0,0 +1 @@ +export * from './simSwapParameters'; diff --git a/packages/network-sim-swap/lib/types/parameters/simSwapParameters.ts b/packages/network-sim-swap/lib/types/parameters/simSwapParameters.ts new file mode 100644 index 00000000..07857e68 --- /dev/null +++ b/packages/network-sim-swap/lib/types/parameters/simSwapParameters.ts @@ -0,0 +1,13 @@ +export type SIMSwapParameters = { + /** + * The Phone number to check if the SIM was swapped + */ + phoneNumber: string; + + /** + * The number of days to check if the SIM was swapped + * + * From 1 to 2400 + */ + maxAge?: number; +}; diff --git a/packages/network-sim-swap/lib/types/responses/index.ts b/packages/network-sim-swap/lib/types/responses/index.ts new file mode 100644 index 00000000..f0ba9bcd --- /dev/null +++ b/packages/network-sim-swap/lib/types/responses/index.ts @@ -0,0 +1 @@ +export * from './simSwapResponse'; diff --git a/packages/network-sim-swap/lib/types/responses/simSwapResponse.ts b/packages/network-sim-swap/lib/types/responses/simSwapResponse.ts new file mode 100644 index 00000000..08815222 --- /dev/null +++ b/packages/network-sim-swap/lib/types/responses/simSwapResponse.ts @@ -0,0 +1,6 @@ +export type SIMSwapResponse = { + /** + * Weahter the SIM was swapped or not + */ + swapped: boolean; +}; diff --git a/packages/network-sim-swap/package.json b/packages/network-sim-swap/package.json new file mode 100644 index 00000000..a9fc7b12 --- /dev/null +++ b/packages/network-sim-swap/package.json @@ -0,0 +1,39 @@ +{ + "name": "@vonage/network-sim-swap", + "version": "1.0.1-4", + "description": "A Network API client for checking if a SIM card was swapped recently for a phone number", + "homepage": "https://github.com/vonage/vonage-node-sdk/tree/main/packages/network-sim-swap#readme", + "bugs": { + "url": "https://github.com/Vonage/vonage-node-sdk/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Vonage/vonage-node-sdk.git" + }, + "license": "Apache-2.0", + "contributors": [ + { + "name": "Chuck MANCHUCK Reeves", + "url": "https://github.com/manchuck" + } + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "directories": { + "lib": "dist", + "test": "__tests__" + }, + "files": [ + "/dist" + ], + "scripts": { + "build": "npm run clean && npm run compile", + "clean": "npx shx rm -rf dist tsconfig.tsbuildinfo", + "compile": "npx tsc --build --verbose", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@vonage/network-client": "1.0.1-2", + "@vonage/server-client": "1.10.2" + } +} diff --git a/packages/network-sim-swap/tsconfig.json b/packages/network-sim-swap/tsconfig.json new file mode 100644 index 00000000..fa910a46 --- /dev/null +++ b/packages/network-sim-swap/tsconfig.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.json", + + "compilerOptions": { + "rootDir": "lib", + "outDir": "dist" + }, + + "exclude": [ + "__tests__", + "jest.config.js", + "dist" + ], + + "references": [ + { "path": "../server-client" }, + { "path": "../vetch" }, + { "path": "../network-client" } + ], + + "ts-node": { + "esm": true + } +} diff --git a/packages/network-sim-swap/typedoc.json b/packages/network-sim-swap/typedoc.json new file mode 100644 index 00000000..7caa9b8a --- /dev/null +++ b/packages/network-sim-swap/typedoc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.base.json"], + "entryPoints": ["lib/index.ts"], + "name": "Vonage Network SIM Swap" +} diff --git a/packages/server-client/lib/client.ts b/packages/server-client/lib/client.ts index 755c178a..c89c23ad 100644 --- a/packages/server-client/lib/client.ts +++ b/packages/server-client/lib/client.ts @@ -73,6 +73,10 @@ export class Client { } as ConfigParams; } + getConfig(): ConfigParams { + return this.config; + } + /** * Adds the appropriate authentication headers or parameters to the request based on the authentication type. * @@ -91,29 +95,40 @@ export class Client { throw new Error('No authentication type set'); } + if ( + [AuthenticationType.OAUTH2, AuthenticationType.CIBA].includes( + this.authType as AuthenticationType, + ) + ) { + throw new Error( + 'OAuth2 and CIBA authentication is not supported for this Client', + ); + } + switch (this.authType) { case AuthenticationType.BASIC: - request.headers = Object.assign({}, request.headers, { - Authorization: await this.auth.createBasicHeader(), - }); - return request; + return this.addBasicAuthToRequest(request); case AuthenticationType.JWT: - request.headers = Object.assign({}, request.headers, { - Authorization: await this.auth.createBearerHeader(), - }); - return request; - } + return this.addJWTToRequest(request); - if (this.authType === AuthenticationType.QUERY_KEY_SECRET) { - log('adding parameters to query string'); - request.params = { - ...(request.params ? request.params : {}), - ...(await this.auth.getQueryParams({})), - }; - return request; + case AuthenticationType.QUERY_KEY_SECRET: + return this.addQueryKeySecretToRequest(request); + + default: + return this.addQueryKeySecretToRequestBody(request); } + } + /** + * Adds API key and secret to the request body. + * + * @param {VetchOptions} request - The request options to which authentication needs to be added. + * @return {VetchOptions} - The request options with the added authentication. + */ + protected async addQueryKeySecretToRequestBody( + request: VetchOptions, + ): Promise { if (typeof request.data === 'string') { throw new Error('Cannot append auth parameters to body'); } @@ -127,7 +142,53 @@ export class Client { ...request.data, ...authParams, }; + return request; + } + /** + * Adds API key and secret to the request. + * + * @param {VetchOptions} request - The request options to which authentication needs to be added. + * @return {VetchOptions} - The request options with the added authentication. + */ + protected async addQueryKeySecretToRequest( + request: VetchOptions, + ): Promise { + log(`adding parameters to query string`); + request.params = { + ...(request.params ? request.params : {}), + ...(await this.auth.getQueryParams({})), + }; + return request; + } + + /** + * Adds a JWT to the request. + * + * @param {VetchOptions} request - The request options to which authentication needs to be added. + * @return {VetchOptions} - The request options with the added authentication. + */ + protected async addJWTToRequest( + request: VetchOptions, + ): Promise { + request.headers = Object.assign({}, request.headers, { + Authorization: await this.auth.createBearerHeader(), + }); + return request; + } + + /** + * Adds basic authentication headers to the request. + * + * @param {VetchOptions} request - The request options to which authentication needs to be added. + * @return {VetchOptions} - The request options with the added authentication. + */ + protected async addBasicAuthToRequest( + request: VetchOptions, + ): Promise { + request.headers = Object.assign({}, request.headers, { + Authorization: await this.auth.createBasicHeader(), + }); return request; } @@ -319,6 +380,7 @@ export class Client { request = await this.addAuthenticationToRequest(request); + log('Request prepared', request); const url = new URL(request.url as string); const params = new URLSearchParams(request.params); diff --git a/packages/server-client/lib/enums/AuthenticationType.ts b/packages/server-client/lib/enums/AuthenticationType.ts index dd34104e..6352bdda 100644 --- a/packages/server-client/lib/enums/AuthenticationType.ts +++ b/packages/server-client/lib/enums/AuthenticationType.ts @@ -4,28 +4,38 @@ */ export enum AuthenticationType { /** - * @description Basic authentication using a base64-encoded string. + * Basic authentication using a base64-encoded string. */ BASIC = 'basic', /** - * @description JSON Web Token (JWT) authentication. + * JSON Web Token (JWT) authentication. */ JWT = 'jwt', /** - * @description Authentication using both API key and secret in the request body. + * Authentication using both API key and secret in the request body. * @deprecated This method is deprecated. */ KEY_SECRET = 'key_secret', /** - * @description Authentication using API key and secret in the query parameters. + * Authentication using API key and secret in the query parameters. */ QUERY_KEY_SECRET = 'query_key_secret', /** - * @description HMAC signature-based authentication. + * HMAC signature-based authentication. */ SIGNATURE = 'signature', + + /** + * CIBA authentication + */ + CIBA = 'ciba', + + /** + * OAuth2 authentication. + */ + OAUTH2 = 'oauth2', }