Skip to content

Commit d229559

Browse files
authored
Merge pull request #9408 from camptocamp/openid-connect-gsggr-152
Add Login for OIDC client
2 parents 3c36c32 + 62f67a8 commit d229559

File tree

4 files changed

+101
-56
lines changed

4 files changed

+101
-56
lines changed

src/auth/FormElement.ts

Lines changed: 72 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,15 @@ export default class GmfAuthForm extends GmfBaseElement {
5353
@state() private allowPasswordReset = false;
5454
@state() private changingPassword = false;
5555
@state() private userMustChangeItsPassword = false;
56+
@state() private openIdConnectUrl = '';
5657
@state() private error = false;
5758
@state() private otpImage = '';
5859
@state() private gmfUser: User = null;
5960
@state() private customCSS_ = '';
6061
private changingPasswordUsername_ = '';
62+
private initialApplicationUrl = window.location.href;
63+
private currentApplicationUrl = window.location.href;
64+
private openIdConnectBaseUrl = '';
6165

6266
connectedCallback(): void {
6367
super.connectedCallback();
@@ -73,13 +77,21 @@ export default class GmfAuthForm extends GmfBaseElement {
7377
}
7478
},
7579
}),
80+
7681
user.getLoginMessage().subscribe({
7782
next: (message: string) => {
7883
this.loginInfoMessage = message;
84+
this._updateOpenIdConnectUrl();
7985
},
8086
}),
8187
);
8288

89+
window.addEventListener('popstate', () => {
90+
this.currentApplicationUrl = window.location.href;
91+
this._updateOpenIdConnectUrl();
92+
});
93+
this._updateOpenIdConnectUrl();
94+
8395
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
8496
const loginField = document.body.querySelector('input[slot=gmf-auth-login]') as HTMLInputElement;
8597
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
@@ -97,11 +109,20 @@ export default class GmfAuthForm extends GmfBaseElement {
97109
});
98110
}
99111

