@c8y/login 1022.45.2 → 1023.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@c8y/login",
3
- "version": "1022.45.2",
3
+ "version": "1023.0.0",
4
4
  "description": "This package is used to scaffold a login application for Cumulocity IoT.",
5
5
  "dependencies": {
6
- "@c8y/style": "1022.45.2",
7
- "@c8y/ngx-components": "1022.45.2",
8
- "@c8y/client": "1022.45.2",
9
- "@c8y/bootstrap": "1022.45.2",
10
- "@angular/cdk": "^19.2.19",
6
+ "@c8y/style": "1023.0.0",
7
+ "@c8y/ngx-components": "1023.0.0",
8
+ "@c8y/client": "1023.0.0",
9
+ "@c8y/bootstrap": "1023.0.0",
10
+ "@angular/cdk": "^20.2.7",
11
11
  "monaco-editor": "~0.53.0",
12
- "ngx-bootstrap": "19.0.2",
13
- "rxjs": "7.8.1"
12
+ "ngx-bootstrap": "20.0.2",
13
+ "rxjs": "7.8.2"
14
14
  },
15
15
  "devDependencies": {
16
- "@c8y/options": "1022.45.2",
17
- "@c8y/devkit": "1022.45.2"
16
+ "@c8y/options": "1023.0.0",
17
+ "@c8y/devkit": "1023.0.0"
18
18
  },
19
19
  "peerDependencies": {
20
- "@angular/common": ">=19 <20"
20
+ "@angular/common": ">=20 <21"
21
21
  },
22
22
  "author": "Cumulocity GmbH",
23
23
  "license": "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  import { Component } from '@angular/core';
2
- import { CookieBannerComponent, TranslationLoaderService } from '@c8y/ngx-components';
3
- import { LoginComponent } from '../login';
2
+ import { CookieBannerComponent } from '@c8y/ngx-components';
3
+ import { LoginComponent } from '../login/login.component';
4
4
 
5
5
  @Component({
6
6
  selector: 'c8y-bootstrap',
@@ -8,9 +8,4 @@ import { LoginComponent } from '../login';
8
8
  standalone: true,
9
9
  imports: [LoginComponent, CookieBannerComponent]
10
10
  })
11
- export class BootstrapLoginComponent {
12
- constructor(
13
- // only here to ensure the service is instantiated
14
- public translationLoaderService: TranslationLoaderService
15
- ) {}
16
- }
11
+ export class BootstrapLoginComponent {}
@@ -1,128 +1,127 @@
1
1
  <div class="login-panel {{ platformAnimationSrc ? 'isc8y' : '' }}">
2
- <div
3
- class="square animated fadeIn"
4
- *ngIf="platformAnimationSrc"
5
- >
6
- <img [src]="platformAnimationSrc" />
7
- </div>
2
+ @if (platformAnimationSrc) {
3
+ <div class="square animated fadeIn">
4
+ <img [src]="platformAnimationSrc" />
5
+ </div>
6
+ }
8
7
 
