1 import { decodeUtf8Base64, encodeUtf8Base64 } from '@proton/crypto/lib/utils';
2 import type { OfflineConfig } from '@proton/pass/lib/cache/crypto';
3 import { AuthMode, type Maybe, type Store } from '@proton/pass/types';
4 import { deobfuscate, obfuscate } from '@proton/pass/utils/obfuscate/xor';
5 import { isObject } from '@proton/pass/utils/object/is-object';
6 import { encodedGetter, encodedSetter } from '@proton/pass/utils/store';
8 import { AUTH_MODE } from './flags';
9 import { LockMode } from './lock/types';
10 import type { AuthSessionVersion, EncryptedAuthSession } from './session';
11 import { type AuthSession, SESSION_VERSION } from './session';
13 export type AuthStore = ReturnType<typeof createAuthStore>;
14 export type AuthStoreOptions = { cookies: boolean };
16 const PASS_ACCESS_TOKEN_KEY = 'pass:access_token';
17 const PASS_COOKIE_AUTH_KEY = 'pass:auth_cookies';
18 const PASS_CLIENT_KEY = 'pass:client_key';
19 const PASS_EXTRA_PWD_KEY = 'pass:extra_password';
20 const PASS_LOCAL_ID_KEY = 'pass:local_id';
21 const PASS_LOCK_EXTEND_TIME_KEY = 'pass:lock_extend_time';
22 const PASS_LOCK_MODE_KEY = 'pass:lock_mode';
23 const PASS_LOCK_STATE_KEY = 'pass:lock_state';
24 const PASS_LOCK_TOKEN_KEY = 'pass:lock_token';
25 const PASS_LOCK_TTL_KEY = 'pass:lock_ttl';
26 const PASS_MAILBOX_PWD_KEY = 'pass:mailbox_pwd';
27 const PASS_OFFLINE_CONFIG_KEY = 'pass:offline_config';
28 const PASS_OFFLINE_KD_KEY = 'pass:offline_kd';
29 const PASS_OFFLINE_VERIFIER_KEY = 'pass:offline_verifier';
30 const PASS_PERSISTENT_SESSION_KEY = 'pass:persistent';
31 const PASS_REFRESH_TIME_KEY = 'pass:refresh_time';
32 const PASS_REFRESH_TOKEN_KEY = 'pass:refresh_token';
33 const PASS_SESSION_VERSION_KEY = 'pass:session_version';
34 const PASS_UID_KEY = 'pass:uid';
35 const PASS_UNLOCK_RETRY_KEY = 'pass:unlock_retry_count';
36 const PASS_USER_ID_KEY = 'pass:user_id';
37 const PASS_ENCRYPTED_OFFLINE_KD = 'pass:encrypted_offline_kd';
38 const PASS_LAST_USED_AT = 'pass:last_used_at';
39 const PASS_USER_DISPLAY_NAME = 'pass:user_display_name';
40 const PASS_USER_EMAIL = 'pass:user_email';
42 export const encodeUserData = (email: string = '', displayName: string = '') => {
43 const encodedEmail = JSON.stringify(obfuscate(email));
44 const encodedDisplayName = JSON.stringify(obfuscate(displayName));
45 return encodeUtf8Base64(`${encodedEmail}.${encodedDisplayName}`);
48 export const decodeUserData = (userData: string): { PrimaryEmail?: string; DisplayName?: string } => {
50 const [encodedEmail, encodedDisplayName] = decodeUtf8Base64(userData).split('.');
52 PrimaryEmail: deobfuscate(JSON.parse(encodedEmail)),
53 DisplayName: deobfuscate(JSON.parse(encodedDisplayName)),
60 export const createAuthStore = (store: Store) => {
62 clear: () => store.reset(),
64 hasSession: (localID?: number) =>
65 Boolean(authStore.getUID() && (localID === undefined || authStore.getLocalID() === localID)),
67 hasOfflinePassword: () =>
68 Boolean(authStore.getOfflineConfig() && authStore.getOfflineKD() && authStore.getOfflineVerifier()),
70 getSession: (): AuthSession => ({
71 AccessToken: authStore.getAccessToken() ?? '',
72 cookies: authStore.getCookieAuth() ?? false,
73 encryptedOfflineKD: authStore.getEncryptedOfflineKD(),
74 extraPassword: authStore.getExtraPassword(),
75 keyPassword: authStore.getPassword() ?? '',
76 lastUsedAt: authStore.getLastUsedAt(),
77 LocalID: authStore.getLocalID(),
78 lockMode: authStore.getLockMode(),
79 lockTTL: authStore.getLockTTL(),
80 offlineConfig: authStore.getOfflineConfig(),
81 offlineKD: authStore.getOfflineKD(),
82 offlineVerifier: authStore.getOfflineVerifier(),
83 payloadVersion: authStore.getSessionVersion(),
84 persistent: authStore.getPersistent(),
85 RefreshTime: authStore.getRefreshTime(),
86 RefreshToken: authStore.getRefreshToken() ?? '',
87 sessionLockToken: authStore.getLockToken(),
88 UID: authStore.getUID() ?? '',
89 unlockRetryCount: authStore.getUnlockRetryCount(),
90 userData: authStore.getUserData(),
91 UserID: authStore.getUserID() ?? '',
94 shouldCookieUpgrade: (data: Partial<AuthSession>) => AUTH_MODE === AuthMode.COOKIE && !data.cookies,
96 validSession: (data: Partial<AuthSession>): data is AuthSession =>
101 (!data.offlineConfig || data.offlineKD) &&
102 (data.cookies || (data.AccessToken && data.RefreshToken))
105 /** Checks wether a parsed persisted session object is
106 * a valid `EncryptedAuthSession` in order to resume */
107 validPersistedSession: (data: any): data is EncryptedAuthSession =>
109 Boolean('UID' in data && data.UID) &&
110 Boolean('UserID' in data && data.UserID) &&
111 Boolean('blob' in data && data.blob) &&
112 (Boolean('cookies' in data && data.cookies) ||
113 (Boolean('AccessToken' in data && data.AccessToken) &&
114 Boolean('RefreshToken' in data && data.RefreshToken))),
116 setSession: (session: Partial<AuthSession>) => {
117 if (session.AccessToken) authStore.setAccessToken(session.AccessToken);
118 if (session.cookies) authStore.setCookieAuth(session.cookies);
119 if (session.encryptedOfflineKD) authStore.setEncryptedOfflineKD(session.encryptedOfflineKD);
120 if (session.extraPassword) authStore.setExtraPassword(true);
121 if (session.keyPassword) authStore.setPassword(session.keyPassword);
122 if (session.lastUsedAt !== undefined) authStore.setLastUsedAt(session.lastUsedAt);
123 if (session.LocalID !== undefined) authStore.setLocalID(session.LocalID);
124 if (session.lockMode) authStore.setLockMode(session.lockMode);
125 if (session.lockTTL) authStore.setLockTTL(session.lockTTL);
126 if (session.offlineConfig) authStore.setOfflineConfig(session.offlineConfig);
127 if (session.offlineKD) authStore.setOfflineKD(session.offlineKD);
128 if (session.offlineVerifier) authStore.setOfflineVerifier(session.offlineVerifier);
129 if (session.payloadVersion !== undefined) authStore.setSessionVersion(session.payloadVersion);
130 if (session.persistent) authStore.setPersistent(session.persistent);
131 if (session.userData !== undefined) authStore.setUserData(session.userData);
132 if (session.RefreshTime) authStore.setRefreshTime(session.RefreshTime);
133 if (session.RefreshToken) authStore.setRefreshToken(session.RefreshToken);
134 if (session.sessionLockToken) authStore.setLockToken(session.sessionLockToken);
135 if (session.UID) authStore.setUID(session.UID);
136 if (session.unlockRetryCount !== undefined) authStore.setUnlockRetryCount(session.unlockRetryCount);
137 if (session.UserID) authStore.setUserID(session.UserID);
140 setAccessToken: (accessToken: Maybe<string>): void => store.set(PASS_ACCESS_TOKEN_KEY, accessToken),
141 getAccessToken: (): Maybe<string> => store.get(PASS_ACCESS_TOKEN_KEY),
142 setRefreshToken: (refreshToken: Maybe<string>): void => store.set(PASS_REFRESH_TOKEN_KEY, refreshToken),
143 getRefreshToken: (): Maybe<string> => store.get(PASS_REFRESH_TOKEN_KEY),
144 setRefreshTime: (refreshTime: Maybe<number>) => store.set(PASS_REFRESH_TIME_KEY, refreshTime),
145 getRefreshTime: (): Maybe<number> => store.get(PASS_REFRESH_TIME_KEY),
147 setUID: (UID: Maybe<string>): void => store.set(PASS_UID_KEY, UID),
148 getUID: (): Maybe<string> => store.get(PASS_UID_KEY),
149 setUserID: (UserID: Maybe<string>): void => store.set(PASS_USER_ID_KEY, UserID),
150 getUserID: (): Maybe<string> => store.get(PASS_USER_ID_KEY),
151 setUserEmail: encodedSetter(store)(PASS_USER_EMAIL),
152 getUserEmail: encodedGetter(store)(PASS_USER_EMAIL),
153 setUserDisplayName: encodedSetter(store)(PASS_USER_DISPLAY_NAME),
154 getUserDisplayName: encodedGetter(store)(PASS_USER_DISPLAY_NAME),
156 getUserData: () => encodeUserData(authStore.getUserEmail(), authStore.getUserDisplayName()),
157 setUserData: (userData: string) => {
158 const data = decodeUserData(userData);
160 authStore.setUserEmail(data.PrimaryEmail);
161 authStore.setUserDisplayName(data.DisplayName);
165 setPassword: encodedSetter(store)(PASS_MAILBOX_PWD_KEY),
166 getPassword: encodedGetter(store)(PASS_MAILBOX_PWD_KEY),
167 setLocalID: (LocalID: Maybe<number>): void => store.set(PASS_LOCAL_ID_KEY, LocalID),
168 getLocalID: (): Maybe<number> => store.get(PASS_LOCAL_ID_KEY),
170 setOfflineKD: encodedSetter(store)(PASS_OFFLINE_KD_KEY),
171 getOfflineKD: encodedGetter(store)(PASS_OFFLINE_KD_KEY),
172 setOfflineConfig: (config: Maybe<OfflineConfig>) => store.set(PASS_OFFLINE_CONFIG_KEY, config),
173 getOfflineConfig: (): Maybe<OfflineConfig> => store.get(PASS_OFFLINE_CONFIG_KEY),
174 setOfflineVerifier: encodedSetter(store)(PASS_OFFLINE_VERIFIER_KEY),
175 getOfflineVerifier: encodedGetter(store)(PASS_OFFLINE_VERIFIER_KEY),
176 setEncryptedOfflineKD: (enryptedKD: Maybe<string>) => store.set(PASS_ENCRYPTED_OFFLINE_KD, enryptedKD),
177 getEncryptedOfflineKD: (): Maybe<string> => store.get(PASS_ENCRYPTED_OFFLINE_KD),
179 setLockMode: (mode: LockMode): void => store.set(PASS_LOCK_MODE_KEY, mode),
180 getLockMode: (): LockMode => store.get(PASS_LOCK_MODE_KEY) ?? LockMode.NONE,
181 setLocked: (status: boolean): void => store.set(PASS_LOCK_STATE_KEY, status),
182 getLocked: (): Maybe<boolean> => store.get(PASS_LOCK_STATE_KEY),
183 setLockToken: encodedSetter(store)(PASS_LOCK_TOKEN_KEY),
184 getLockToken: encodedGetter(store)(PASS_LOCK_TOKEN_KEY),
185 setLockTTL: (ttl: Maybe<number>) => store.set(PASS_LOCK_TTL_KEY, ttl),
186 getLockTTL: (): Maybe<number> => store.get(PASS_LOCK_TTL_KEY),
187 setLockLastExtendTime: (extendTime: Maybe<number>): void => store.set(PASS_LOCK_EXTEND_TIME_KEY, extendTime),
188 getLockLastExtendTime: (): Maybe<number> => store.get(PASS_LOCK_EXTEND_TIME_KEY),
190 setLastUsedAt: (lastUsedAt: number): void => store.set(PASS_LAST_USED_AT, lastUsedAt),
191 getLastUsedAt: (): number => store.get(PASS_LAST_USED_AT) ?? 0,
193 setUnlockRetryCount: (count: number): void => store.set(PASS_UNLOCK_RETRY_KEY, count),
194 getUnlockRetryCount: (): number => store.get(PASS_UNLOCK_RETRY_KEY) ?? 0,
196 setExtraPassword: (enabled: boolean): void => store.set(PASS_EXTRA_PWD_KEY, enabled),
197 getExtraPassword: () => store.get(PASS_EXTRA_PWD_KEY) ?? false,
199 getSessionVersion: (): AuthSessionVersion => store.get(PASS_SESSION_VERSION_KEY) ?? SESSION_VERSION,
200 setSessionVersion: (version: AuthSessionVersion) => store.set(PASS_SESSION_VERSION_KEY, version),
202 getClientKey: encodedGetter(store)(PASS_CLIENT_KEY),
203 setClientKey: encodedSetter(store)(PASS_CLIENT_KEY),
205 getPersistent: (): Maybe<boolean> => store.get(PASS_PERSISTENT_SESSION_KEY),
206 setPersistent: (persistent: boolean): void => store.set(PASS_PERSISTENT_SESSION_KEY, persistent),
208 setCookieAuth: (enabled: boolean): void => store.set(PASS_COOKIE_AUTH_KEY, enabled),
209 getCookieAuth: (): boolean => store.get(PASS_COOKIE_AUTH_KEY) ?? false,
215 export let authStore: AuthStore;
216 export const exposeAuthStore = (value: AuthStore) => (authStore = value);