-
-
Save laudaikinhdi/68858a280537091b0bc784ca128b0473 to your computer and use it in GitHub Desktop.
vuex 4 cognito module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
ActionContext, | |
ActionTree, | |
GetterTree, | |
MutationTree, | |
Module, | |
Store as VuexStore, | |
CommitOptions, | |
DispatchOptions, | |
} from 'vuex' | |
import { SignUpParams } from '@aws-amplify/auth/lib-esm/types' | |
import { CognitoUserAttribute as Attribute } from 'amazon-cognito-identity-js' | |
import Auth from '@aws-amplify/auth' | |
import { State as RootState } from '@/store' | |
import { AccountService } from '@/modules/auth/services/account' | |
// Declare types | |
export type AuthenticationStatus = { | |
state?: string | |
message?: string | |
variant?: string | |
} | |
export type Credentials = { | |
username: string | |
password: string | |
} | |
export type ConfirmationParams = { | |
username: string | |
code: string | |
} | |
export enum AttributeNames { | |
NAME = 'name', | |
EMAIL = 'email', | |
EMAIL_VERIFIED = 'email_verified', | |
SUB = 'sub', | |
} | |
// Declare state | |
export type State = { | |
teamId?: string | |
isAuthenticated: boolean | |
authenticationStatus?: AuthenticationStatus | |
passwordForgetUsername?: string | |
attributes?: Attribute[] | |
} | |
// Create initial state | |
const state: State = { | |
isAuthenticated: false, | |
} | |
// mutations enums | |
export enum MutationTypes { | |
AUTHENTICATION_ERROR = 'AUTHENTICATION_ERROR', | |
CLEAR_AUTHENTICATION_STATUS = 'CLEAR_AUTHENTICATION_STATUS', | |
CLEAR_AUTHENTICATION = 'CLEAR_AUTHENTICATION', | |
SET_USER_AUTHENTICATED = 'SET_USER_AUTHENTICATED', | |
SET_TEAM_ID = 'SET_TEAM_ID', | |
PASSWORD_FORGET_SET_USERNAME = 'PASSWORD_FORGET_SET_USERNAME', | |
CLEAR_PASSWORD_FORGET_USERNAME = 'CLEAR_PASSWORD_FORGET_SET_USERNAME', | |
SET_ATTRIBUTES = 'SET_ATTRIBUTES', | |
} | |
// Mutation contracts | |
export type Mutations<S = State> = { | |
[MutationTypes.AUTHENTICATION_ERROR](state: S, err: Error): void | |
[MutationTypes.CLEAR_AUTHENTICATION_STATUS](state: S): void | |
[MutationTypes.CLEAR_AUTHENTICATION](state: S): void | |
[MutationTypes.SET_USER_AUTHENTICATED](state: S): void | |
[MutationTypes.SET_TEAM_ID](state: S, teamId: string): void | |
[MutationTypes.PASSWORD_FORGET_SET_USERNAME](state: S, username: string): void | |
[MutationTypes.CLEAR_PASSWORD_FORGET_USERNAME](state: S): void | |
[MutationTypes.SET_ATTRIBUTES](state: S, attributes: Attribute[]): void | |
} | |
// Define mutations | |
const mutations: MutationTree<State> & Mutations = { | |
[MutationTypes.AUTHENTICATION_ERROR](state: State, err: Error) { | |
state.isAuthenticated = false | |
state.authenticationStatus = { | |
state: 'failed', | |
message: err.message, | |
variant: 'danger', | |
} | |
}, | |
[MutationTypes.CLEAR_AUTHENTICATION_STATUS](state: State) { | |
state.authenticationStatus = undefined | |
}, | |
[MutationTypes.CLEAR_AUTHENTICATION](state: State) { | |
state.isAuthenticated = false | |
state.authenticationStatus = undefined | |
}, | |
[MutationTypes.SET_USER_AUTHENTICATED](state: State) { | |
state.isAuthenticated = true | |
}, | |
[MutationTypes.SET_TEAM_ID](state: State, teamId: string) { | |
state.teamId = teamId | |
}, | |
[MutationTypes.PASSWORD_FORGET_SET_USERNAME](state: State, username: string) { | |
state.passwordForgetUsername = username | |
}, | |
[MutationTypes.CLEAR_PASSWORD_FORGET_USERNAME](state: State) { | |
state.passwordForgetUsername = undefined | |
}, | |
[MutationTypes.SET_ATTRIBUTES](state: State, attributes: Attribute[]) { | |
state.attributes = attributes | |
}, | |
} | |
// Action enums | |
export enum ActionTypes { | |
SIGNUP = 'SIGNUP', | |
CONFIRM_SIGNUP = 'CONFIRM_SIGNUP', | |
SIGNIN = 'SIGNIN', | |
SIGNOUT = 'SIGNOUT', | |
INIT_PASSWORD_FORGET = 'INIT_PASSWORD_FORGET', | |
PASSWORD_FORGET_SUBMIT = 'PASSWORD_FORGET_SUBMIT', | |
FETCH_ATTRIBUTES = 'FETCH_ATTRIBUTES', | |
CHANGE_EMAIL = 'CHANGE_EMAIL', | |
} | |
// Actions context | |
type AugmentedActionContext = { | |
commit<K extends keyof Mutations>( | |
key: K, | |
payload: Parameters<Mutations[K]>[1], | |
): ReturnType<Mutations[K]> | |
getters<K extends keyof Getters>( | |
key: K, | |
payload: Parameters<Getters[K]>[1], | |
): ReturnType<Getters[K]> | |
} & Omit<ActionContext<State, RootState>, 'commit'> | |
// Actions contracts | |
export interface Actions { | |
[ActionTypes.SIGNUP]( | |
{ commit }: AugmentedActionContext, | |
payload: SignUpParams, | |
): void | |
[ActionTypes.CONFIRM_SIGNUP]( | |
{ commit }: AugmentedActionContext, | |
payload: ConfirmationParams, | |
): void | |
[ActionTypes.SIGNIN]( | |
{ commit }: AugmentedActionContext, | |
payload: Credentials, | |
): void | |
[ActionTypes.SIGNOUT]( | |
{ commit }: AugmentedActionContext, | |
payload: undefined, | |
): void | |
[ActionTypes.INIT_PASSWORD_FORGET]( | |
{ commit }: AugmentedActionContext, | |
username: string, | |
): void | |
[ActionTypes.PASSWORD_FORGET_SUBMIT]( | |
{ commit }: AugmentedActionContext, | |
payload: { username: string; code: string; password: string }, | |
): void | |
[ActionTypes.FETCH_ATTRIBUTES]( | |
{ commit }: AugmentedActionContext, | |
payload: undefined, | |
): void | |
[ActionTypes.CHANGE_EMAIL]( | |
{ commit, getters }: AugmentedActionContext, | |
email: string, | |
): void | |
} | |
// Define actions | |
export const actions: ActionTree<State, RootState> & Actions = { | |
async [ActionTypes.SIGNUP]({ commit }, payload: SignUpParams) { | |
commit(MutationTypes.CLEAR_AUTHENTICATION_STATUS, undefined) | |
try { | |
await Auth.signUp(payload) | |
commit(MutationTypes.CLEAR_AUTHENTICATION, undefined) | |
} catch (err) { | |
commit(MutationTypes.AUTHENTICATION_ERROR, err) | |
} | |
}, | |
async [ActionTypes.CONFIRM_SIGNUP]({ commit }, payload: ConfirmationParams) { | |
commit(MutationTypes.CLEAR_AUTHENTICATION_STATUS, undefined) | |
try { | |
await Auth.confirmSignUp(payload.username, payload.code) | |
} catch (err) { | |
commit(MutationTypes.AUTHENTICATION_ERROR, err) | |
} | |
}, | |
async [ActionTypes.SIGNIN]({ commit }, payload: Credentials) { | |
commit(MutationTypes.CLEAR_AUTHENTICATION_STATUS, undefined) | |
try { | |
await Auth.signIn(payload.username, payload.password) | |
commit(MutationTypes.SET_USER_AUTHENTICATED, undefined) | |
// toDo: Temporary solution for setting team id | |
const accountService = new AccountService() | |
const account = await accountService.getAccount() | |
commit(MutationTypes.SET_TEAM_ID, account.teams[0].team_id) | |
} catch (err) { | |
commit(MutationTypes.AUTHENTICATION_ERROR, err) | |
} | |
}, | |
async [ActionTypes.SIGNOUT]({ commit }) { | |
try { | |
await Auth.signOut() | |
} catch (err) { | |
commit(MutationTypes.AUTHENTICATION_ERROR, err) | |
} | |
commit(MutationTypes.CLEAR_AUTHENTICATION, undefined) | |
}, | |
async [ActionTypes.INIT_PASSWORD_FORGET]({ commit }, username: string) { | |
commit(MutationTypes.CLEAR_AUTHENTICATION_STATUS, undefined) | |
try { | |
await Auth.forgotPassword(username) | |
} catch (err) { | |
commit(MutationTypes.AUTHENTICATION_ERROR, err) | |
} | |
}, | |
async [ActionTypes.PASSWORD_FORGET_SUBMIT]( | |
{ commit }, | |
payload: { username: string; code: string; password: string }, | |
) { | |
const { username, code, password } = payload | |
commit(MutationTypes.CLEAR_AUTHENTICATION_STATUS, undefined) | |
try { | |
await Auth.forgotPasswordSubmit(username, code, password) | |
} catch (err) { | |
commit(MutationTypes.AUTHENTICATION_ERROR, err) | |
} | |
}, | |
async [ActionTypes.FETCH_ATTRIBUTES]({ commit }) { | |
const user = await Auth.currentUserPoolUser() | |
try { | |
const attributes: Attribute[] = await Auth.userAttributes(user) | |
commit(MutationTypes.SET_ATTRIBUTES, attributes) | |
} catch (err) { | |
commit(MutationTypes.AUTHENTICATION_ERROR, err) | |
} | |
}, | |
async [ActionTypes.CHANGE_EMAIL]({ commit, getters }, email) { | |
const emailAttribute = getters.getAttribute(AttributeNames.EMAIL) | |
try { | |
const user = await Auth.currentUserPoolUser() | |
await Auth.updateUserAttributes(user, { ...emailAttribute, Value: email }) | |
} catch (err) { | |
commit(MutationTypes.AUTHENTICATION_ERROR, err) | |
} | |
}, | |
} | |
// getters types | |
export type Getters = { | |
isAuthenticated(state: State): boolean | |
hasAuthenticationStatus(state: State): boolean | |
getAuthenticationStatus(state: State): AuthenticationStatus | undefined | |
getTeamId(state: State): string | undefined | |
getPasswordForgetUsername(state: State): string | undefined | |
getAttributes(state: State): Attribute[] | undefined | |
getAttribute(state: State): (name: AttributeNames) => Attribute | undefined | |
getFullName(state: State, getters: Getters): string | undefined | |
getEmail(state: State, getters: Getters): string | undefined | |
isEmailVerified(state: State, getters: Getters): string | undefined | |
} | |
// getters | |
export const getters: GetterTree<State, RootState> & Getters = { | |
isAuthenticated: (state) => state.isAuthenticated, | |
hasAuthenticationStatus: (state) => !!state.authenticationStatus, | |
getAuthenticationStatus: (state) => state.authenticationStatus, | |
getTeamId: (state) => state.teamId, | |
getPasswordForgetUsername: (state) => state.passwordForgetUsername, | |
getAttributes: (state) => state.attributes, | |
getAttribute: (state) => (name: AttributeNames) => { | |
if (state.attributes === undefined) return undefined | |
return state.attributes.find((attr) => attr.getName() === name) | |
}, | |
getFullName: (state, getters) => { | |
return getters.getAttribute(AttributeNames.NAME)?.getValue() | |
}, | |
getEmail: (state, getters) => { | |
return getters.getAttribute(AttributeNames.EMAIL)?.getValue() | |
}, | |
isEmailVerified: (state, getters) => { | |
return getters.getAttribute(AttributeNames.EMAIL_VERIFIED)?.getValue() | |
}, | |
} | |
//setup store type | |
export type Store<S = State> = Omit< | |
VuexStore<S>, | |
'commit' | 'getters' | 'dispatch' | |
> & { | |
commit<K extends keyof Mutations, P extends Parameters<Mutations[K]>[1]>( | |
key: K, | |
payload: P, | |
options?: CommitOptions, | |
): ReturnType<Mutations[K]> | |
} & { | |
getters: { | |
[K in keyof Getters]: ReturnType<Getters[K]> | |
} | |
} & { | |
dispatch<K extends keyof Actions>( | |
key: K, | |
payload: Parameters<Actions[K]>[1], | |
options?: DispatchOptions, | |
): ReturnType<Actions[K]> | |
} | |
export const AuthModule: Module<State, RootState> = { | |
state, | |
mutations, | |
actions, | |
// Namespacing Vuex modules is tricky and hard to type check with typescript. | |
// Instead of namespacing, we could create our own namespacing mechanism by | |
// prefixing the value of the TypeScript enum with the namespace, e.g. | |
// enum TodoActions { | |
// AddTodo = 'TODO__ADD_TODO' | |
// } | |
// namespaced: true, | |
getters, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment