@c8y/login 1022.3.2
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/.browserslistrc +16 -0
- package/cumulocity.config.ts +27 -0
- package/jest.config.js +18 -0
- package/package.json +23 -0
- package/public/favicon.ico +0 -0
- package/public/platform-animation.svg +2533 -0
- package/src/app/app.config.ts +11 -0
- package/src/app/bootstrap-login/bootstrap-login.component.html +3 -0
- package/src/app/bootstrap-login/bootstrap-login.component.ts +16 -0
- package/src/app/login/change-password/change-password.component.html +97 -0
- package/src/app/login/change-password/change-password.component.ts +101 -0
- package/src/app/login/credentials/credentials.component.html +141 -0
- package/src/app/login/credentials/credentials.component.ts +148 -0
- package/src/app/login/credentials-component-params.ts +4 -0
- package/src/app/login/credentials-from-query-params.service.ts +86 -0
- package/src/app/login/index.ts +9 -0
- package/src/app/login/login.component.html +128 -0
- package/src/app/login/login.component.less +136 -0
- package/src/app/login/login.component.ts +238 -0
- package/src/app/login/login.model.ts +36 -0
- package/src/app/login/login.service.ts +651 -0
- package/src/app/login/missing-application-access/missing-application-access.component.html +2 -0
- package/src/app/login/missing-application-access/missing-application-access.component.ts +21 -0
- package/src/app/login/password-strength-validator.directive.ts +26 -0
- package/src/app/login/provide-phone-number/provide-phone-number.component.html +39 -0
- package/src/app/login/provide-phone-number/provide-phone-number.component.ts +73 -0
- package/src/app/login/recover-password/recover-password.component.html +53 -0
- package/src/app/login/recover-password/recover-password.component.ts +59 -0
- package/src/app/login/sms-challenge/sms-challenge.component.html +50 -0
- package/src/app/login/sms-challenge/sms-challenge.component.ts +134 -0
- package/src/app/login/strength-validator-service.ts +18 -0
- package/src/app/login/tenant-id-setup/tenant-id-setup.component.html +28 -0
- package/src/app/login/tenant-id-setup/tenant-id-setup.component.ts +94 -0
- package/src/app/login/totp-auth/totp-auth.component.html +18 -0
- package/src/app/login/totp-auth/totp-auth.component.ts +72 -0
- package/src/bootstrap.ts +19 -0
- package/src/i18n.ts +18 -0
- package/src/main.ts +25 -0
- package/src/polyfills.ts +33 -0
- package/tsconfig.app.json +20 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<div class="login-panel {{ platformAnimationSrc ? 'isc8y' : '' }}">
|
|
2
|
+
<div
|
|
3
|
+
class="square animated fadeIn"
|
|
4
|
+
*ngIf="platformAnimationSrc"
|
|
5
|
+
>
|
|
6
|
+
<img [src]="platformAnimationSrc" />
|
|
7
|
+
</div>
|
|
8
|
+
|
|
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>
|
|
128
|
+
</div>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
.login-panel {
|
|
2
|
+
display: grid;
|
|
3
|
+
min-height: 100vh;
|
|
4
|
+
&.isc8y{
|
|
5
|
+
@media( min-width: 768px) {
|
|
6
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
c8y-bootstrap, c8y-login, c8y-cookie-banner {
|
|
12
|
+
display: contents;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.square{
|
|
16
|
+
background-color: var(--c8y-palette-gray-10);
|
|
17
|
+
color: var(--c8y-palette-gray-90);
|
|
18
|
+
display: none;
|
|
19
|
+
img{
|
|
20
|
+
max-width: 100%;
|
|
21
|
+
}
|
|
22
|
+
.c8y-dark-theme & {
|
|
23
|
+
background-color: var(--c8y-palette-gray-100);
|
|
24
|
+
color: var(--c8y-palette-gray-20);
|
|
25
|
+
}
|
|
26
|
+
@media( min-width: 768px) {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
position: sticky;
|
|
30
|
+
top: 0;
|
|
31
|
+
max-height: 100vh;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.password-strength {
|
|
36
|
+
width: 180px;
|
|
37
|
+
margin-bottom: 20px;
|
|
38
|
+
|
|
39
|
+
.table & {
|
|
40
|
+
width: 100px;
|
|
41
|
+
margin-left: auto;
|
|
42
|
+
margin-right: auto;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.password-strength>div {
|
|
47
|
+
position: relative;
|
|
48
|
+
width: 100%;
|
|
49
|
+
height: 4px;
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
background-color: var(--c8y-palette-gray-90);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.password-strength>.password-strength-label {
|
|
55
|
+
float: left;
|
|
56
|
+
color: var(--c8y-component-form-label-color, var(--c8y-root-component-form-label-color));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.password-green .password-bar,
|
|
60
|
+
.password-yellow .password-bar,
|
|
61
|
+
.password-red .password-bar {
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: 0;
|
|
64
|
+
left: 0;
|
|
65
|
+
height: 100%;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.password-green {
|
|
69
|
+
.password-bar {
|
|
70
|
+
width: 100%;
|
|
71
|
+
background-color: var(--palette-status-success, var(--c8y-palette-status-success));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.password-yellow {
|
|
76
|
+
.password-bar {
|
|
77
|
+
width: 50%;
|
|
78
|
+
background-color: var(--palette-status-warning, var(--c8y-palette-status-warning));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.password-red {
|
|
83
|
+
.password-bar {
|
|
84
|
+
width: 25%;
|
|
85
|
+
background-color: var(--palette-status-danger, var(--c8y-palette-status-danger));;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.login-form {
|
|
90
|
+
height: auto;
|
|
91
|
+
padding: 32px;
|
|
92
|
+
width: 100%;
|
|
93
|
+
margin: auto;
|
|
94
|
+
@media (min-width: 768px) {
|
|
95
|
+
animation-delay: .5s;
|
|
96
|
+
max-width: 550px;
|
|
97
|
+
.isc8y &{
|
|
98
|
+
padding-left: 56px;
|
|
99
|
+
margin-left: 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.init-load {
|
|
105
|
+
height: 100vh;
|
|
106
|
+
margin: 0 auto;
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-direction: column;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
max-width: 320px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.mainlogo {
|
|
114
|
+
background-image: var(--brand-logo-img, var(--c8y-brand-logo-img));
|
|
115
|
+
width: 100%;
|
|
116
|
+
max-width: 350px;
|
|
117
|
+
padding-bottom: var(--brand-logo-height, var(--c8y-brand-logo-height));
|
|
118
|
+
background-position: top center;
|
|
119
|
+
background-repeat: no-repeat;
|
|
120
|
+
background-size: contain;
|
|
121
|
+
display: block;
|
|
122
|
+
margin: 0 auto 32px;
|
|
123
|
+
&.c8y-logo{
|
|
124
|
+
margin: 0 0 32px;
|
|
125
|
+
background-position: top left;
|
|
126
|
+
filter: brightness(.4);
|
|
127
|
+
.c8y-dark-theme &{
|
|
128
|
+
filter: grayscale(1) contrast(0.5) brightness(1.4);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.mainlogo[src] {
|
|
134
|
+
background: none;
|
|
135
|
+
padding-bottom: 0;
|
|
136
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
Input,
|
|
4
|
+
OnInit,
|
|
5
|
+
HostListener,
|
|
6
|
+
OnDestroy,
|
|
7
|
+
ViewEncapsulation
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
import { ICredentials, TenantLoginOptionType } from '@c8y/client';
|
|
10
|
+
import { LoginService } from './login.service';
|
|
11
|
+
import {
|
|
12
|
+
OptionsService,
|
|
13
|
+
AlertService,
|
|
14
|
+
AppStateService,
|
|
15
|
+
AlertOutletComponent,
|
|
16
|
+
C8yTranslateDirective,
|
|
17
|
+
C8yTranslatePipe
|
|
18
|
+
} from '@c8y/ngx-components';
|
|
19
|
+
import { gettext } from '@c8y/ngx-components/gettext';
|
|
20
|
+
import { LoginEvent, LoginViews, SsoData, SsoError } from './login.model';
|
|
21
|
+
import { CredentialsFromQueryParamsService } from './credentials-from-query-params.service';
|
|
22
|
+
import { CredentialsComponentParams } from './credentials-component-params';
|
|
23
|
+
import { NgIf, NgSwitch, NgSwitchCase, NgFor, AsyncPipe } from '@angular/common';
|
|
24
|
+
import { CredentialsComponent } from './credentials/credentials.component';
|
|
25
|
+
import { RecoverPasswordComponent } from './recover-password/recover-password.component';
|
|
26
|
+
import { ChangePasswordComponent } from './change-password/change-password.component';
|
|
27
|
+
import { TotpAuthComponent } from './totp-auth/totp-auth.component';
|
|
28
|
+
import { TenantIdSetupComponent } from './tenant-id-setup/tenant-id-setup.component';
|
|
29
|
+
import { ProvidePhoneNumberComponent } from './provide-phone-number/provide-phone-number.component';
|
|
30
|
+
import { SmsChallengeComponent } from './sms-challenge/sms-challenge.component';
|
|
31
|
+
import { MissingApplicationAccessComponent } from './missing-application-access/missing-application-access.component';
|
|
32
|
+
|
|
33
|
+
@Component({
|
|
34
|
+
selector: 'c8y-login',
|
|
35
|
+
templateUrl: './login.component.html',
|
|
36
|
+
styleUrls: ['./login.component.less'],
|
|
37
|
+
encapsulation: ViewEncapsulation.None,
|
|
38
|
+
standalone: true,
|
|
39
|
+
imports: [
|
|
40
|
+
NgIf,
|
|
41
|
+
NgSwitch,
|
|
42
|
+
NgSwitchCase,
|
|
43
|
+
CredentialsComponent,
|
|
44
|
+
RecoverPasswordComponent,
|
|
45
|
+
ChangePasswordComponent,
|
|
46
|
+
TotpAuthComponent,
|
|
47
|
+
SmsChallengeComponent,
|
|
48
|
+
ProvidePhoneNumberComponent,
|
|
49
|
+
TenantIdSetupComponent,
|
|
50
|
+
NgFor,
|
|
51
|
+
AlertOutletComponent,
|
|
52
|
+
AsyncPipe,
|
|
53
|
+
MissingApplicationAccessComponent,
|
|
54
|
+
C8yTranslateDirective,
|
|
55
|
+
C8yTranslatePipe
|
|
56
|
+
]
|
|
57
|
+
})
|
|
58
|
+
export class LoginComponent implements OnInit, OnDestroy {
|
|
59
|
+
currentView: LoginViews = LoginViews.None;
|
|
60
|
+
LOGIN_VIEWS = LoginViews;
|
|
61
|
+
platformAnimationSrc: string | false = false;
|
|
62
|
+
isBrandLogoSet = false;
|
|
63
|
+
|
|
64
|
+
disabled = false;
|
|
65
|
+
|
|
66
|
+
@Input() name: string;
|
|
67
|
+
|
|
68
|
+
credentials: ICredentials = {};
|
|
69
|
+
loginViewParams: CredentialsComponentParams | { [key: string]: any } = {};
|
|
70
|
+
displayAlerts = false;
|
|
71
|
+
private TOKEN_PARAM = 'token';
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Just DI.
|
|
75
|
+
*/
|
|
76
|
+
constructor(
|
|
77
|
+
public loginService: LoginService,
|
|
78
|
+
private options: OptionsService,
|
|
79
|
+
private alert: AlertService,
|
|
80
|
+
private credentialsFromQueryParamsService: CredentialsFromQueryParamsService,
|
|
81
|
+
public ui: AppStateService
|
|
82
|
+
) {
|
|
83
|
+
this.isBrandLogoSet = !!this.getValueForCSSVariable('--brand-logo-img');
|
|
84
|
+
this.platformAnimationSrc = this.getPlatformAnimationPath();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ngOnInit() {
|
|
88
|
+
const token = this.getParamAndClear(this.TOKEN_PARAM);
|
|
89
|
+
const ssoData = this.getSsoData();
|
|
90
|
+
if (ssoData) {
|
|
91
|
+
this.handleSso(ssoData);
|
|
92
|
+
} else if (this.loginService.isFirstLogin) {
|
|
93
|
+
if (!token) {
|
|
94
|
+
this.loginAutomatically();
|
|
95
|
+
} else {
|
|
96
|
+
this.credentials.token = token;
|
|
97
|
+
this.reset(false);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
this.loginService.isFirstLogin = false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
ngOnDestroy(): void {
|
|
104
|
+
// make sure that we do not have any queryParameters related to credentials after logging in or even if we were already logged in.
|
|
105
|
+
this.credentialsFromQueryParamsService.removeCredentialsFromQueryParams();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
handleLoginTemplate(event: LoginEvent) {
|
|
109
|
+
this.currentView = event.view;
|
|
110
|
+
this.credentials = event.credentials || {};
|
|
111
|
+
this.loginViewParams = event.loginViewParams || {};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@HostListener('keyup', ['$event']) onkeyup(event: KeyboardEvent) {
|
|
115
|
+
if (event.key !== 'Enter') {
|
|
116
|
+
this.loginService.cleanMessages();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
reset(missingPermissions: boolean) {
|
|
121
|
+
if (missingPermissions) {
|
|
122
|
+
this.handleLoginTemplate({ view: LoginViews.MissingApplicationAccess });
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
this.loginService.reset();
|
|
126
|
+
this.setView();
|
|
127
|
+
this.loginService.cleanMessages();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private getPlatformAnimationPath() {
|
|
131
|
+
const defaultPath = './platform-animation.svg';
|
|
132
|
+
|
|
133
|
+
const platformAnimationImagePath = this.getValueForCSSVariable(
|
|
134
|
+
'--login-platform-animation-img'
|
|
135
|
+
);
|
|
136
|
+
if (platformAnimationImagePath) {
|
|
137
|
+
return platformAnimationImagePath;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// in case we have a brand logo image, we don't want to show the platform animation
|
|
141
|
+
if (this.isBrandLogoSet) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return defaultPath;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private getValueForCSSVariable(variableName: string): string {
|
|
149
|
+
const rootStyles = getComputedStyle(document.body);
|
|
150
|
+
|
|
151
|
+
// getPropertyValue might not be available in e.g. unit tests
|
|
152
|
+
if (rootStyles && typeof rootStyles.getPropertyValue === 'function') {
|
|
153
|
+
const brandLogo = rootStyles?.getPropertyValue(variableName).trim();
|
|
154
|
+
return brandLogo;
|
|
155
|
+
}
|
|
156
|
+
return '';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private async loginAutomatically() {
|
|
160
|
+
this.loginService.automaticLoginInProgress$.next(true);
|
|
161
|
+
try {
|
|
162
|
+
const result = await this.loginService.login();
|
|
163
|
+
if (result) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.reset(true);
|
|
167
|
+
} catch (e) {
|
|
168
|
+
await this.loginService.clearCookies();
|
|
169
|
+
const preferredLoginOptionType = this.loginService.loginMode.type;
|
|
170
|
+
if (preferredLoginOptionType === TenantLoginOptionType.OAUTH2) {
|
|
171
|
+
this.loginService.redirectToOauth();
|
|
172
|
+
} else {
|
|
173
|
+
this.reset(false);
|
|
174
|
+
if (
|
|
175
|
+
preferredLoginOptionType === TenantLoginOptionType.OAUTH2_INTERNAL &&
|
|
176
|
+
window.location.protocol !== 'https:'
|
|
177
|
+
) {
|
|
178
|
+
this.alert.danger(gettext('Current login mode only supports HTTPS.'));
|
|
179
|
+
} else if (e.res && e.res.status === 403) {
|
|
180
|
+
this.alert.addServerFailure(e);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
this.loginService.automaticLoginInProgress$.next(false);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private setView() {
|
|
188
|
+
if (this.credentials && this.credentials.token) {
|
|
189
|
+
this.handleLoginTemplate({ view: LoginViews.ChangePassword, credentials: this.credentials });
|
|
190
|
+
} else if (this.loginService.showTenantSetup()) {
|
|
191
|
+
this.handleLoginTemplate({ view: LoginViews.TenantIdSetup });
|
|
192
|
+
} else {
|
|
193
|
+
this.handleLoginTemplate({ view: LoginViews.Credentials });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private getParamAndClear(paramName: string): string | undefined {
|
|
198
|
+
const paramValue = this.options.get<string>(paramName);
|
|
199
|
+
if (paramValue) {
|
|
200
|
+
this.options.set(paramName, undefined); // only use once
|
|
201
|
+
}
|
|
202
|
+
return paramValue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private getSsoData(): SsoData | SsoError | false {
|
|
206
|
+
const code = this.getParamAndClear('code');
|
|
207
|
+
const sessionState = this.getParamAndClear('session_state');
|
|
208
|
+
if (code) {
|
|
209
|
+
return { sessionState, code };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const ssoError = this.getParamAndClear('error');
|
|
213
|
+
const ssoErrorDescription = this.getParamAndClear('error_description');
|
|
214
|
+
if (ssoError && ssoErrorDescription) {
|
|
215
|
+
return { ssoError, ssoErrorDescription };
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private handleSso(ssoData: SsoData | SsoError) {
|
|
221
|
+
if ('ssoError' in ssoData) {
|
|
222
|
+
this.loginService.showSsoError(
|
|
223
|
+
decodeURIComponent(ssoData.ssoErrorDescription).replace(/\+/g, '%20')
|
|
224
|
+
);
|
|
225
|
+
this.reset(false);
|
|
226
|
+
} else {
|
|
227
|
+
this.loginService
|
|
228
|
+
.loginBySso(ssoData)
|
|
229
|
+
.then(() => this.loginService.login())
|
|
230
|
+
.catch(e => {
|
|
231
|
+
this.reset(false);
|
|
232
|
+
if (e.res?.status) {
|
|
233
|
+
this.alert.addServerFailure(e);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ICredentials } from '@c8y/client';
|
|
2
|
+
import { CredentialsComponentParams } from './credentials-component-params';
|
|
3
|
+
|
|
4
|
+
export interface LoginMessage {
|
|
5
|
+
message: string;
|
|
6
|
+
type: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface LoginEvent {
|
|
10
|
+
view: LoginViews;
|
|
11
|
+
credentials?: ICredentials;
|
|
12
|
+
loginViewParams?: CredentialsComponentParams | { [key: string]: any };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export enum LoginViews {
|
|
16
|
+
None = 'NONE',
|
|
17
|
+
Credentials = 'CREDENTIALS',
|
|
18
|
+
RecoverPassword = 'RECOVER_PASSWORD',
|
|
19
|
+
SmsChallenge = 'SMS_CHALLENGE',
|
|
20
|
+
ChangePassword = 'CHANGE_PASSWORD',
|
|
21
|
+
TotpChallenge = 'TOTP_CHALLENGE',
|
|
22
|
+
TotpSetup = 'TOTP_SETUP',
|
|
23
|
+
ProvidePhoneNumber = 'PROVIDE_PHONE_NUMBER',
|
|
24
|
+
TenantIdSetup = 'TENANT_ID_SETUP',
|
|
25
|
+
MissingApplicationAccess = 'MISSING_APPLICATION_ACCESS'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type SsoData = {
|
|
29
|
+
code: string;
|
|
30
|
+
sessionState?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type SsoError = {
|
|
34
|
+
ssoError: string;
|
|
35
|
+
ssoErrorDescription: string;
|
|
36
|
+
};
|