9
- <div
10
- class="login-form animated fadeIn"
11
- *ngIf="currentView !== LOGIN_VIEWS.None"
12
- [ngSwitch]="currentView"
13
- >
14
- <main class="card-block p-b-0 form-group-lg">
15
- <span class="mainlogo {{ !isBrandLogoSet ? 'c8y-logo' : '' }}"></span>
16
- <ng-container *ngSwitchCase="LOGIN_VIEWS.Credentials">
17
- <span class="{{ platformAnimationSrc ? '' : 'text-center' }}">
18
- <h2
19
- class="m-b-8"
20
- translate
21
- >
22
- Welcome
23
- </h2>
24
- <p
25
- class="text-16 m-b-40"
26
- translate
27
- >
28
- Log in to access your IoT platform.
29
- </p>
30
- </span>
31
- </ng-container>
32
- <ng-container *ngSwitchCase="LOGIN_VIEWS.RecoverPassword">
33
- <span class="{{ platformAnimationSrc ? '' : 'text-center' }}">
34
- <h2
35
- class="m-b-8"
36
- translate
37
- >
38
- Reset password
39
- </h2>
40
- <p
41
- class="text-16 m-b-40"
42
- translate
43
- >
44
- Enter your email address and we'll send you a secure link to reset your password.
45
- </p>
46
- </span>
47
- </ng-container>
48
- <c8y-alert-outlet
49
- class="m-b-24 d-block"
50
- position="static"
51
- ></c8y-alert-outlet>
52
-
53
- <c8y-credentials
54
- *ngSwitchCase="LOGIN_VIEWS.Credentials"
55
- (onChangeView)="handleLoginTemplate($event)"
56
- [loginViewParams]="loginViewParams"
57
- ></c8y-credentials>
58
- <c8y-recover-password
59
- *ngSwitchCase="LOGIN_VIEWS.RecoverPassword"
60
- (onChangeView)="handleLoginTemplate($event)"
61
- ></c8y-recover-password>
62
- <c8y-change-password
63
- *ngSwitchCase="LOGIN_VIEWS.ChangePassword"
64
- (onChangeView)="handleLoginTemplate($event)"
65
- [credentials]="credentials"
66
- ></c8y-change-password>
67
- <c8y-totp-auth
68
- *ngSwitchCase="LOGIN_VIEWS.TotpChallenge"
69
- (onCancel)="reset(false)"
70
- [view]="currentView"
71
- [credentials]="credentials"
72
- ></c8y-totp-auth>
73
- <c8y-totp-auth
74
- *ngSwitchCase="LOGIN_VIEWS.TotpSetup"
75
- (onCancel)="reset(false)"
76
- [view]="currentView"
77
- [credentials]="credentials"
78
- ></c8y-totp-auth>
79
- <c8y-sms-challenge
80
- *ngSwitchCase="LOGIN_VIEWS.SmsChallenge"
81
- (onCancel)="reset(false)"
82
- [credentials]="credentials"
83
- ></c8y-sms-challenge>
84
-
85
- <c8y-provide-phone-number
86
- *ngSwitchCase="LOGIN_VIEWS.ProvidePhoneNumber"
87
- (onCancel)="reset(false)"
88
- (onChangeView)="handleLoginTemplate($event)"
89
- [credentials]="credentials"
90
- ></c8y-provide-phone-number>
91
- <c8y-tenant-id-setup
92
- *ngSwitchCase="LOGIN_VIEWS.TenantIdSetup"
93
- (onChangeView)="handleLoginTemplate($event)"
94
- ></c8y-tenant-id-setup>
95
-
96
- <c8y-missing-application-access
97
- *ngSwitchCase="LOGIN_VIEWS.MissingApplicationAccess"
98
- ></c8y-missing-application-access>
99
-
100
- <div
101
- class="text-center m-t-8"
102
- *ngIf="!!(ui.state$ | async).loginExtraLink"
103
- >
104
- <div *ngIf="!!(ui.state$ | async).loginExtraLink.length; else singleExtraLink">
105
- <a
106
- class="small d-block m-t-8"
107
- title="{{ link.label }}"
108
- role="button"
109
- *ngFor="let link of (ui.state$ | async).loginExtraLink"
110
- [href]="link.url"
111
- >
112
- {{ link.label }}
113
- </a>
114
- </div>
115
- <ng-template #singleExtraLink>
116
- <a
117
- class="small"
118
- title="{{ (ui.state$ | async).loginExtraLink.label }}"
119
- role="button"
120
- [href]="(ui.state$ | async).loginExtraLink.url"
121
- >
122
- {{ (ui.state$ | async).loginExtraLink.label }}
123
- </a>
124
- </ng-template>
125
- </div>
126
- </main>
127
- </div>
8
+ @if (currentView !== LOGIN_VIEWS.None) {
9
+ <div class="login-form animated fadeIn">
10
+ <main class="card-block p-b-0 form-group-lg">
11
+ <span class="mainlogo {{ !isBrandLogoSet ? 'c8y-logo' : '' }}"></span>
12
+ <c8y-alert-outlet
13
+ class="m-b-24 d-block"
14
+ position="static"
15
+ ></c8y-alert-outlet>
16
+ @switch (currentView) {
17
+ @case (LOGIN_VIEWS.Credentials) {
18
+ <span class="{{ platformAnimationSrc ? '' : 'text-center' }}">
19
+ <h2
20
+ class="m-b-8"
21
+ translate
22
+ >
23
+ Welcome
24
+ </h2>
25
+ <p
26
+ class="text-16 m-b-40"
27
+ translate
28
+ >
29
+ Log in to access your IoT platform.
30
+ </p>
31
+ </span>
32
+ <c8y-credentials
33
+ (onChangeView)="handleLoginTemplate($event)"
34
+ [loginViewParams]="loginViewParams"
35
+ ></c8y-credentials>
36
+ }
37
+ @case (LOGIN_VIEWS.RecoverPassword) {
38
+ <span class="{{ platformAnimationSrc ? '' : 'text-center' }}">
39
+ <h2
40
+ class="m-b-8"
41
+ translate
42
+ >
43
+ Reset password
44
+ </h2>
45
+ <p
46
+ class="text-16 m-b-40"
47
+ translate
48
+ >
49
+ Enter your email address and we'll send you a secure link to reset your password.
50
+ </p>
51
+ </span>
52
+ <c8y-recover-password
53
+ [recoverPasswordData]="recoverPasswordData"
54
+ (onChangeView)="handleLoginTemplate($event)"
55
+ ></c8y-recover-password>
56
+ }
57
+ @case (LOGIN_VIEWS.ChangePassword) {
58
+ <c8y-change-password
59
+ (onChangeView)="handleLoginTemplate($event)"
60
+ [credentials]="credentials"
61
+ ></c8y-change-password>
62
+ }
63
+ @case (LOGIN_VIEWS.TotpChallenge) {
64
+ <c8y-totp-auth
65
+ (onCancel)="reset(false)"
66
+ [view]="currentView"
67
+ [credentials]="credentials"
68
+ ></c8y-totp-auth>
69
+ }
70
+ @case (LOGIN_VIEWS.TotpSetup) {
71
+ <c8y-totp-auth
72
+ (onCancel)="reset(false)"
73
+ [view]="currentView"
74
+ [credentials]="credentials"
75
+ ></c8y-totp-auth>
76
+ }
77
+ @case (LOGIN_VIEWS.SmsChallenge) {
78
+ <c8y-sms-challenge
79
+ (onCancel)="reset(false)"
80
+ [credentials]="credentials"
81
+ ></c8y-sms-challenge>
82
+ }
83
+ @case (LOGIN_VIEWS.ProvidePhoneNumber) {
84
+ <c8y-provide-phone-number
85
+ (onCancel)="reset(false)"
86
+ (onChangeView)="handleLoginTemplate($event)"
87
+ [credentials]="credentials"
88
+ ></c8y-provide-phone-number>
89
+ }
90
+ @case (LOGIN_VIEWS.TenantIdSetup) {
91
+ <c8y-tenant-id-setup (onChangeView)="handleLoginTemplate($event)"></c8y-tenant-id-setup>
92
+ }
93
+ @case (LOGIN_VIEWS.MissingApplicationAccess) {
94
+ <c8y-missing-application-access></c8y-missing-application-access>
95
+ }
96
+ }
97
+ @if (!!(ui.state$ | async).loginExtraLink) {
98
+ <div class="text-center m-t-8">
99
+ @if (!!(ui.state$ | async).loginExtraLink.length) {
100
+ <div>
101
+ @for (link of (ui.state$ | async).loginExtraLink; track link) {
102
+ <a
103
+ class="small d-block m-t-8"
104
+ title="{{ link.label }}"
105
+ role="button"
106
+ [href]="link.url"
107
+ >
108
+ {{ link.label }}
109
+ </a>
110
+ }
111
+ </div>
112
+ } @else {
113
+ <a
114
+ class="small"
115
+ title="{{ (ui.state$ | async).loginExtraLink.label }}"
116
+ role="button"
117
+ [href]="(ui.state$ | async).loginExtraLink.url"
118
+ >
119
+ {{ (ui.state$ | async).loginExtraLink.label }}
120
+ </a>
121
+ }
122
+ </div>
123
+ }
124
+ </main>
125
+ </div>
126
+ }
128
127
  </div>
@@ -20,7 +20,7 @@ import { gettext } from '@c8y/ngx-components/gettext';
20
20
  import { LoginEvent, LoginViews, SsoData, SsoError } from './login.model';
21
21
  import { CredentialsFromQueryParamsService } from './credentials-from-query-params.service';
22
22
  import { CredentialsComponentParams } from './credentials-component-params';
23
- import { NgIf, NgSwitch, NgSwitchCase, NgFor, AsyncPipe } from '@angular/common';
23
+ import { AsyncPipe } from '@angular/common';
24
24
  import { CredentialsComponent } from './credentials/credentials.component';
25
25
  import { RecoverPasswordComponent } from './recover-password/recover-password.component';
26
26
  import { ChangePasswordComponent } from './change-password/change-password.component';
@@ -37,9 +37,6 @@ import { MissingApplicationAccessComponent } from './missing-application-access/
37
37
  encapsulation: ViewEncapsulation.None,
38
38
  standalone: true,
39
39
  imports: [
40
- NgIf,
41
- NgSwitch,
42
- NgSwitchCase,
43
40
  CredentialsComponent,
44
41
  RecoverPasswordComponent,
45
42
  ChangePasswordComponent,
@@ -47,7 +44,6 @@ import { MissingApplicationAccessComponent } from './missing-application-access/
47
44
  SmsChallengeComponent,
48
45
  ProvidePhoneNumberComponent,
49
46
  TenantIdSetupComponent,
50
- NgFor,
51
47
  AlertOutletComponent,
52
48
  AsyncPipe,
53
49
  MissingApplicationAccessComponent,
@@ -67,6 +63,7 @@ export class LoginComponent implements OnInit, OnDestroy {
67
63
 
68
64
  credentials: ICredentials = {};
69
65
  loginViewParams: CredentialsComponentParams | { [key: string]: any } = {};
66
+ recoverPasswordData: LoginEvent['recoverPasswordData'];
70
67
  displayAlerts = false;
71
68
  private TOKEN_PARAM = 'token';
72
69
 
@@ -84,17 +81,17 @@ export class LoginComponent implements OnInit, OnDestroy {
84
81
  this.platformAnimationSrc = this.getPlatformAnimationPath();
85
82
  }
86
83
 
87
- ngOnInit() {
84
+ async ngOnInit() {
88
85
  const token = this.getParamAndClear(this.TOKEN_PARAM);
89
86
  const ssoData = this.getSsoData();
90
87
  if (ssoData) {
91
- this.handleSso(ssoData);
88
+ await this.handleSso(ssoData);
92
89
  } else if (this.loginService.isFirstLogin) {
93
90
  if (!token) {
94
91
  this.loginAutomatically();
95
92
  } else {
96
93
  this.credentials.token = token;
97
- this.reset(false);
94
+ await this.reset(false);
98
95
  }
99
96
  }
100
97
  this.loginService.isFirstLogin = false;
@@ -109,6 +106,7 @@ export class LoginComponent implements OnInit, OnDestroy {
109
106
  this.currentView = event.view;
110
107
  this.credentials = event.credentials || {};
111
108
  this.loginViewParams = event.loginViewParams || {};
109
+ this.recoverPasswordData = event.recoverPasswordData || null;
112
110
  }
113
111
 
114
112
  @HostListener('keyup', ['$event']) onkeyup(event: KeyboardEvent) {
@@ -117,13 +115,13 @@ export class LoginComponent implements OnInit, OnDestroy {
117
115
  }
118
116
  }
119
117
 
120
- reset(missingPermissions: boolean) {
118
+ async reset(missingPermissions: boolean) {
121
119
  if (missingPermissions) {
122
120
  this.handleLoginTemplate({ view: LoginViews.MissingApplicationAccess });
123
121
  return;
124
122
  }
125
123
  this.loginService.reset();
126
- this.setView();
124
+ await this.setView();
127
125
  this.loginService.cleanMessages();
128
126
  }
129
127
 
@@ -163,14 +161,14 @@ export class LoginComponent implements OnInit, OnDestroy {
163
161
  if (result) {
164
162
  return;
165
163
  }
166
- this.reset(true);
164
+ await this.reset(true);
167
165
  } catch (e) {
168
166
  await this.loginService.clearCookies();
169
167
  const preferredLoginOptionType = this.loginService.loginMode.type;
170
168
  if (preferredLoginOptionType === TenantLoginOptionType.OAUTH2 && e.res?.status !== 403) {
171
169
  this.loginService.redirectToOauth();
172
170
  } else {
173
- this.reset(false);
171
+ await this.reset(false);
174
172
  if (
175
173
  preferredLoginOptionType === TenantLoginOptionType.OAUTH2_INTERNAL &&
176
174
  window.location.protocol !== 'https:'
@@ -184,9 +182,25 @@ export class LoginComponent implements OnInit, OnDestroy {
184
182
  this.loginService.automaticLoginInProgress$.next(false);
185
183
  }
186
184
 
187
- private setView() {
185
+ private async setView() {
188
186
  if (this.credentials && this.credentials.token) {
189
- this.handleLoginTemplate({ view: LoginViews.ChangePassword, credentials: this.credentials });
187
+ const email = this.options.get('email', '');
188
+ const tokenStatus = await this.loginService.validateResetToken(this.credentials.token, email);
189
+ if (tokenStatus === 'valid') {
190
+ this.handleLoginTemplate({
191
+ view: LoginViews.ChangePassword,
192
+ credentials: this.credentials
193
+ });
194
+ } else {
195
+ this.handleLoginTemplate({
196
+ view: LoginViews.RecoverPassword,
197
+ recoverPasswordData: {
198
+ email,
199
+ tokenStatus,
200
+ tenantId: this.loginService.getTenant()
201
+ }
202
+ });
203
+ }
190
204
  } else if (this.loginService.showTenantSetup()) {
191
205
  this.handleLoginTemplate({ view: LoginViews.TenantIdSetup });
192
206
  } else {
@@ -217,12 +231,12 @@ export class LoginComponent implements OnInit, OnDestroy {
217
231
  return false;
218
232
  }
219
233
 
220
- private handleSso(ssoData: SsoData | SsoError) {
234
+ private async handleSso(ssoData: SsoData | SsoError) {
221
235
  if ('ssoError' in ssoData) {
222
236
  this.loginService.showSsoError(
223
237
  decodeURIComponent(ssoData.ssoErrorDescription).replace(/\+/g, '%20')
224
238
  );
225
- this.reset(false);
239
+ await this.reset(false);
226
240
  } else {
227
241
  this.loginService
228
242
  .loginBySso(ssoData)
@@ -10,6 +10,11 @@ export interface LoginEvent {
10
10
  view: LoginViews;
11
11
  credentials?: ICredentials;
12
12
  loginViewParams?: CredentialsComponentParams | { [key: string]: any };
13
+ recoverPasswordData?: {
14
+ email: string;
15
+ tokenStatus: 'valid' | 'invalid' | 'expired';
16
+ tenantId?: string;
17
+ };
13
18
  }
14
19
 
15
20
  export enum LoginViews {
@@ -21,7 +21,7 @@ import { switchMap } from 'rxjs/operators';
21
21
  import { BehaviorSubject, EMPTY } from 'rxjs';
22
22
  import { isEmpty } from 'lodash-es';
23
23
  import { TranslateService } from '@ngx-translate/core';
24
- import { SsoData } from './login.model';
24
+ import { LoginEvent, SsoData } from './login.model';
25
25
  import {
26
26
  getStoredToken,
27
27
  getStoredTfaToken,
@@ -546,6 +546,28 @@ export class LoginService extends SimplifiedAuthService {
546
546
  return await this.cookieAuth.logout({ redirect: 'manual' });
547
547
  }
548
548
 
549
+ /**
550
+ * Validates the reset password token.
551
+ * @param token The reset password token to validate.
552
+ * @param email The email address associated with the token.
553
+ * @returns Returns 'valid', 'invalid', or 'expired' based on the token status.
554
+ */
555
+ async validateResetToken(
556
+ token: string,
557
+ email: string
558
+ ): Promise<LoginEvent['recoverPasswordData']['tokenStatus']> {
559
+ try {
560
+ await this.user.validateResetToken(token, email);
561
+ return 'valid';
562
+ } catch (e) {
563
+ if (e.res?.status === 422) {
564
+ return 'expired';
565
+ } else {
566
+ return 'invalid';
567
+ }
568
+ }
569
+ }
570
+
549
571
  /**
550
572
  * Sets the tenant to the client and updates the credentials on the
551
573
  * auth strategy.
@@ -1,49 +1,59 @@
1
- <form #resetForm="ngForm" class="loginForm" (ngSubmit)="resetPassword()" novalidate>
2
- <c8y-form-group class="tenantField" id="tenantField" *ngIf="loginService.showTenant()">
3
- <label translate>Tenant ID</label>
4
- <input
5
- [(ngModel)]="model.tenantId"
6
- #tenantId="ngModel"
7
- type="text"
8
- name="tenantId"
9
- autocapitalize="off"
10
- autocorrect="off"
11
- class="form-control"
12
- placeholder="{{ 'Tenant ID' | translate }}"
13
- required
14
- />
15
- </c8y-form-group>
1
+ <form
2
+ class="loginForm"
3
+ #resetForm="ngForm"
4
+ (ngSubmit)="resetPassword()"
5
+ novalidate
6
+ >
7
+ @if (loginService.showTenant()) {
8
+ <c8y-form-group
9
+ class="tenantField"
10
+ id="tenantField"
11
+ >
12
+ <label translate>Tenant ID</label>
13
+ <input
14
+ class="form-control"
15
+ placeholder="{{ 'Tenant ID' | translate }}"
16
+ name="tenantId"
17
+ type="text"
18
+ required
19
+ [(ngModel)]="model.tenantId"
20
+ #tenantId="ngModel"
21
+ autocapitalize="off"
22
+ autocorrect="off"
23
+ />
24
+ </c8y-form-group>
25
+ }
16
26
 
17
27
  <c8y-form-group>
18
28
  <label translate>Email address</label>
19
29
  <input
30
+ class="form-control"
31
+ placeholder="{{ 'Email address' | translate }}"
32
+ name="email"
33
+ type="text"
34
+ required
20
35
  [(ngModel)]="model.email"
21
36
  #email="ngModel"
22
- type="text"
23
- name="email"
24
37
  autocapitalize="off"
25
38
  autocorrect="off"
26
- class="form-control"
27
- placeholder="{{ 'Email address' | translate }}"
28
39
  email
29
- required
30
40
  />
31
41
  </c8y-form-group>
32
42
 
33
43
  <div class="m-t-32">
34
44
  <button
45
+ class="btn btn-primary btn-lg btn-block form-group"
35
46
  title="{{ 'Reset password' | translate }}"
36
- [disabled]="!resetForm.form.valid || isLoading"
37
47
  type="submit"
38
- class="btn btn-primary btn-lg btn-block form-group"
48
+ [disabled]="!resetForm.form.valid || isLoading"
39
49
  >
40
50
  {{ 'Reset password' | translate }}
41
51
  </button>
42
52
  <div class="text-center m-t-8">
43
53
  <button
44
- type="submit"
45
- title="{{ 'Login' | translate }}"
46
54
  class="btn btn-link btn-sm"
55
+ title="{{ 'Login' | translate }}"
56
+ type="submit"
47
57
  (click)="onChangeView.emit({ view: LOGIN_VIEWS.Credentials })"
48
58
  >
49
59
  {{ 'Login' | translate }}
@@ -1,4 +1,4 @@
1
- import { Component, OnInit, Output, EventEmitter } from '@angular/core';
1
+ import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core';
2
2
  import { UserService } from '@c8y/client';
3
3
  import { LoginService } from '../login.service';
4
4
  import { LoginEvent, LoginViews } from '../login.model';
@@ -7,10 +7,10 @@ import {
7
7
  C8yTranslateDirective,
8
8
  FormGroupComponent,
9
9
  RequiredInputPlaceholderDirective,
10
- C8yTranslatePipe
10
+ C8yTranslatePipe,
11
+ AlertService
11
12
  } from '@c8y/ngx-components';
12
-
13
- import { NgIf } from '@angular/common';
13
+ import { gettext } from '@c8y/ngx-components/gettext';
14
14
 
15
15
  @Component({
16
16
  selector: 'c8y-recover-password',
@@ -20,13 +20,13 @@ import { NgIf } from '@angular/common';
20
20
  imports: [
21
21
  FormsModule,
22
22
  C8yTranslateDirective,
23
- NgIf,
24
23
  FormGroupComponent,
25
24
  RequiredInputPlaceholderDirective,
26
25
  C8yTranslatePipe
27
26
  ]
28
27
  })
29
28
  export class RecoverPasswordComponent implements OnInit {
29
+ @Input() recoverPasswordData: LoginEvent['recoverPasswordData'];
30
30
  @Output() onChangeView = new EventEmitter<LoginEvent>();
31
31
  LOGIN_VIEWS = LoginViews;
32
32
  isLoading = false;
@@ -37,11 +37,27 @@ export class RecoverPasswordComponent implements OnInit {
37
37
 
38
38
  constructor(
39
39
  private users: UserService,
40
- public loginService: LoginService
40
+ public loginService: LoginService,
41
+ private alertService: AlertService
41
42
  ) {}
42
43
 
43
44
  ngOnInit() {
44
- this.model.tenantId = this.loginService.getTenant();
45
+ if (this.recoverPasswordData) {
46
+ this.model.email = this.recoverPasswordData.email || '';
47
+ this.model.tenantId = this.recoverPasswordData.tenantId || '';
48
+
49
+ const message =
50
+ this.recoverPasswordData.tokenStatus === 'expired'
51
+ ? gettext('This password reset link expired. To continue, request a new one.')
52
+ : this.recoverPasswordData.tokenStatus === 'invalid'
53
+ ? gettext('This password reset link is invalid. To continue, request a new one.')
54
+ : '';
55
+ if (message) {
56
+ this.alertService.danger(message);
57
+ }
58
+ } else {
59
+ this.model.tenantId = this.loginService.getTenant();
60
+ }
45
61
  }
46
62
 
47
63
  async resetPassword() {