Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / shared / lib / srp.ts
blob310a50809610c184833e791c602cf4517b3fe92a
1 import { getRandomSrpVerifier, getSrp } from '@proton/srp';
3 import { getInfo, getModulus } from './api/auth';
4 import type { Fido2Data, InfoResponse, ModulusResponse } from './authentication/interface';
5 import type { Api } from './interfaces';
7 export interface AuthCredentials {
8     username?: string;
9     password: string;
12 export type Credentials =
13     | AuthCredentials
14     | (AuthCredentials & { totp: string })
15     | (AuthCredentials & { fido2: Fido2Data });
17 interface SrpAuthData {
18     ClientProof: string;
19     ClientEphemeral: string;
20     TwoFactorCode?: string;
21     SRPSession: string;
24 export interface SrpConfig {
25     [key: string]: any;
28 /**
29  * Call the API with the SRP parameters and validate the server proof.
30  */
31 interface CallAndValidateArguments {
32     api: Api;
33     config: SrpConfig;
34     authData: SrpAuthData;
35     expectedServerProof: string;
38 const callAndValidate = async ({
39     api,
40     config: { data, ...restConfig },
41     authData,
42     expectedServerProof,
43 }: CallAndValidateArguments) => {
44     const response: Response = await api({
45         ...restConfig,
46         output: 'raw',
47         data: {
48             ...authData,
49             ...data,
50         },
51     });
52     const clonedResponse = response.clone();
53     const result = await clonedResponse.json();
55     const { ServerProof } = result;
56     if (ServerProof !== expectedServerProof) {
57         throw new Error('Unexpected server proof');
58     }
60     return response;
63 /**
64  * Perform an API call with SRP auth.
65  */
66 interface SrpAuthArguments {
67     api: Api;
68     credentials: Credentials;
69     config: SrpConfig;
70     info?: InfoResponse;
71     version?: number;
74 export const srpAuth = async ({ api, credentials, config, info, version }: SrpAuthArguments) => {
75     const actualInfo = info || (await api<InfoResponse>(getInfo({ username: credentials.username })));
76     const { expectedServerProof, clientProof, clientEphemeral } = await getSrp(actualInfo, credentials, version);
77     const authData = {
78         ClientProof: clientProof,
79         ClientEphemeral: clientEphemeral,
80         SRPSession: actualInfo.SRPSession,
81         ...('totp' in credentials ? { TwoFactorCode: credentials.totp } : undefined),
82         ...('fido2' in credentials ? { FIDO2: credentials.fido2 } : undefined),
83     };
84     return callAndValidate({
85         api,
86         config,
87         authData,
88         expectedServerProof,
89     });
92 /**
93  * Get initialization parameters for SRP.
94  */
95 export const srpGetVerify = async ({ api, credentials }: { api: Api; credentials: Credentials }) => {
96     const data = await api<ModulusResponse>(getModulus());
97     const { version, salt, verifier } = await getRandomSrpVerifier(data, credentials);
98     const authData = {
99         ModulusID: data.ModulusID,
100         Version: version,
101         Salt: salt,
102         Verifier: verifier,
103     };
104     return {
105         Auth: authData,
106     };
110  * Perform an SRP call with the random verifier.
111  */
112 export const srpVerify = async <T = any>({
113     api,
114     credentials,
115     config: { data, ...restConfig },
116 }: {
117     api: Api;
118     credentials: Credentials;
119     config: SrpConfig;
120 }) => {
121     const authData = await srpGetVerify({ api, credentials });
122     return api<T>({
123         ...restConfig,
124         data: {
125             ...data,
126             ...authData,
127         },
128     });