Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/reset-password/reset-password-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class PasswordResetTokenResolver
resolve(route: ActivatedRouteSnapshot): Observable<ResetPasswordEmailForm> {
const key = route.params['key']
return this._accountRecoveryService.resetPasswordEmailValidateToken({
encryptedEmail: key,
token: key,
})
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
mode="indeterminate"
></mat-progress-bar>
} @if (showForm) { @if (!expiredPasswordResetToken &&
!invalidPasswordResetToken) {
!invalidPasswordResetToken && !alreadyUsedPasswordResetToken) {
<mat-card-header class="p-0">
<mat-card-title role="heading" aria-level="1">
<div>
Expand Down Expand Up @@ -83,6 +83,10 @@ <h1 class="orc-font-heading-small font-normal mb-4 mt-0">
<ng-container i18n="@@reset.hasExpired"
>Your password reset link has expired</ng-container
>
} @else if (alreadyUsedPasswordResetToken) {
<ng-container i18n="@@reset.hasAlreadyBeenUsed"
>This password reset link has already been used</ng-container
>
} @else {
<ng-container i18n="@@reset.invalidLink"
>There is a problem with your password reset link</ng-container
Expand All @@ -92,7 +96,7 @@ <h1 class="orc-font-heading-small font-normal mb-4 mt-0">
</mat-card-title>
<mat-card-subtitle role="heading" aria-level="2">
<h2 class="orc-font-body-small font-normal! mt-0">
@if (expiredPasswordResetToken) {
@if (expiredPasswordResetToken || alreadyUsedPasswordResetToken) {
<ng-container i18n="@@reset.youCanRequestANewLink"
>You can request a new link from</ng-container
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ import { Overlay } from '@angular/cdk/overlay'
import { RegisterService } from '../../core/register/register.service'
import { PasswordRecoveryService } from '../../core/password-recovery/password-recovery.service'
import { MdePopoverModule } from '../../cdk/popover'
import { ActivatedRoute } from '@angular/router'
import { of } from 'rxjs'

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'

describe('ResetPasswordComponent', () => {
let component: ResetPasswordComponent
let fixture: ComponentFixture<ResetPasswordComponent>

beforeEach(async () => {
async function setupWithTokenErrors(errors: string[]) {
await TestBed.configureTestingModule({
imports: [HttpClientTestingModule, MdePopoverModule, RouterTestingModule],
declarations: [ResetPasswordComponent],
Expand All @@ -36,18 +38,53 @@ describe('ResetPasswordComponent', () => {
MatSnackBar,
MatDialog,
Overlay,
{
provide: ActivatedRoute,
useValue: {
data: of({ tokenVerification: { errors } }),
queryParams: of({}),
snapshot: { params: { key: 'a-token' } },
},
},
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents()
})

beforeEach(() => {
fixture = TestBed.createComponent(ResetPasswordComponent)
component = fixture.componentInstance
fixture.detectChanges()
}

beforeEach(() => {
TestBed.resetTestingModule()
})

xit('should create', () => {
it('should create', async () => {
await setupWithTokenErrors([])
expect(component).toBeTruthy()
})

it('shows the reset form when the token is valid', async () => {
await setupWithTokenErrors([])
expect(component.alreadyUsedPasswordResetToken).toBeFalsy()
expect(fixture.nativeElement.textContent).toContain('Reset your password')
})

it('shows the already used panel when the token has been used', async () => {
await setupWithTokenErrors(['alreadyUsedPasswordResetToken'])
expect(component.alreadyUsedPasswordResetToken).toBeTrue()
expect(fixture.nativeElement.textContent).toContain(
'This password reset link has already been used'
)
expect(fixture.nativeElement.textContent).toContain(
'Password and iD recovery'
)
})

it('shows the expired panel when the token has expired', async () => {
await setupWithTokenErrors(['expiredPasswordResetToken'])
expect(fixture.nativeElement.textContent).toContain(
'Your password reset link has expired'
)
})
})
21 changes: 18 additions & 3 deletions src/app/reset-password/reset-password/reset-password.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
loading = false
errors: string[]
$destroy = new Subject<void>()
emailKey: string
token: string
expiredPasswordResetToken: boolean
invalidPasswordResetToken: boolean
alreadyUsedPasswordResetToken: boolean
isMobile: boolean
isOauthAuthorizationTogglzEnable: boolean
showForm = true
Expand Down Expand Up @@ -83,7 +84,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
this.isMobile = platform.columns4 || platform.columns8
})

this.emailKey = this._route.snapshot.params.key
this.token = this._route.snapshot.params.key

this._route.data.subscribe((data) => {
if (
Expand All @@ -100,6 +101,13 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
) {
this.expiredPasswordResetToken = true
}
if (
data.tokenVerification.errors.find(
(x: string) => x === 'alreadyUsedPasswordResetToken'
)
) {
this.alreadyUsedPasswordResetToken = true
}
})

this.form = this._fb.group({
Expand All @@ -116,7 +124,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
return {
newPassword: this.form.value.passwordGroup.password,
retypedPassword: this.form.value.passwordGroup.passwordConfirm,
encryptedEmail: this.emailKey,
token: this.token,
successRedirectLocation: null,
twoFactorCode: this.form.value.twoFactorCode,
twoFactorRecoveryCode: this.form.value.twoFactorRecoveryCode,
Expand Down Expand Up @@ -204,6 +212,13 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
) {
this.expiredPasswordResetToken = true
}
if (
value.errors?.find(
(x: string) => x === 'alreadyUsedPasswordResetToken'
)
) {
this.alreadyUsedPasswordResetToken = true
}
if (
value.newPassword?.errors?.find(
(x: string) =>
Expand Down
4 changes: 2 additions & 2 deletions src/app/types/reset-password.endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AuthChallenge, Value } from './common.endpoint'
export interface ResetPasswordEmailForm extends AuthChallenge {
newPassword: Value
retypedPassword: Value
encryptedEmail: string
token: string
successRedirectLocation?: string
twoFactorEnabled?: boolean
twoFactorCode?: string
Expand All @@ -12,6 +12,6 @@ export interface ResetPasswordEmailForm extends AuthChallenge {
errors?: any[]
}
export interface ResetPasswordEmailFormValidate {
encryptedEmail: string
token: string
errors?: any[]
}
Loading