@@ -16,13 +16,13 @@ import * as StorageAccess from "../StorageAccess";
16
16
*/
17
17
18
18
/*
19
- * Keys used when storing the tokens in indexeddb or localstorage
19
+ * Names used when storing the tokens in indexeddb or localstorage
20
20
*/
21
21
export const ACCESS_TOKEN_STORAGE_KEY = "mx_access_token" ;
22
22
export const REFRESH_TOKEN_STORAGE_KEY = "mx_refresh_token" ;
23
23
/*
24
- * Used as initialization vector during encryption in persistTokenInStorage
25
- * And decryption in restoreFromLocalStorage
24
+ * Names of the tokens. Used as part of the calculation to derive AES keys during encryption in persistTokenInStorage,
25
+ * and decryption in restoreSessionFromStorage.
26
26
*/
27
27
export const ACCESS_TOKEN_IV = "access_token" ;
28
28
export const REFRESH_TOKEN_IV = "refresh_token" ;
@@ -60,50 +60,63 @@ async function pickleKeyToAesKey(pickleKey: string): Promise<Uint8Array> {
60
60
) ;
61
61
}
62
62
63
- const isEncryptedPayload = ( token ?: IEncryptedPayload | string | undefined ) : token is IEncryptedPayload => {
64
- return ! ! token && typeof token !== "string" ;
65
- } ;
66
63
/**
67
64
* Try to decrypt a token retrieved from storage
68
- * Where token is not encrypted (plain text) returns the plain text token
69
- * Where token is encrypted, attempts decryption. Returns successfully decrypted token, else undefined.
70
- * @param pickleKey pickle key used during encryption of token, or undefined
71
- * @param token
72
- * @param tokenIv initialization vector used during encryption of token eg ACCESS_TOKEN_IV
73
- * @returns the decrypted token, or the plain text token. Returns undefined when token cannot be decrypted
65
+ *
66
+ * Where token is not encrypted (plain text) returns the plain text token.
67
+ *
68
+ * Where token is encrypted, attempts decryption. Returns successfully decrypted token, or throws if
69
+ * decryption failed.
70
+ *
71
+ * @param pickleKey Pickle key: used to derive the encryption key, or undefined if the token is not encrypted.
72
+ * Must be the same as provided to {@link persistTokenInStorage}.
73
+ * @param token token to be decrypted.
74
+ * @param tokenName Name of the token. Used in logging, but also used as an input when generating the actual AES key,
75
+ * so the same value must be provided to {@link persistTokenInStorage}.
76
+ *
77
+ * @returns the decrypted token, or the plain text token.
74
78
*/
75
79
export async function tryDecryptToken (
76
80
pickleKey : string | undefined ,
77
- token : IEncryptedPayload | string | undefined ,
78
- tokenIv : string ,
79
- ) : Promise < string | undefined > {
80
- if ( pickleKey && isEncryptedPayload ( token ) ) {
81
- const encrKey = await pickleKeyToAesKey ( pickleKey ) ;
82
- const decryptedToken = await decryptAES ( token , encrKey , tokenIv ) ;
83
- encrKey . fill ( 0 ) ;
84
- return decryptedToken ;
85
- }
86
- // if the token wasn't encrypted (plain string) just return it back
81
+ token : IEncryptedPayload | string ,
82
+ tokenName : string ,
83
+ ) : Promise < string > {
87
84
if ( typeof token === "string" ) {
85
+ // Looks like an unencrypted token
88
86
return token ;
89
87
}
90
- // otherwise return undefined
88
+
89
+ // Otherwise, it must be an encrypted token.
90
+ if ( ! pickleKey ) {
91
+ throw new Error ( `Error decrypting secret ${ tokenName } : no pickle key found.` ) ;
92
+ }
93
+
94
+ const encrKey = await pickleKeyToAesKey ( pickleKey ) ;
95
+ const decryptedToken = await decryptAES ( token , encrKey , tokenName ) ;
96
+ encrKey . fill ( 0 ) ;
97
+ return decryptedToken ;
91
98
}
92
99
93
100
/**
94
101
* Persist a token in storage
95
- * When pickle key is present, will attempt to encrypt the token
96
- * Stores in idb, falling back to localStorage
97
102
*
98
- * @param storageKey key used to store the token
99
- * @param initializationVector Initialization vector for encrypting the token. Only used when `pickleKey` is present
100
- * @param token the token to store, when undefined any existing token at the storageKey is removed from storage
101
- * @param pickleKey optional pickle key used to encrypt token
102
- * @param hasTokenStorageKey Localstorage key for an item which stores whether we expect to have a token in indexeddb, eg "mx_has_access_token".
103
+ * When pickle key is present, will attempt to encrypt the token. If encryption fails (typically because
104
+ * WebCrypto is unavailable), the key will be stored unencrypted.
105
+ *
106
+ * Stores in IndexedDB, falling back to localStorage.
107
+ *
108
+ * @param storageKey key used to store the token. Note: not an encryption key; rather a localstorage or indexeddb key.
109
+ * @param tokenName Name of the token. Used in logging, but also used as an input when generating the actual AES key,
110
+ * so the same value must be provided to {@link tryDecryptToken} when decrypting.
111
+ * @param token the token to store. When undefined, any existing token at the `storageKey` is removed from storage.
112
+ * @param pickleKey Pickle key: used to derive the key used to encrypt token. If `undefined`, the token will be stored
113
+ * unencrypted.
114
+ * @param hasTokenStorageKey Localstorage key for an item which stores whether we expect to have a token in indexeddb,
115
+ * eg "mx_has_access_token".
103
116
*/
104
117
export async function persistTokenInStorage (
105
118
storageKey : string ,
106
- initializationVector : string ,
119
+ tokenName : string ,
107
120
token : string | undefined ,
108
121
pickleKey : string | undefined ,
109
122
hasTokenStorageKey : string ,
@@ -122,12 +135,12 @@ export async function persistTokenInStorage(
122
135
try {
123
136
// try to encrypt the access token using the pickle key
124
137
const encrKey = await pickleKeyToAesKey ( pickleKey ) ;
125
- encryptedToken = await encryptAES ( token , encrKey , initializationVector ) ;
138
+ encryptedToken = await encryptAES ( token , encrKey , tokenName ) ;
126
139
encrKey . fill ( 0 ) ;
127
140
} catch ( e ) {
128
141
// This is likely due to the browser not having WebCrypto or somesuch.
129
142
// Warn about it, but fall back to storing the unencrypted token.
130
- logger . warn ( `Could not encrypt token for ${ storageKey } ` , e ) ;
143
+ logger . warn ( `Could not encrypt token for ${ tokenName } ` , e ) ;
131
144
}
132
145
}
133
146
try {
@@ -159,9 +172,11 @@ export async function persistTokenInStorage(
159
172
}
160
173
161
174
/**
162
- * Wraps persistTokenInStorage with accessToken storage keys
163
- * @param token the token to store, when undefined any existing accessToken is removed from storage
164
- * @param pickleKey optional pickle key used to encrypt token
175
+ * Wraps {@link persistTokenInStorage} with accessToken storage keys
176
+ *
177
+ * @param token - The token to store. When undefined, any existing accessToken is removed from storage.
178
+ * @param pickleKey - Pickle key: used to derive the key used to encrypt token. If `undefined`, the token will be stored
179
+ * unencrypted.
165
180
*/
166
181
export async function persistAccessTokenInStorage (
167
182
token : string | undefined ,
@@ -177,9 +192,11 @@ export async function persistAccessTokenInStorage(
177
192
}
178
193
179
194
/**
180
- * Wraps persistTokenInStorage with refreshToken storage keys
181
- * @param token the token to store, when undefined any existing refreshToken is removed from storage
182
- * @param pickleKey optional pickle key used to encrypt token
195
+ * Wraps {@link persistTokenInStorage} with refreshToken storage keys.
196
+ *
197
+ * @param token - The token to store. When undefined, any existing refreshToken is removed from storage.
198
+ * @param pickleKey - Pickle key: used to derive the key used to encrypt token. If `undefined`, the token will be stored
199
+ * unencrypted.
183
200
*/
184
201
export async function persistRefreshTokenInStorage (
185
202
token : string | undefined ,
0 commit comments