2023-05-17 07:40:53 -07:00
|
|
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2023-06-21 06:19:38 -07:00
|
|
|
import type { ClientOAuth2Options, ClientOAuth2RequestObject } from './ClientOAuth2';
|
2023-05-17 07:40:53 -07:00
|
|
|
import { ERROR_RESPONSES } from './constants';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if properties exist on an object and throw when they aren't.
|
|
|
|
*/
|
2023-06-21 06:19:38 -07:00
|
|
|
export function expects<Keys extends keyof ClientOAuth2Options>(
|
|
|
|
obj: ClientOAuth2Options,
|
|
|
|
...keys: Keys[]
|
|
|
|
): asserts obj is ClientOAuth2Options & {
|
|
|
|
[K in Keys]: NonNullable<ClientOAuth2Options[K]>;
|
|
|
|
} {
|
|
|
|
for (const key of keys) {
|
|
|
|
if (obj[key] === null || obj[key] === undefined) {
|
|
|
|
throw new TypeError('Expected "' + key + '" to exist');
|
2023-05-17 07:40:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class AuthError extends Error {
|
2023-07-28 04:55:16 -07:00
|
|
|
constructor(
|
|
|
|
message: string,
|
|
|
|
readonly body: any,
|
|
|
|
readonly code = 'EAUTH',
|
|
|
|
) {
|
2023-05-17 07:40:53 -07:00
|
|
|
super(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pull an authentication error from the response data.
|
|
|
|
*/
|
|
|
|
export function getAuthError(body: {
|
|
|
|
error: string;
|
|
|
|
error_description?: string;
|
|
|
|
}): Error | undefined {
|
|
|
|
const message: string | undefined =
|
|
|
|
ERROR_RESPONSES[body.error] ?? body.error_description ?? body.error;
|
|
|
|
|
|
|
|
if (message) {
|
|
|
|
return new AuthError(message, body);
|
|
|
|
}
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensure a value is a string.
|
|
|
|
*/
|
|
|
|
function toString(str: string | null | undefined) {
|
|
|
|
return str === null ? '' : String(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create basic auth header.
|
|
|
|
*/
|
|
|
|
export function auth(username: string, password: string): string {
|
|
|
|
return 'Basic ' + Buffer.from(toString(username) + ':' + toString(password)).toString('base64');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Merge request options from an options object.
|
|
|
|
*/
|
|
|
|
export function getRequestOptions(
|
|
|
|
{ url, method, body, query, headers }: ClientOAuth2RequestObject,
|
2023-07-26 08:56:59 -07:00
|
|
|
options: ClientOAuth2Options,
|
2023-05-17 07:40:53 -07:00
|
|
|
): ClientOAuth2RequestObject {
|
|
|
|
const rOptions = {
|
|
|
|
url,
|
|
|
|
method,
|
|
|
|
body: { ...body, ...options.body },
|
|
|
|
query: { ...query, ...options.query },
|
2023-07-26 08:56:59 -07:00
|
|
|
headers: headers ?? {},
|
|
|
|
ignoreSSLIssues: options.ignoreSSLIssues,
|
2023-05-17 07:40:53 -07:00
|
|
|
};
|
|
|
|
// if request authorization was overridden delete it from header
|
|
|
|
if (rOptions.headers.Authorization === '') {
|
|
|
|
delete rOptions.headers.Authorization;
|
|
|
|
}
|
|
|
|
return rOptions;
|
|
|
|
}
|