Add support for different 2fa provider
parent
2751b9bc3b
commit
9f1b241fb5
|
@ -13,6 +13,10 @@ export const GENERIC = {
|
||||||
* Banner containing any error messages as text.
|
* Banner containing any error messages as text.
|
||||||
*/
|
*/
|
||||||
ERROR_BANNER: "div.p-body-pageContent > div.blockMessage",
|
ERROR_BANNER: "div.p-body-pageContent > div.blockMessage",
|
||||||
|
/**
|
||||||
|
* Provider that the platform expects to use to verify the code for two-factor authentication.
|
||||||
|
*/
|
||||||
|
EXPECTED_2FA_PROVIDER: 'input[name="provider"]',
|
||||||
/**
|
/**
|
||||||
* Locate the token used for the session.
|
* Locate the token used for the session.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -32,6 +32,8 @@ type LookupMapCodeT = {
|
||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ProviderT = "auto" | "totp" | "email";
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
const USER_AGENT =
|
const USER_AGENT =
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) " +
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) " +
|
||||||
|
@ -144,11 +146,13 @@ export async function authenticate(
|
||||||
* Send an OTP code if the login procedure requires it.
|
* Send an OTP code if the login procedure requires it.
|
||||||
* @param code OTP code.
|
* @param code OTP code.
|
||||||
* @param token Unique token for the session associated with the credentials in use.
|
* @param token Unique token for the session associated with the credentials in use.
|
||||||
|
* @param provider Provider used to generate the access code.
|
||||||
* @param trustedDevice If the device in use is trusted, 2FA authentication is not required for 30 days.
|
* @param trustedDevice If the device in use is trusted, 2FA authentication is not required for 30 days.
|
||||||
*/
|
*/
|
||||||
export async function send2faCode(
|
export async function send2faCode(
|
||||||
code: number,
|
code: number,
|
||||||
token: string,
|
token: string,
|
||||||
|
provider: ProviderT = "auto",
|
||||||
trustedDevice: boolean = false
|
trustedDevice: boolean = false
|
||||||
): Promise<Result<GenericAxiosError, LoginResult>> {
|
): Promise<Result<GenericAxiosError, LoginResult>> {
|
||||||
// Prepare the parameters to send via POST request
|
// Prepare the parameters to send via POST request
|
||||||
|
@ -160,20 +164,25 @@ export async function send2faCode(
|
||||||
_xfWithData: "1",
|
_xfWithData: "1",
|
||||||
code: code.toString(),
|
code: code.toString(),
|
||||||
confirm: "1",
|
confirm: "1",
|
||||||
provider: "totp",
|
provider: provider,
|
||||||
remember: "1",
|
remember: "1",
|
||||||
trust: trustedDevice ? "1" : "0"
|
trust: trustedDevice ? "1" : "0"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send 2FA params
|
// Send 2FA params
|
||||||
const response = await fetchPOSTResponse(urls.LOGIN_2FA, params);
|
const response = await fetchPOSTResponse(urls.LOGIN_2FA, params);
|
||||||
return response.applyOnSuccess((r: AxiosResponse<any>) => {
|
|
||||||
// r.data.status is 'ok' if the authentication is successful
|
// Check if the authentication is valid
|
||||||
const result = r.data.status === "ok";
|
const validAuth = response.applyOnSuccess((r) => manage2faResponse(r));
|
||||||
const message: string = result ? AUTH_SUCCESSFUL_MESSAGE : r.data.errors.join(",");
|
|
||||||
const code = messageToCode(message);
|
if (validAuth.isSuccess() && validAuth.value.isSuccess()) {
|
||||||
return new LoginResult(result, code, message);
|
// Valid login
|
||||||
});
|
return success(validAuth.value.value);
|
||||||
|
} else if (validAuth.isSuccess() && validAuth.value.isFailure()) {
|
||||||
|
// Wrong provider, try with another
|
||||||
|
const expectedProvider = validAuth.value.value;
|
||||||
|
return await send2faCode(code, token, expectedProvider, trustedDevice);
|
||||||
|
} else failure(validAuth.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -403,4 +412,25 @@ function messageToCode(message: string): number {
|
||||||
return result ? result.code : LoginResult.UNKNOWN_ERROR;
|
return result ? result.code : LoginResult.UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage the response given by the platform when the 2FA is required.
|
||||||
|
*/
|
||||||
|
function manage2faResponse(r: AxiosResponse<any>): Result<ProviderT, LoginResult> {
|
||||||
|
// The html property exists only if the provider is wrong
|
||||||
|
const rightProvider = !("html" in r.data);
|
||||||
|
|
||||||
|
// Wrong provider!
|
||||||
|
if (!rightProvider) {
|
||||||
|
const $ = cheerio.load(r.data.html.content);
|
||||||
|
const expectedProvider = $(GENERIC.EXPECTED_2FA_PROVIDER).attr("value");
|
||||||
|
return failure(expectedProvider as ProviderT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// r.data.status is 'ok' if the authentication is successful
|
||||||
|
const result = r.data.status === "ok";
|
||||||
|
const message: string = result ? AUTH_SUCCESSFUL_MESSAGE : r.data.errors.join(",");
|
||||||
|
const loginCode = messageToCode(message);
|
||||||
|
return success(new LoginResult(result, loginCode, message));
|
||||||
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
Loading…
Reference in New Issue