Skip to content

Commit

Permalink
Resumable Sign In
Browse files Browse the repository at this point in the history
  • Loading branch information
JoonWon Choi committed Jun 7, 2024
1 parent 211a7ec commit e78c8e4
Showing 1 changed file with 80 additions and 7 deletions.
87 changes: 80 additions & 7 deletions packages/auth/src/providers/cognito/utils/signInStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { ConsoleLogger } from '@aws-amplify/core';

import { CognitoAuthSignInDetails } from '../types';

import { ChallengeName } from './clients/CognitoIdentityProvider/types';
Expand All @@ -27,6 +29,22 @@ type Store<State, Action> = (reducer: Reducer<State, Action>) => {

type Reducer<State, Action> = (state: State, action: Action) => State;

const logger = new ConsoleLogger('Auth signInStore');

// Minutes until stored session invalidates
const EXPIRATION_MINUTES = 5;
const MS_TO_EXPIRY = 1000 * 60 * EXPIRATION_MINUTES;
const SIGN_IN_STATE_KEYS = [
'username',
'challengeName',
'signInSession',
'expiry',
].reduce((keys: Record<string, string>, key) => {
keys[key] = `CognitoSignInState.${key}`;

return keys;
}, {});

const signInReducer: Reducer<SignInState, SignInAction> = (state, action) => {
switch (action.type) {
case 'SET_SIGN_IN_SESSION':
Expand All @@ -49,22 +67,58 @@ const signInReducer: Reducer<SignInState, SignInAction> = (state, action) => {
username: action.value,
};
case 'SET_INITIAL_STATE':
return defaultState();
return getInitialState();
default:
return state;
}
};

function defaultState(): SignInState {
const isExpired = (expiryDate: string): boolean => {
const expiryTimestamp = Number(expiryDate);
const currentTimestamp = Date.now();

return expiryTimestamp <= currentTimestamp;
};

const clearPersistedSignInState = () => {
for (const key in SIGN_IN_STATE_KEYS) {
sessionStorage.removeItem(key);
}
};

const getDefaultState = (): SignInState => ({
username: undefined,
challengeName: undefined,
signInSession: undefined,
});

// Hydrate signInStore from sessionStorage
const getInitialState = (): SignInState => {
const expiry = sessionStorage.getItem(SIGN_IN_STATE_KEYS.expiry) as string;
if (isExpired(expiry)) {
logger.warn('Sign-in session expired');
clearPersistedSignInState();

return getDefaultState();
}

const username =
sessionStorage.getItem(SIGN_IN_STATE_KEYS.username) ?? ('' as string);
const challengeName = (sessionStorage.getItem(
SIGN_IN_STATE_KEYS.challengeName,
) ?? '') as ChallengeName;
const signInSession =
sessionStorage.getItem(SIGN_IN_STATE_KEYS.signInSession) ?? ('' as string);

return {
username: undefined,
challengeName: undefined,
signInSession: undefined,
username,
challengeName,
signInSession,
};
}
};

const createStore: Store<SignInState, SignInAction> = reducer => {
let currentState = reducer(defaultState(), { type: 'SET_INITIAL_STATE' });
let currentState = reducer(getDefaultState(), { type: 'SET_INITIAL_STATE' });

return {
getState: () => currentState,
Expand All @@ -81,8 +135,27 @@ export function setActiveSignInState(state: SignInState): void {
type: 'SET_SIGN_IN_STATE',
value: state,
});

// Save the local state into sessionStorage
persistSignInState(state);
}

// Free saved sign in states up from both memory and sessionStorage
export function cleanActiveSignInState(): void {
signInStore.dispatch({ type: 'SET_INITIAL_STATE' });
clearPersistedSignInState();
}

const persistSignInState = ({
challengeName = '' as ChallengeName,
signInSession = '',
username = '',
}: SignInState) => {
sessionStorage.setItem(SIGN_IN_STATE_KEYS.username, username);
sessionStorage.setItem(SIGN_IN_STATE_KEYS.challengeName, challengeName);

Check failure

Code scanning / CodeQL

Clear text storage of sensitive information High

This stores sensitive data returned by
a call to handleCompleteNewPasswordChallenge
as clear text.
sessionStorage.setItem(SIGN_IN_STATE_KEYS.signInSession, signInSession);

Check failure

Code scanning / CodeQL

Clear text storage of sensitive information High

This stores sensitive data returned by
a call to handleCompleteNewPasswordChallenge
as clear text.
sessionStorage.setItem(
SIGN_IN_STATE_KEYS.expiry,
String(Date.now() + MS_TO_EXPIRY),
);
};

0 comments on commit e78c8e4

Please sign in to comment.