diff --git a/packages/auth/lib/auth.ts b/packages/auth/lib/auth.ts index 0d24a037..abec9c23 100644 --- a/packages/auth/lib/auth.ts +++ b/packages/auth/lib/auth.ts @@ -1,6 +1,6 @@ import { tokenGenerate, GeneratorOptions } from '@vonage/jwt'; import { createHash, createHmac } from 'crypto'; -import { existsSync, readFileSync } from 'fs'; +import { PathLike, existsSync, readFileSync } from 'fs'; import { AuthParams, AuthQueryParams, @@ -22,11 +22,37 @@ import debug from 'debug'; const log = debug('vonage:auth'); export class Auth implements AuthInterface { + /** + * @property {string} apiKey - The API key used for authentication. + */ apiKey: string; + + /** + * @property {string} apiSecret - The API secret used for authentication. + */ apiSecret: string; - privateKey?: string; - applicationId?: string; - signature: SignedHashParams; + + /** + * @property {string | null} [privateKey] - The private key used for JWT + * authentication, either as a string or read from a file. + */ + privateKey?: string | null; + + /** + * @property {string | null} [applicationId] - The application ID used for + * JWT authentication. + */ + applicationId?: string | null; + + /** + * @property {SignedHashParams | null} [signature] - The signature parameters + * used for signature authentication. + */ + signature?: SignedHashParams | null; + + /** + * @property {GeneratorOptions} jwtOptions - Options used for generating JWTs. + */ jwtOptions: GeneratorOptions; constructor(opts?: AuthParams) { @@ -36,17 +62,34 @@ export class Auth implements AuthInterface { this.applicationId = opts?.applicationId || null; this.jwtOptions = opts?.jwtOptions || {}; - if (existsSync(opts.privateKey)) { + let privateKey = opts?.privateKey; + if (existsSync(opts?.privateKey as PathLike)) { log('Reading private key file'); - opts.privateKey = readFileSync(opts.privateKey).toString(); + privateKey = readFileSync(opts?.privateKey as PathLike).toString(); } this.privateKey - = opts.privateKey instanceof Buffer - ? opts.privateKey.toString() - : opts.privateKey; + = privateKey instanceof Buffer + ? privateKey.toString() + : (opts?.privateKey as string); } + /** + * Generates query parameters for authentication, optionally merging with + * provided parameters. + * + * + * @param {T} [params] - Additional parameters to merge with the + * generated authentication query parameters. + * + * @return {Promise} - A promise that resolves + * with the merged authentication query parameters. + * + * @throws {MissingApiKeyError} - Thrown when the API key is missing. + * @throws {MissingApiSecretError} - Thrown when the API secret is missing. + * @throws {InvalidApiKeyError} - Thrown when the API key is not a valid string. + * @throws {InvalidApiSecretError} - Thrown when the API secret is not a valid string. + */ getQueryParams = async ( params?: AuthQueryParams & T, ): Promise => { @@ -70,9 +113,20 @@ export class Auth implements AuthInterface { ...params, api_key: this.apiKey, api_secret: this.apiSecret, - }; + } as AuthQueryParams & T; }; + /** + * Generates a basic authentication header. + * + * @return {Promise} - A promise that resolves with the + * generated basic authentication header. + * + * @throws {MissingApiKeyError} - Thrown when the API key is missing. + * @throws {MissingApiSecretError} - Thrown when the API secret is missing. + * @throws {InvalidApiKeyError} - Thrown when the API key is not a valid string. + * @throws {InvalidApiSecretError} - Thrown when the API secret is not a valid string. + */ createBasicHeader = async (): Promise => { log('Creating basic auth header'); if (!this.apiKey) { @@ -95,15 +149,38 @@ export class Auth implements AuthInterface { return `Basic ${buf.toString('base64')}`; }; + /** + * Generates a bearer authentication header. + * + * @return {Promise} - A promise that resolves with the + * generated bearer authentication header. + */ createBearerHeader = async (): Promise => { log('Creating bearer header'); return `Bearer ${tokenGenerate( - this.applicationId, - this.privateKey, + this.applicationId || '', + this.privateKey || '', this.jwtOptions, )}`; }; + /** + * Generates a signature hash for authentication, merging it with + * provided parameters. + * + * @template T - Type of the parameters to merge with. + * @param {T} params - Parameters to merge with the generated + * signature hash. + * @return {Promise} - A promise that resolves + * with the merged signature hash and parameters. + * + * @throws {MissingApiKeyError} - Thrown when the API key is missing. + * @throws {InvalidApiKeyError} - Thrown when the API key is not a valid string. + * @throws {MissingSignatureError} - Thrown when the signature algorithm is missing. + * @throws {MissingApiSecretError} - Thrown when the API secret is missing. + * @throws {InvalidApiSecretError} - Thrown when the API secret is not a valid string. + * @throws {InvalidSignatureAlgorithmError} - Thrown when an invalid signature algorithm is provided. + */ createSignatureHash = async ( params: AuthSignedParams & T, ): Promise => { @@ -116,15 +193,15 @@ export class Auth implements AuthInterface { throw new InvalidApiKeyError(); } - if (!this.signature.algorithm) { + if (!this.signature?.algorithm) { throw new MissingSignatureError(); } - if (!this.signature.secret) { + if (!this.signature?.secret) { throw new MissingApiSecretError(); } - if (typeof this.signature.secret !== 'string') { + if (typeof this.signature?.secret !== 'string') { throw new InvalidApiSecretError(); } @@ -150,9 +227,7 @@ export class Auth implements AuthInterface { switch (this.signature.algorithm) { case AlgorithmTypes.md5hash: returnParams.sig = createHash('md5') - .update( - `${stringifiedParamsforSigning}${this.signature.secret}`, - ) + .update(`${stringifiedParamsforSigning}${this.signature.secret}`) .digest('hex'); break; diff --git a/packages/auth/lib/enums/AlgroithmTypes.ts b/packages/auth/lib/enums/AlgroithmTypes.ts index fa0cda8f..1efedb3c 100644 --- a/packages/auth/lib/enums/AlgroithmTypes.ts +++ b/packages/auth/lib/enums/AlgroithmTypes.ts @@ -1,7 +1,29 @@ +/** + * Enumeration of supported algorithm types for HMAC hashing. + * + * @enum {string} + * + * @property {string} md5hash - Represents the MD5 hash algorithm. + * + * @property {string} md5hmac - Represents the HMAC-MD5 hash algorithm, + * which uses a secret key for hashing. + * + * @property {string} sha1hmac - Represents the HMAC-SHA1 hash algorithm, + * which uses a secret key for hashing. + * + * @property {string} sha256hmac - Represents the HMAC-SHA256 hash algorithm, + * which uses a secret key for hashing. + * + * @property {string} sha512hmac - Represents the HMAC-SHA512 hash algorithm, + * which uses a secret key for hashing. + * + * Note: Ensure to select an algorithm that adheres to your security + * requirements and is supported by the API endpoint you're interacting with. + */ export enum AlgorithmTypes { - md5hash = 'MD5HASH', - md5hmac = 'MD5HMAC', - sha1hmac = 'SHA1HMAC', - sha256hmac = 'SHA256HMAC', - sha512hmac = 'SHA512HMAC', + md5hash = 'MD5HASH', + md5hmac = 'MD5HMAC', + sha1hmac = 'SHA1HMAC', + sha256hmac = 'SHA256HMAC', + sha512hmac = 'SHA512HMAC', } diff --git a/packages/auth/lib/errors/InvalidApiKeyError.ts b/packages/auth/lib/errors/InvalidApiKeyError.ts index d5d128dd..bbcf6383 100644 --- a/packages/auth/lib/errors/InvalidApiKeyError.ts +++ b/packages/auth/lib/errors/InvalidApiKeyError.ts @@ -1,3 +1,17 @@ +/** + * Error class representing a specific error scenario where an API key is + * provided but is not a valid string. + * + * This error is thrown when an API request is made with an API key that + * does not meet the expected format or type (string). + * + * @extends {Error} + * + * @example + * if (typeof apiKey !== 'string') { + * throw new InvalidApiKeyError(); + * } + */ export class InvalidApiKeyError extends Error { constructor() { super('API Key must be a string'); diff --git a/packages/auth/lib/errors/InvalidApiSecretError.ts b/packages/auth/lib/errors/InvalidApiSecretError.ts index 5b4f44ea..3fdb8bf2 100644 --- a/packages/auth/lib/errors/InvalidApiSecretError.ts +++ b/packages/auth/lib/errors/InvalidApiSecretError.ts @@ -1,3 +1,17 @@ +/** + * Error class representing a specific error scenario where an API secret is + * provided but is not a valid string. + * + * This error is thrown when an API request is made with an API secret that + * does not meet the expected format or type (string). + * + * @extends {Error} + * + * @example + * if (typeof apiSecret !== 'string') { + * throw new InvalidApiSecretError(); + * } + */ export class InvalidApiSecretError extends Error { constructor() { super('API Secret must be a string'); diff --git a/packages/auth/lib/errors/InvalidSignatureAlgroithmError.ts b/packages/auth/lib/errors/InvalidSignatureAlgroithmError.ts index 8e5077a6..805ade6c 100644 --- a/packages/auth/lib/errors/InvalidSignatureAlgroithmError.ts +++ b/packages/auth/lib/errors/InvalidSignatureAlgroithmError.ts @@ -1,3 +1,17 @@ +/** + * Error class representing a specific error scenario where an invalid + * signature algorithm is provided. + * + * This error is thrown when an API request is made with a signature + * algorithm that is not supported or recognized. + * + * @extends {Error} + * + * @example + * if (!isAlgorithmSupported(algorithm)) { + * throw new InvalidSignatureAlgorithmError(); + * } + */ export class InvalidSignatureAlgorithmError extends Error { constructor() { super('Invalid Signature algorithm'); diff --git a/packages/auth/lib/errors/InvalidSignatureError.ts b/packages/auth/lib/errors/InvalidSignatureError.ts index 8e5077a6..7d99f2b3 100644 --- a/packages/auth/lib/errors/InvalidSignatureError.ts +++ b/packages/auth/lib/errors/InvalidSignatureError.ts @@ -1,4 +1,18 @@ -export class InvalidSignatureAlgorithmError extends Error { +/** + * Error class representing a specific error scenario where a user selects + * an invalid or unsupported signature algorithm. + * + * This error is thrown when an API request is made with a signature + * algorithm that is not present in the AlgorithmTypes enum. + * + * @extends {Error} + * + * @example + * if (!Object.values(AlgorithmTypes).includes(algorithm)) { + * throw new InvalidSignatureError(); + * } + */ +export class InvalidSignatureError extends Error { constructor() { super('Invalid Signature algorithm'); } diff --git a/packages/auth/lib/errors/MissingApiKeyError.ts b/packages/auth/lib/errors/MissingApiKeyError.ts index fbcc5059..0d530fed 100644 --- a/packages/auth/lib/errors/MissingApiKeyError.ts +++ b/packages/auth/lib/errors/MissingApiKeyError.ts @@ -1,3 +1,17 @@ +/** + * Error class representing a specific error scenario where an API key is + * missing in the request. + * + * This error is thrown when an API request is made without providing the + * necessary API key for authentication. + * + * @extends {Error} + * + * @example + * if (!apiKey) { + * throw new MissingApiKeyError(); + * } + */ export class MissingApiKeyError extends Error { constructor() { super('Missing API Key'); diff --git a/packages/auth/lib/errors/MissingApiSecretError.ts b/packages/auth/lib/errors/MissingApiSecretError.ts index 04c116bc..c468704c 100644 --- a/packages/auth/lib/errors/MissingApiSecretError.ts +++ b/packages/auth/lib/errors/MissingApiSecretError.ts @@ -1,3 +1,17 @@ +/** + * Error class representing a specific error scenario where an API secret is + * missing in the request. + * + * This error is thrown when an API request is made without providing the + * necessary API secret for authentication. + * + * @extends {Error} + * + * @example + * if (!apiSecret) { + * throw new MissingApiSecretError(); + * } + */ export class MissingApiSecretError extends Error { constructor() { super('Missing API Secret'); diff --git a/packages/auth/lib/errors/MissingSignatureError.ts b/packages/auth/lib/errors/MissingSignatureError.ts index 8a3eb024..663b82d1 100644 --- a/packages/auth/lib/errors/MissingSignatureError.ts +++ b/packages/auth/lib/errors/MissingSignatureError.ts @@ -1,3 +1,21 @@ +/** + * Error class representing a specific error scenario where a signature + * algorithm is expected but missing in the request. + * + * This error is thrown when an API request is made without providing the + * necessary signature algorithm for authentication. + * + * Users should select a value from the AlgorithmTypes enum. + * + * @extends {Error} + * + * @example + * if (!signatureAlgorithm) { + * throw new MissingSignatureError(); + * } + * + * @see {@link AlgorithmTypes} + */ export class MissingSignatureError extends Error { constructor() { super('Missing signature algorithm'); diff --git a/packages/auth/lib/interfaces/AuthConstructor.ts b/packages/auth/lib/interfaces/AuthConstructor.ts deleted file mode 100644 index f0194bd6..00000000 --- a/packages/auth/lib/interfaces/AuthConstructor.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { AuthParams } from '../types/index'; -import { AuthInterface } from './AuthInterface'; - -/** - * @deprecated - */ -export type AuthConstructor = new (opts?: AuthParams) => AuthInterface diff --git a/packages/auth/lib/interfaces/AuthInterface.ts b/packages/auth/lib/interfaces/AuthInterface.ts index 8ceb78cc..f433805c 100644 --- a/packages/auth/lib/interfaces/AuthInterface.ts +++ b/packages/auth/lib/interfaces/AuthInterface.ts @@ -1,8 +1,50 @@ import { AuthQueryParams, AuthSignedParams, AuthParams } from '../types/index'; +/** + * Interface defining the methods for handling various authentication + * mechanisms and parameter generation. + * + * @interface + * @extends {AuthParams} + */ export interface AuthInterface extends AuthParams { - getQueryParams(params?: T): Promise - createSignatureHash(params: T): Promise - createBasicHeader(): Promise - createBearerHeader(): Promise + /** + * Asynchronously generates query parameters for authentication, + * optionally merging with provided parameters. + * + * @template T - Type of the additional parameters to merge with. + * @param {T} [params] - Additional parameters to merge with the + * generated authentication query parameters. + * @returns {Promise} - A promise that resolves + * with the merged authentication query parameters. + */ + getQueryParams(params?: T): Promise; + + /** + * Asynchronously generates a signature hash for authentication, + * merging it with provided parameters. + * + * @template T - Type of the parameters to merge with. + * @param {T} params - Parameters to merge with the generated + * signature hash. + * @returns {Promise} - A promise that resolves + * with the merged signature hash and parameters. + */ + createSignatureHash(params: T): Promise; + + /** + * Asynchronously generates a basic authentication header. + * + * @returns {Promise} - A promise that resolves with the + * generated basic authentication header. + */ + createBasicHeader(): Promise; + + /** + * Asynchronously generates a bearer authentication header. + * + * @returns {Promise} - A promise that resolves with the + * generated bearer authentication header. + */ + createBearerHeader(): Promise; } diff --git a/packages/auth/lib/interfaces/index.ts b/packages/auth/lib/interfaces/index.ts index 9d66168d..1cfbd77c 100644 --- a/packages/auth/lib/interfaces/index.ts +++ b/packages/auth/lib/interfaces/index.ts @@ -1,2 +1 @@ -export * from './AuthConstructor'; export * from './AuthInterface'; diff --git a/packages/auth/lib/types.ts b/packages/auth/lib/types.ts deleted file mode 100644 index 36a5e2c3..00000000 --- a/packages/auth/lib/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './enums'; -export * from './interfaces'; -export * from './types'; diff --git a/packages/auth/lib/types/AuthParams.ts b/packages/auth/lib/types/AuthParams.ts index ee65290a..e4cf2ed9 100644 --- a/packages/auth/lib/types/AuthParams.ts +++ b/packages/auth/lib/types/AuthParams.ts @@ -1,16 +1,41 @@ import { GeneratorOptions } from '@vonage/jwt'; import { SignedHashParams } from './SignedHashParams'; -export type AuthParams = { - apiKey?: string - apiSecret?: string - privateKey?: string | Buffer - applicationId?: string - signature?: SignedHashParams - jwtOptions?: GeneratorOptions -} - /** - * @deprecated please use AuthParams + * Represents the authentication parameters required for API requests. + * + * @property {string | null} [apiKey] - The API key used to authenticate + * requests. It may be omitted if using JWT or signature authentication. + * This value can be found in your Vonage Developer Dashboard. + * + * @property {string | null} [apiSecret] - The API secret used to authenticate + * requests. It may be omitted if using JWT or signature authentication. + * This value can be found in your Vonage Developer Dashboard. + * + * @property {string | Buffer | null} [privateKey] - The private key used for + * JWT authentication. It can be provided as a string (read from a file) + * or as a Buffer (opened directly from a file). This key is downloaded + * when you create an application in your Vonage Developer Dashboard and + * may be omitted if using API key/secret or signature authentication. + * + * @property {string | null} [applicationId] - The application ID used in + * conjunction with the private key for JWT authentication. It may be + * omitted if using API key/secret or signature authentication. + * This value can be found in your Vonage Developer Dashboard. + * + * @property {SignedHashParams | null} [signature] - An object containing + * parameters for signature authentication, including the secret and + * algorithm. It may be omitted if using API key/secret or JWT + * authentication. + * + * @property {GeneratorOptions} jwtOptions - Options for generating JWTs, + * including the JWT issuer (application ID) and subject (user ID). */ -export type AuthOpts = AuthParams +export type AuthParams = { + apiKey?: string | null; + apiSecret?: string | null; + privateKey?: string | Buffer | null; + applicationId?: string | null; + signature?: SignedHashParams | null; + jwtOptions?: GeneratorOptions; +}; diff --git a/packages/auth/lib/types/AuthQueryParams.ts b/packages/auth/lib/types/AuthQueryParams.ts index ca7c5c47..8d41c408 100644 --- a/packages/auth/lib/types/AuthQueryParams.ts +++ b/packages/auth/lib/types/AuthQueryParams.ts @@ -1,4 +1,19 @@ +/** + * Represents the query parameters used for API request authentication. + * + * @deprecated This method of authentication, using API credentials in query + * parameters, is outdated and not recommended due to security concerns. + * Consider using more secure authentication methods, such as JWT or + * signature authentication. + * + * @property {string} api_key - The API key used to authenticate requests. + * This value can be found in your Vonage Developer Dashboard. + * + * @property {string} api_secret - The API secret used to authenticate + * requests. This value can also be found in your Vonage Developer + * Dashboard. + */ export type AuthQueryParams = { - api_key: string - api_secret: string -} + api_key: string; + api_secret: string; +}; diff --git a/packages/auth/lib/types/AuthSignedParams.ts b/packages/auth/lib/types/AuthSignedParams.ts index 4369ed6a..9c42e068 100644 --- a/packages/auth/lib/types/AuthSignedParams.ts +++ b/packages/auth/lib/types/AuthSignedParams.ts @@ -1,5 +1,27 @@ +/** + * Represents the parameters used for HMAC signature-based API request + * authentication. + * + * Note: Not all API endpoints support this method of authentication. + * Please refer to the specific API documentation to determine if HMAC + * signature authentication is supported. + * + * For more information on signing requests, visit: + * https://developer.vonage.com/en/getting-started/concepts/signing-messages + * + * @property {string} api_key - The API key used to authenticate requests. + * This value can be found in your Vonage Developer Dashboard. + * + * @property {string} [sig] - The generated signature, used to verify the + * authenticity of the request. It's typically generated by hashing + * the request parameters with a secret key. + * + * @property {string} [timestamp] - The UNIX timestamp when the request is + * made. Utilized to prevent replay attacks by making each signature + * unique to a specific time window. + */ export type AuthSignedParams = { - api_key: string - sig?: string - timestamp?: string -} + api_key: string; + sig?: string; + timestamp?: string; +}; diff --git a/packages/auth/lib/types/SignedHashParams.ts b/packages/auth/lib/types/SignedHashParams.ts index 068d170c..36029a80 100644 --- a/packages/auth/lib/types/SignedHashParams.ts +++ b/packages/auth/lib/types/SignedHashParams.ts @@ -1,6 +1,17 @@ import { AlgorithmTypes } from '../enums'; +/** + * Represents the parameters required for generating a signed hash. + * + * @property {string} secret - The secret key used to sign the hash, ensuring + * the integrity and authenticity of the message. It should be kept + * private and secure. + * + * @property {AlgorithmTypes} algorithm - Specifies the algorithm type used + * for signing the hash. Utilizes the algorithm types defined in the + * `AlgorithmTypes` enum. + */ export type SignedHashParams = { - secret: string - algorithm: AlgorithmTypes -} + secret: string; + algorithm: AlgorithmTypes; +}; diff --git a/packages/auth/package.json b/packages/auth/package.json index 242dac22..2dd5a5a5 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -27,10 +27,10 @@ "debug": "^4.3.4" }, "devDependencies": { + "@tsconfig/node16": "16.1.1", "@types/node": "^20.8.4" }, "publishConfig": { "directory": "dist" - }, - "gitHead": "328f18e5c8a458cb4d06d7955ec2399a6ce6f5d8" + } } diff --git a/packages/auth/typedoc.json b/packages/auth/typedoc.json new file mode 100644 index 00000000..9e428074 --- /dev/null +++ b/packages/auth/typedoc.json @@ -0,0 +1,5 @@ +{ + "extends": ["../../typedoc.base.json"], + "entryPoints": ["lib/index.ts"], + "name": "Vonage Auth" +}