You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// Redirect to main page TODO application specific: choose correct route
import{useApolloClient}from'@vue/apollo-composable';import*asAmazonCognitoIdentityfrom'amazon-cognito-identity-js';import{CognitoUser,CognitoUserSession}from'amazon-cognito-identity-js';importcloneDeepfrom'lodash/cloneDeep';import{QVueGlobals,useQuasar}from'quasar';import{i18n}from'boot/i18n';importEnvfrom'src/env';importAdminCreatedUserfrom'src/flox/modules/auth/data/types/AdminCreatedUser';import{showSuccessNotification}from'src/tools/notification.tool';importUserEntityfrom'src/flox/modules/auth/entities/user.entity';import{useAuthStore}from'../stores/auth.store';importROUTESfrom'../../../../router/routes';importErrorServicefrom'../../../../services/ErrorService';importRouterServicefrom'../../../../services/RouterService';importForgotPasswordDialogfrom'../components/dialogs/ForgotPasswordDialog.vue';importChangePasswordDialogfrom'../components/dialogs/ChangePasswordDialog.vue';importEmailConfirmationDialogfrom'../components/dialogs/EmailConfirmationDialog.vue';importQrCodeDialogfrom'../components/dialogs/QrCodeDialog.vue';importResetPasswordDialogfrom'../components/dialogs/ResetPasswordDialog.vue';importMFADialogfrom'../components/dialogs/MFADialog.vue';importDELIVERY_MEDIUMSfrom'../../../enum/DELIVERY_MEDIUMS';importROLEfrom'../../../enum/USER_ROLES';import{adminCreateUser,disableUser,enableUser,signup,}from'./user.service';constuserNotDefinedError=i18n.global.t('errors.user_not_defined');/** * This is a service that is used globally throughout the application for maintaining authentication state as well as * signing up, logging in, logging out, changing passwords, and more. */exportdefaultclassAuthenticationService{// Application infoappName: string;// Quasar instance$q: QVueGlobals;// Error handler service$errorService: ErrorService;// Router service$routerService: RouterService;$authStore: ReturnType<typeofuseAuthStore>;interval: ReturnType<typeofsetInterval>|null;/** * Constructor * * @param quasar - Quasar instance * @param errorService - error service instance * @param routerService - router service instance */constructor(quasar: QVueGlobals,errorService: ErrorService,routerService: RouterService){// Storethis.$authStore=useAuthStore();// Set up authentication user poolconstpoolSettings={UserPoolId: Env.VUE_APP_USER_POOL_ID,ClientId: Env.VUE_APP_USER_POOL_CLIENT_ID,};constuserPool=newAmazonCognitoIdentity.CognitoUserPool(poolSettings);this.$authStore.setUserPool(userPool);// Quasar & environment variablesthis.$q=quasar;this.appName=Env.VUE_APP_NAME;// Error servicethis.$errorService=errorService;// Router servicethis.$routerService=routerService;// Every 5 minutes, check whether token is still validif(!Env.SERVER){this.interval=setInterval(()=>{voidthis.refreshToken();},1000*60*5);}else{this.interval=null;}}/** * Logs into the AWS authentication pool using the given data * * @param identifier - the authentication's identifier (usually E-mail or username) * @param password - the authentication's password * @param q - Quasar instance (for opening dialogs) * @param [newPassword] - the new password if this function is triggered from set-password page */asynclogin(identifier: string,password: string,q: QVueGlobals,newPassword=''): Promise<void>{// Generate auth detailsconstauthenticationDetails=newAmazonCognitoIdentity.AuthenticationDetails({Username: identifier,Password: password,});const{ userPool }=this.$authStore;if(!userPool){this.$errorService.showErrorDialog(newError(userNotDefinedError));returnundefined;}// Actual Cognito authentication on given poolconstcognitoUser=newAmazonCognitoIdentity.CognitoUser({Username: identifier,Pool: userPool,});// Execute auth functionreturnnewPromise((resolve)=>{// Store in local variablethis.$authStore.setCognitoUser(cognitoUser);cognitoUser.authenticateUser(authenticationDetails,{onSuccess: (result)=>{voidthis.loginSuccess(result);resolve();},onFailure: (err: Error)=>{this.onFailure(err,identifier,password);},// Sets up MFA & logs in (only done once after signing up)mfaSetup: ()=>{this.setupMFA(cognitoUser,resolve,identifier);},// Called in order to select the MFA token type (SOFTWARE_TOKEN_MFA or SMS_TOKEN_MFA)selectMFAType(){cognitoUser.sendMFASelectionAnswer('SOFTWARE_TOKEN_MFA',this);},// Called when user is in FORCE_PASSWORD_CHANGE state and must thus set a new passwordnewPasswordRequired(userAttributes: Record<string,unknown>,// all user attributes (e.g. 'email', 'email_verified')requiredAttributes: string[]// list of attributes that must be passed in order to update password (usually none)){// Build list of required attributes (for most user pools, this will be left empty)constattributes: Record<string,unknown>={};if(requiredAttributes?.length>0){Object.keys(userAttributes).forEach((key)=>{if(requiredAttributes.includes(key)){attributes[key]=userAttributes[key];}});}// Case 1: Password already given; complete directlyif(newPassword){cognitoUser.completeNewPasswordChallenge(newPassword,attributes,this);return;}// Case 2: No password given: show dialog for setting new oneq.dialog({component: ChangePasswordDialog,}).onOk(({ passwordNew }: {passwordNew: string})=>{cognitoUser.completeNewPasswordChallenge(passwordNew,attributes,this);});},// Called if time-limited one time password is required (only second login or later)totpRequired: (tokenType: string)=>{this.verify2FACode(tokenType,resolve);},// MFA code required (NOT part of normal flow)mfaRequired(){// eslint-disable-next-line no-alertconstverificationCode=prompt(i18n.global.t('messages.enter_verification_code',''));if(typeofverificationCode==='string'){cognitoUser.sendMFACode(verificationCode,this);}},});});}/** * Sets up MFA for the given cognito user * * @param cognitoUser - the user * @param resolve - resolve function * @param identifier - identifier (username of email) */setupMFA(cognitoUser: CognitoUser,resolve: (value: void|PromiseLike<void>)=>void,identifier: string): void{cognitoUser.associateSoftwareToken({// eslint-disable-next-line @typescript-eslint/no-misused-promisesassociateSecretCode: async(secret: string)=>{this.$authStore.setCognitoUser(cognitoUser);awaitthis.showQRCodeDialog(secret,cognitoUser,identifier);resolve();},onFailure: (err: Error)=>{this.onFailure(err);},});}/** * Lets an admin create a new user * @param username - the chosen username * @param email - the authentication's e-mail address * @param role - the user's role * @param deliveryMediums - medium to use to deliver user's new login information (sms, email, both or none) * @param [phoneNumber] - number to send the SMS invitation to * @param [locale] - the chosen language locale * @returns - the newly created user */// eslint-disable-next-line class-methods-use-thisasyncadminCreateUser(username: string,email: string,role: ROLE,deliveryMediums: DELIVERY_MEDIUMS[],phoneNumber?: string,locale?: string): Promise<AdminCreatedUser|null>{returnadminCreateUser(username,email,role,deliveryMediums,phoneNumber,locale);}/** * Signs up by creating a new authentication using the given Username, e-mail, password and language. * * @param username - the chosen username * @param email - the authentication's e-mail address * @param password - the new authentication's chosen password. Must fulfill the set password conditions * @param [lang] - the chosen language */// eslint-disable-next-line class-methods-use-thisasyncsignup(username: string,email: string,password: string,lang?: string): Promise<UserEntity|null>{returnsignup(username,email,password,lang);}/** * Disable all users in the given list * * @param users - list of users to disable */// eslint-disable-next-line class-methods-use-thisasyncdisableUsers(users: UserEntity[]): Promise<PromiseSettledResult<Awaited<UserEntity>|null>[]>{constdisableRequests=users.map((user: UserEntity)=>disableUser(user.uuid));returnPromise.allSettled(disableRequests);}/** * Enable all users in the given list * * @param users - list of users to enable */// eslint-disable-next-line class-methods-use-thisasyncenableUsers(users: UserEntity[]): Promise<PromiseSettledResult<Awaited<UserEntity>|null>[]>{constdisableRequests=users.map((user: UserEntity)=>enableUser(user.uuid));returnPromise.allSettled(disableRequests);}/** * Logs out the currently logged in authentication (if any) */asynclogout(): Promise<void>{// Deep copy to avoid mutating store stateconstcognitoUser: CognitoUser|undefined=cloneDeep(this.$authStore.cognitoUser);if(!cognitoUser){this.$errorService.showErrorDialog(newError(i18n.global.t('errors.not_logged_in')));}else{// Sign outawaitnewPromise<void>((resolve)=>{cognitoUser.signOut(()=>{this.$authStore.setCognitoUser(undefined);this.$authStore.setUserSession(undefined);localStorage.clear();// Needed to remove session,id,... tokensresolve();});});// Redirect to loginawaitthis.$routerService.routeTo(ROUTES.LOGIN);// Clear cache to prevent erroneous loading when changing rolesconstapolloClient=useApolloClient();awaitapolloClient.client.clearStore();}}/** * Shows a dialog for changing password */showChangePasswordDialog(): void{this.$q.dialog({component: ChangePasswordDialog,componentProps: {},}).onOk(({
passwordNew,
passwordOld,}: {passwordNew: string;passwordOld: string;})=>{this.$authStore.cognitoUser?.changePassword(passwordOld,passwordNew,(err: Error|undefined)=>{if(err){this.$errorService.showErrorDialog(err);}});});}/** * Shows a dialog for requesting password reset */showResetPasswordDialog(): void{const{ userPool }=this.$authStore;if(userPool===undefined){this.$errorService.showErrorDialog(newError(userNotDefinedError));return;}this.$q.dialog({component: ForgotPasswordDialog,}).onOk((username: string)=>{// Set up cognitoUser firstthis.$authStore.setCognitoUser(newCognitoUser({Username: username,Pool: userPool,}));// Call forgotPassword on cognitoUserthis.$authStore.cognitoUser?.forgotPassword({onSuccess(){// Do nothing},onFailure: (err: Error)=>{this.$authStore.setCognitoUser(undefined);this.onFailure(err);},inputVerificationCode: ()=>{this.showResetPasswordFormDialog();},});});}/** * Show actual password reset form dialog */showResetPasswordFormDialog(): void{this.$q.dialog({component: ResetPasswordDialog,}).onOk(({
verificationCode,
newPassword,}: {verificationCode: string;newPassword: string;})=>{this.$authStore.cognitoUser?.confirmPassword(verificationCode,newPassword,{onSuccess: ()=>{showSuccessNotification(this.$q,i18n.global.t('messages.password_changed'));},onFailure: (err: Error)=>{this.$errorService.showErrorDialog(err);},});});}/** * Shows a dialog for requesting password reset. The verification code is sent to the given e-mail address * @param username - the username of the logged-in user * @returns void */showRequestNewPasswordDialog(username: string): void{const{ userPool }=this.$authStore;if(userPool===undefined){this.$errorService.showErrorDialog(newError(userNotDefinedError));return;}this.$authStore.setCognitoUser(newCognitoUser({Username: username,Pool: userPool,}));// Call forgotPassword on cognitoUserthis.$authStore.cognitoUser?.forgotPassword({onSuccess(){// Do nothing},onFailure: (err: Error)=>{this.$authStore.setCognitoUser(undefined);this.onFailure(err);},inputVerificationCode: ()=>{this.showResetPasswordFormDialog();},});}/** * Resends the e-mail configuration code for the current user */asyncresendEmailVerificationCode(): Promise<void>{awaitnewPromise((resolve,reject)=>{this.$authStore.cognitoUser?.resendConfirmationCode((err)=>{if(!err){resolve(null);}reject();});});}/** * Shows a dialog for verifying E-Mail */asyncshowEmailVerificationDialog(): Promise<void>{awaitnewPromise((resolve,reject)=>{this.$q.dialog({component: EmailConfirmationDialog,componentProps: {q: this.$q,authService: this,},}).onOk(({ code }: {code: string})=>{this.$authStore.cognitoUser?.confirmRegistration(code,true,(err,result)=>{if(!err){resolve(result);}reject();});});});}/** * Shows a dialog containing a QR code for setting up two factor authentication * * @param secretCode - the authenticator code to encode in QR code form * @param cognitoUser - the cognito user to show the dialog for * @param identifier - identifier (username of email) * @returns void */showQRCodeDialog(secretCode: string,cognitoUser: CognitoUser,identifier: string): Promise<void>{returnnewPromise((resolve)=>{constcodeUrl=`otpauth://totp/${this.appName} (${identifier})?secret=${secretCode}&Issuer=${this.appName}`;this.$q.dialog({component: QrCodeDialog,componentProps: {value: codeUrl,},}).onOk(()=>{// Verify codethis.$q.dialog({component: MFADialog,}).onOk((code: string)=>{cognitoUser.verifySoftwareToken(code,'TOTP Device',{onSuccess: (userSession: CognitoUserSession)=>{voidthis.loginSuccess(userSession);resolve();},onFailure: (error: Error)=>{this.onFailure(error);},});});});});}/** * Confirm e-mail verification code * * @param code -verification code */asyncverifyEmail(code: string): Promise<void>{returnnewPromise((resolve,reject)=>{this.$authStore.cognitoUser?.confirmRegistration(code,true,(err: Error)=>{if(err){console.error(err);reject();}resolve();});});}/** * Verifies a given 2FA code * * @param tokenType - the type of token to verify * @param resolve - resolve function */verify2FACode(tokenType: string,resolve: (value: void|PromiseLike<void>)=>void): void{// Verify codethis.$q.dialog({component: MFADialog,}).onOk((code: string)=>{// Deep copy user so state object does not get alteredconstcurrentUser: CognitoUser|undefined=cloneDeep(this.$authStore.cognitoUser);currentUser?.sendMFACode(code,{onSuccess: (userSession: CognitoUserSession)=>{this.$authStore.setCognitoUser(currentUser);voidthis.loginSuccess(userSession);resolve();},onFailure: (error: Error)=>{this.onFailure(error);},},tokenType);});}/** * When login succeeds * * @param userSession - the currently active Cognito authentication session */asyncloginSuccess(userSession: CognitoUserSession): Promise<void>{// Store locallythis.$authStore.setUserSession(userSession);// Redirect to main page TODO application specific: choose correct routeawaitthis.$routerService?.routeTo(ROUTES.HOME);}/** * When any operation (mostly login) fails, verify whether it is due to the authentication not having verified their account * * @param error - the error that caused the failure * @param identifier - the authentication's identifier (usually E-mail or username) for re-login * @param password - the authentication's password for re-login */onFailure(error: Error,identifier?: string,password?: string): void{letmessage;switch(error.name){// Case 1: User has not verified their e-mail yetcase'UserNotConfirmedException':
voidthis.showEmailVerificationDialog().then(()=>{if(identifier&&password){// Retry loginvoidthis.login(identifier,password,this.$q);}});break;// Case 2: User must reset password (e.g. if forced via AWS console)case'PasswordResetRequiredException':
// Call forgotPassword on cognitoUser, since user must reset passwordthis.$authStore.cognitoUser?.forgotPassword({onSuccess(){const$q=useQuasar();// Show success notificationshowSuccessNotification($q,i18n.global.t('messages.password_changed'));},onFailure: (err: Error)=>{this.$authStore.setCognitoUser(undefined);this.onFailure(err);},inputVerificationCode: ()=>{this.showResetPasswordFormDialog();},});break;// Case 3: Wrong password or user's Cognito account is disabledcase'NotAuthorizedException':
if(error.message.includes('Incorrect username or password')){// Wrong username / passwordmessage=i18n.global.t('errors.incorrect_username_or_password');}elseif(error.message.includes('User is disabled')){// Account disabledmessage=i18n.global.t('errors.account_disabled');}elseif(error.message.includes('User password cannot be reset in the current state')){// Password can't be resetmessage=i18n.global.t('errors.cannot_reset_password');}// Other NotAuthorizedExceptions are handled directlythis.$errorService.showErrorDialog(message ? newError(message) : error);break;// Default: any other errordefault:
this.$errorService.showErrorDialog(error);break;}}/** * Refreshes the idToken if necessary * * @returns void */refreshToken(): Promise<void>{returnnewPromise((resolve,reject)=>{const{ userSession }=this.$authStore;constidTokenExpiration=userSession?.getIdToken().getExpiration();if(!idTokenExpiration){resolve();}constrefreshToken=userSession?.getRefreshToken();if(refreshToken&&idTokenExpiration&&idTokenExpiration-Date.now()/1000<45*60){// 15min before de-validation token is refreshedconstcurrentUser: CognitoUser|undefined=cloneDeep(this.$authStore.cognitoUser);// refresh session mutates the state of store: illegalcurrentUser?.refreshSession(refreshToken,(err,session)=>{if(session){this.$authStore.setCognitoUser(currentUser);this.$authStore.setUserSession(sessionasCognitoUserSession|undefined);resolve();}else{reject(err);}});}else{resolve();}});}}
bd74d00ad8a0712dbb2e46e8eaac9c26946e6045
The text was updated successfully, but these errors were encountered:
application specific: choose correct route
flox/frontend/src/flox/modules/auth/services/auth.service.ts
Line 637 in 4d186fe
bd74d00ad8a0712dbb2e46e8eaac9c26946e6045
The text was updated successfully, but these errors were encountered: