@@ -20,7 +20,7 @@ limitations under the License.
20
20
import { ReactNode } from "react" ;
21
21
import { createClient , MatrixClient , SSOAction } from "matrix-js-sdk/src/matrix" ;
22
22
import { InvalidStoreError } from "matrix-js-sdk/src/errors" ;
23
- import { decryptAES , encryptAES , IEncryptedPayload } from "matrix-js-sdk/src/crypto/aes" ;
23
+ import { IEncryptedPayload } from "matrix-js-sdk/src/crypto/aes" ;
24
24
import { QueryDict } from "matrix-js-sdk/src/utils" ;
25
25
import { logger } from "matrix-js-sdk/src/logger" ;
26
26
import { MINIMUM_MATRIX_VERSION } from "matrix-js-sdk/src/version-support" ;
@@ -67,27 +67,21 @@ import { messageForLoginError } from "./utils/ErrorUtils";
67
67
import { completeOidcLogin } from "./utils/oidc/authorize" ;
68
68
import { persistOidcAuthenticatedSettings } from "./utils/oidc/persistOidcSettings" ;
69
69
import GenericToast from "./components/views/toasts/GenericToast" ;
70
+ import {
71
+ ACCESS_TOKEN_IV ,
72
+ ACCESS_TOKEN_STORAGE_KEY ,
73
+ HAS_ACCESS_TOKEN_STORAGE_KEY ,
74
+ HAS_REFRESH_TOKEN_STORAGE_KEY ,
75
+ persistAccessTokenInStorage ,
76
+ persistRefreshTokenInStorage ,
77
+ REFRESH_TOKEN_IV ,
78
+ REFRESH_TOKEN_STORAGE_KEY ,
79
+ tryDecryptToken ,
80
+ } from "./utils/tokens/tokens" ;
70
81
71
82
const HOMESERVER_URL_KEY = "mx_hs_url" ;
72
83
const ID_SERVER_URL_KEY = "mx_is_url" ;
73
84
74
- /*
75
- * Keys used when storing the tokens in indexeddb or localstorage
76
- */
77
- const ACCESS_TOKEN_STORAGE_KEY = "mx_access_token" ;
78
- const REFRESH_TOKEN_STORAGE_KEY = "mx_refresh_token" ;
79
- /*
80
- * Used as initialization vector during encryption in persistTokenInStorage
81
- * And decryption in restoreFromLocalStorage
82
- */
83
- const ACCESS_TOKEN_IV = "access_token" ;
84
- const REFRESH_TOKEN_IV = "refresh_token" ;
85
- /*
86
- * Keys for localstorage items which indicate whether we expect a token in indexeddb.
87
- */
88
- const HAS_ACCESS_TOKEN_STORAGE_KEY = "mx_has_access_token" ;
89
- const HAS_REFRESH_TOKEN_STORAGE_KEY = "mx_has_refresh_token" ;
90
-
91
85
dis . register ( ( payload ) => {
92
86
if ( payload . action === Action . TriggerLogout ) {
93
87
// noinspection JSIgnoredPromiseFromCall - we don't care if it fails
@@ -566,32 +560,6 @@ export async function getStoredSessionVars(): Promise<Partial<IStoredSession>> {
566
560
return { hsUrl, isUrl, hasAccessToken, accessToken, refreshToken, hasRefreshToken, userId, deviceId, isGuest } ;
567
561
}
568
562
569
- // The pickle key is a string of unspecified length and format. For AES, we
570
- // need a 256-bit Uint8Array. So we HKDF the pickle key to generate the AES
571
- // key. The AES key should be zeroed after it is used.
572
- async function pickleKeyToAesKey ( pickleKey : string ) : Promise < Uint8Array > {
573
- const pickleKeyBuffer = new Uint8Array ( pickleKey . length ) ;
574
- for ( let i = 0 ; i < pickleKey . length ; i ++ ) {
575
- pickleKeyBuffer [ i ] = pickleKey . charCodeAt ( i ) ;
576
- }
577
- const hkdfKey = await window . crypto . subtle . importKey ( "raw" , pickleKeyBuffer , "HKDF" , false , [ "deriveBits" ] ) ;
578
- pickleKeyBuffer . fill ( 0 ) ;
579
- return new Uint8Array (
580
- await window . crypto . subtle . deriveBits (
581
- {
582
- name : "HKDF" ,
583
- hash : "SHA-256" ,
584
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
585
- // @ts -ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/879
586
- salt : new Uint8Array ( 32 ) ,
587
- info : new Uint8Array ( 0 ) ,
588
- } ,
589
- hkdfKey ,
590
- 256 ,
591
- ) ,
592
- ) ;
593
- }
594
-
595
563
async function abortLogin ( ) : Promise < void > {
596
564
const signOut = await showStorageEvictedDialog ( ) ;
597
565
if ( signOut ) {
@@ -602,36 +570,6 @@ async function abortLogin(): Promise<void> {
602
570
}
603
571
}
604
572
605
- const isEncryptedPayload = ( token ?: IEncryptedPayload | string | undefined ) : token is IEncryptedPayload => {
606
- return ! ! token && typeof token !== "string" ;
607
- } ;
608
- /**
609
- * Try to decrypt a token retrieved from storage
610
- * Where token is not encrypted (plain text) returns the plain text token
611
- * Where token is encrypted, attempts decryption. Returns successfully decrypted token, else undefined.
612
- * @param pickleKey pickle key used during encryption of token, or undefined
613
- * @param token
614
- * @param tokenIv initialization vector used during encryption of token eg ACCESS_TOKEN_IV
615
- * @returns the decrypted token, or the plain text token. Returns undefined when token cannot be decrypted
616
- */
617
- async function tryDecryptToken (
618
- pickleKey : string | undefined ,
619
- token : IEncryptedPayload | string | undefined ,
620
- tokenIv : string ,
621
- ) : Promise < string | undefined > {
622
- if ( pickleKey && isEncryptedPayload ( token ) ) {
623
- const encrKey = await pickleKeyToAesKey ( pickleKey ) ;
624
- const decryptedToken = await decryptAES ( token , encrKey , tokenIv ) ;
625
- encrKey . fill ( 0 ) ;
626
- return decryptedToken ;
627
- }
628
- // if the token wasn't encrypted (plain string) just return it back
629
- if ( typeof token === "string" ) {
630
- return token ;
631
- }
632
- // otherwise return undefined
633
- }
634
-
635
573
// returns a promise which resolves to true if a session is found in
636
574
// localstorage
637
575
//
@@ -901,73 +839,6 @@ async function showStorageEvictedDialog(): Promise<boolean> {
901
839
// `instanceof`. Babel 7 supports this natively in their class handling.
902
840
class AbortLoginAndRebuildStorage extends Error { }
903
841
904
- /**
905
- * Persist a token in storage
906
- * When pickle key is present, will attempt to encrypt the token
907
- * Stores in idb, falling back to localStorage
908
- *
909
- * @param storageKey key used to store the token
910
- * @param initializationVector Initialization vector for encrypting the token. Only used when `pickleKey` is present
911
- * @param token the token to store, when undefined any existing token at the storageKey is removed from storage
912
- * @param pickleKey optional pickle key used to encrypt token
913
- * @param hasTokenStorageKey Localstorage key for an item which stores whether we expect to have a token in indexeddb, eg "mx_has_access_token".
914
- */
915
- async function persistTokenInStorage (
916
- storageKey : string ,
917
- initializationVector : string ,
918
- token : string | undefined ,
919
- pickleKey : string | undefined ,
920
- hasTokenStorageKey : string ,
921
- ) : Promise < void > {
922
- // store whether we expect to find a token, to detect the case
923
- // where IndexedDB is blown away
924
- if ( token ) {
925
- localStorage . setItem ( hasTokenStorageKey , "true" ) ;
926
- } else {
927
- localStorage . removeItem ( hasTokenStorageKey ) ;
928
- }
929
-
930
- if ( pickleKey ) {
931
- let encryptedToken : IEncryptedPayload | undefined ;
932
- try {
933
- if ( ! token ) {
934
- throw new Error ( "No token: not attempting encryption" ) ;
935
- }
936
- // try to encrypt the access token using the pickle key
937
- const encrKey = await pickleKeyToAesKey ( pickleKey ) ;
938
- encryptedToken = await encryptAES ( token , encrKey , initializationVector ) ;
939
- encrKey . fill ( 0 ) ;
940
- } catch ( e ) {
941
- logger . warn ( "Could not encrypt access token" , e ) ;
942
- }
943
- try {
944
- // save either the encrypted access token, or the plain access
945
- // token if we were unable to encrypt (e.g. if the browser doesn't
946
- // have WebCrypto).
947
- await StorageManager . idbSave ( "account" , storageKey , encryptedToken || token ) ;
948
- } catch ( e ) {
949
- // if we couldn't save to indexedDB, fall back to localStorage. We
950
- // store the access token unencrypted since localStorage only saves
951
- // strings.
952
- if ( ! ! token ) {
953
- localStorage . setItem ( storageKey , token ) ;
954
- } else {
955
- localStorage . removeItem ( storageKey ) ;
956
- }
957
- }
958
- } else {
959
- try {
960
- await StorageManager . idbSave ( "account" , storageKey , token ) ;
961
- } catch ( e ) {
962
- if ( ! ! token ) {
963
- localStorage . setItem ( storageKey , token ) ;
964
- } else {
965
- localStorage . removeItem ( storageKey ) ;
966
- }
967
- }
968
- }
969
- }
970
-
971
842
async function persistCredentials ( credentials : IMatrixClientCreds ) : Promise < void > {
972
843
localStorage . setItem ( HOMESERVER_URL_KEY , credentials . homeserverUrl ) ;
973
844
if ( credentials . identityServerUrl ) {
@@ -976,20 +847,8 @@ async function persistCredentials(credentials: IMatrixClientCreds): Promise<void
976
847
localStorage . setItem ( "mx_user_id" , credentials . userId ) ;
977
848
localStorage . setItem ( "mx_is_guest" , JSON . stringify ( credentials . guest ) ) ;
978
849
979
- await persistTokenInStorage (
980
- ACCESS_TOKEN_STORAGE_KEY ,
981
- ACCESS_TOKEN_IV ,
982
- credentials . accessToken ,
983
- credentials . pickleKey ,
984
- HAS_ACCESS_TOKEN_STORAGE_KEY ,
985
- ) ;
986
- await persistTokenInStorage (
987
- REFRESH_TOKEN_STORAGE_KEY ,
988
- REFRESH_TOKEN_IV ,
989
- credentials . refreshToken ,
990
- credentials . pickleKey ,
991
- HAS_REFRESH_TOKEN_STORAGE_KEY ,
992
- ) ;
850
+ await persistAccessTokenInStorage ( credentials . accessToken , credentials . pickleKey ) ;
851
+ await persistRefreshTokenInStorage ( credentials . refreshToken , credentials . pickleKey ) ;
993
852
994
853
if ( credentials . pickleKey ) {
995
854
localStorage . setItem ( "mx_has_pickle_key" , String ( true ) ) ;
0 commit comments