112+
_updateOpenIdConnectUrl(): void {
113+
const applicationUrl = this.loginInfoMessage ? this.currentApplicationUrl : this.initialApplicationUrl;
114+
const params = new URLSearchParams({
115+
came_from: applicationUrl,
116+
});
117+
this.openIdConnectUrl = `${this.openIdConnectBaseUrl}?${params.toString()}`;
118+
}
119+
100120
// override default initConfig
101121
initConfig(configuration: Configuration): void {
102122
this.twoFactorAuth = configuration.gmfTwoFactorAuth;
103123
this.allowPasswordChange = configuration.gmfAuthenticationConfig.allowPasswordChange;
104124
this.allowPasswordReset = configuration.gmfAuthenticationConfig.allowPasswordReset;
125+
this.openIdConnectBaseUrl = configuration.gmfOidcLoginUrl;
105126
if (configuration.gmfCustomCSS && configuration.gmfCustomCSS.authentication !== undefined) {
106127
this.customCSS_ = configuration.gmfCustomCSS.authentication;
107128
}
@@ -167,7 +188,7 @@ export default class GmfAuthForm extends GmfBaseElement {
167188
</div>
168189
<div class="form-group">
169190
<input
170-
?hidden="${!this.allowPasswordChange}"
191+
?hidden="${!(this.allowPasswordChange && this.gmfUser.login_type !== 'oidc')}"
171192
type="button"
172193
class="form-control btn btn-default"
173194
value=${i18next.t('Change password')}
@@ -195,53 +216,57 @@ export default class GmfAuthForm extends GmfBaseElement {
195216
`
196217
: ''}
197218
${this.gmfUser.username === null && !this.changingPassword
198-
? html`
199-
<div>
200-
<form name="loginForm" role="form" @submit=${(evt: Event) => this.login(evt)}>
201-
<div class="form-group">
202-
<slot name="gmf-auth-login"></slot>
203-
</div>
204-
<div class="form-group">
205-
<slot name="gmf-auth-password"></slot>
206-
</div>
207-
${this.twoFactorAuth
208-
? html`
209-
<div class="form-group">
210-
${i18next.t('The following field should be kept empty on first login:')}
211-
<input
212-
type="text"
213-
class="form-control"
214-
name="otp"
215-
autocomplete="one-time-code"
216-
placeholder=${i18next.t('Authentication code')}
217-
/>
218-
</div>
219-
`
220-
: ''}
221-
<div class="form-group">
222-
<input type="submit" class="form-control btn prime" value=${i18next.t('Connect')} />
223-
</div>
224-
${this.isLoading
225-
? html`
226-
<div class="login-spinner">
227-
<i class="fa fa-spin">${svgSpinner()}</i>
228-
</div>
229-
`
219+
? this.gmfUser.login_type === 'oidc'
220+
? html`<a class="btn prime form-control" role="button" href="${this.openIdConnectUrl}"
221+
>${i18next.t('Connect')}</a
222+
>`
223+
: html`
224+
<div>
225+
<form name="loginForm" role="form" @submit=${(evt: Event) => this.login(evt)}>
226+
<div class="form-group">
227+
<slot name="gmf-auth-login"></slot>
228+
</div>
229+
<div class="form-group">
230+
<slot name="gmf-auth-password"></slot>
231+
</div>
232+
${this.twoFactorAuth
233+
? html`
234+
<div class="form-group">
235+
${i18next.t('The following field should be kept empty on first login:')}
236+
<input
237+
type="text"
238+
class="form-control"
239+
name="otp"
240+
autocomplete="one-time-code"
241+
placeholder=${i18next.t('Authentication code')}
242+
/>
243+
</div>
244+
`
245+
: ''}
246+
<div class="form-group">
247+
<input type="submit" class="form-control btn prime" value=${i18next.t('Connect')} />
248+
</div>
249+
${this.isLoading
250+
? html`
251+
<div class="login-spinner">
252+
<i class="fa fa-spin">${svgSpinner()}</i>
253+
</div>
254+
`
255+
: ''}
256+
<div ?hidden="${!this.allowPasswordReset}" class="form-group">
257+
<a @click=${(evt: Event) => this.resetPassword(evt)} href=""
258+
>${i18next.t('Password forgotten?')}</a
259+
>
260+
</div>
261+
</form>
262+
263+
${this.resetPasswordShown
264+
? html` <div class="alert alert-info">
265+
${i18next.t('A new password has just been sent to you by e-mail.')}
266+
</div>`
230267
: ''}
231-
<div ?hidden="${!this.allowPasswordReset}" class="form-group">
232-
<a @click=${(evt: Event) => this.resetPassword(evt)} href=""
233-
>${i18next.t('Password forgotten?')}</a
234-
>
235-
</div>
236-
</form>
237-
238-
${this.resetPasswordShown
239-
? html` <div class="alert alert-info">
240-
${i18next.t('A new password has just been sent to you by e-mail.')}
241-
</div>`
242-
: ''}
243-
</div>
244-
`
268+
</div>
269+
`
245270
: ''}
246271
${this.changingPassword
247272
? html`

src/auth/component.stories.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ const login = user.getEmptyUserProperties();
7878
login.username = 'George';
7979
WithUser.args.user = login;
8080

81+
export const EmptyOidc: any = Template.bind({});
82+
EmptyOidc.args = {...defaultProperties};
83+
const loginEmptyOidc = user.getEmptyUserProperties();
84+
loginEmptyOidc.login_type = 'oidc';
85+
EmptyOidc.args.user = loginEmptyOidc;
86+
87+
export const WithUserOidc: any = Template.bind({});
88+
WithUserOidc.args = {...defaultProperties};
89+
const loginOidc = user.getEmptyUserProperties();
90+
loginOidc.login_type = 'oidc';
91+
loginOidc.username = 'George OIDC';
92+
WithUserOidc.args.user = loginOidc;
93+
8194
/**
8295
* @returns The HTML of the story
8396
*/

srcapi/store/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,7 @@ export type Configuration = {
14711471
gmfI18nextConfiguration: InitOptions;
14721472
pytreeLidarprofileJsonUrl: pytreeLidarprofileJsonUrl;
14731473
gmfDatasourceOptions: gmfDatasourceOptions;
1474+
gmfOidcLoginUrl: string;
14741475
};
14751476

14761477
export type APIConfig = {

srcapi/store/user.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ export interface User {
9292
* The two-factor authentication secret on first login
9393
*/
9494
two_factor_totp_secret: string;
95+
/**
96+
* The server-side login type (oidc or local)
97+
*/
98+
login_type?: string;
9599
}
96100

97101
export enum UserState {
@@ -203,15 +207,17 @@ export class UserModel {
203207
*/
204208
getEmptyUserProperties(): User {
205209
return {
206-
email: null,
207-
is_intranet: null,
208-
functionalities: null,
209-
is_password_changed: null,
210-
roles: null,
211-
username: null,
212-
otp_key: null,
213-
otp_uri: null,
214-
two_factor_totp_secret: null,
210+
...{
211+
email: null,
212+
is_intranet: null,
213+
functionalities: null,
214+
is_password_changed: null,
215+
roles: null,
216+
username: null,
217+
otp_key: null,
218+
otp_uri: null,
219+
two_factor_totp_secret: null,
220+
},
215221
};
216222
}
217223

0 commit comments

Comments
 (0)