accounts-frontend/packages/app/services/api/authentication.ts

181 lines
4.3 KiB
TypeScript
Raw Normal View History

import { UserResponse } from 'app/services/api/accounts';
import logger from 'app/services/logger';
import request, { InternalServerError } from 'app/services/request';
import { getInfo as getInfoEndpoint } from 'app/services/api/accounts';
export interface OAuthResponse {
2020-05-24 04:38:24 +05:30
access_token: string;
refresh_token?: string;
expires_in: number; // count seconds before expire
success: true;
}
export function login({
2020-05-24 04:38:24 +05:30
login,
password,
totp,
rememberMe = false,
}: {
2020-05-24 04:38:24 +05:30
login: string;
password?: string;
totp?: string;
rememberMe: boolean;
}): Promise<OAuthResponse> {
2020-05-24 04:38:24 +05:30
return request.post(
'/api/authentication/login',
{
login,
password,
totp,
rememberMe,
},
{ token: null },
);
}
/**
* @param {string} token - an optional token to overwrite headers
* in middleware and disable token auto-refresh
*
* @returns {Promise}
*/
export function logout(token?: string): Promise<{ success: boolean }> {
2020-05-24 04:38:24 +05:30
return request.post(
'/api/authentication/logout',
{},
{
token,
},
);
}
export function forgotPassword(
2020-05-24 04:38:24 +05:30
login: string,
captcha: string,
): Promise<{
2020-05-24 04:38:24 +05:30
success: boolean;
data: {
canRepeatIn: number;
emailMask: string | void;
repeatFrequency: number;
};
errors: {
[key: string]: string;
};
}> {
2020-05-24 04:38:24 +05:30
return request.post(
'/api/authentication/forgot-password',
{
login,
captcha,
},
{ token: null },
);
}
2016-07-28 10:33:30 +05:30
2020-05-24 04:38:24 +05:30
export function recoverPassword(key: string, newPassword: string, newRePassword: string): Promise<OAuthResponse> {
return request.post(
'/api/authentication/recover-password',
{
key,
newPassword,
newRePassword,
},
{ token: null },
);
}
/**
* Resolves if token is valid
*
* @param {number} id
* @param {string} token
* @param {string} refreshToken
*
* @returns {Promise} - resolves with options.token or with a new token
* if it was refreshed. As a side effect the response
* will have a `user` field with current user data
*
*/
export async function validateToken(
2020-05-24 04:38:24 +05:30
id: number,
token: string,
refreshToken: string | void | null,
): Promise<{
2020-05-24 04:38:24 +05:30
token: string;
refreshToken: string | null;
user: UserResponse;
}> {
2020-05-24 04:38:24 +05:30
if (typeof token !== 'string') {
throw new Error('token must be a string');
}
refreshToken = refreshToken || null;
let user: UserResponse;
try {
user = await getInfoEndpoint(id, token);
} catch (resp) {
token = await handleTokenError(resp, refreshToken);
user = await getInfoEndpoint(id, token); // TODO: replace with recursive call
}
return {
token,
refreshToken,
user,
};
}
2020-05-24 04:38:24 +05:30
const recoverableErrors = ['Token expired', 'Incorrect token', 'You are requesting with an invalid credential.'];
function handleTokenError(resp: Error | { message: string }, refreshToken: string | null): Promise<string> {
if (resp instanceof InternalServerError) {
// delegate error recovering to the bsod middleware
return new Promise(() => {});
}
2020-05-24 04:38:24 +05:30
if (refreshToken) {
if (recoverableErrors.includes(resp.message)) {
return requestToken(refreshToken);
}
logger.error('Unexpected error during token validation', { resp });
}
2016-11-05 15:41:41 +05:30
2020-05-24 04:38:24 +05:30
return Promise.reject(resp);
}
/**
* Request new access token using a refreshToken
*
* @param {string} refreshToken
*
* @returns {Promise} - resolves to token
*/
export async function requestToken(refreshToken: string): Promise<string> {
2020-05-24 04:38:24 +05:30
try {
const response: OAuthResponse = await request.post(
'/api/authentication/refresh-token',
{
refresh_token: refreshToken,
},
{
token: null,
},
);
return response.access_token;
} catch (resp) {
const errors = resp.errors || {};
if (errors.refresh_token !== 'error.refresh_token_not_exist') {
logger.error('Failed refreshing token: unknown error', {
resp,
});
}
throw resp;
}
}