Skip to content

Commit

Permalink
Portalicious: SSO (redirect slice) (#6110)
Browse files Browse the repository at this point in the history
* portalicious: setup MSAL auth strategy

AB#31188

* redirect flow

* process feedback

---------

Co-authored-by: Domenico Gemoli <domenicogemoli@gmail.com>
  • Loading branch information
PeterSmallenbroek and aberonni authored Nov 25, 2024
1 parent e985965 commit 773f217
Show file tree
Hide file tree
Showing 27 changed files with 602 additions and 87 deletions.
1 change: 1 addition & 0 deletions interfaces/Portalicious/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ USE_SSO_AZURE_ENTRA=
# Make sure to set these values the same for the 121-service.
AZURE_ENTRA_CLIENT_ID=
AZURE_ENTRA_TENANT_ID=
AZURE_ENTRA_URL=https://login.microsoftonline.com

# API:
NG_URL_121_SERVICE_API=http://localhost:3000/api
Expand Down
31 changes: 26 additions & 5 deletions interfaces/Portalicious/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions interfaces/Portalicious/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
"@angular/router": "^18.2.11",
"@microsoft/applicationinsights-web": "^3.3.4",
"@tanstack/angular-query-experimental": "^5.60.0",
"@azure/msal-angular": "^3.1.0",
"@azure/msal-browser": "^3.27.0",
"angular-mentions": "^1.5.0",
"chart.js": "^4.4.6",
"chartjs-plugin-datalabels": "^2.2.0",
Expand Down
18 changes: 18 additions & 0 deletions interfaces/Portalicious/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { provideHttpClient } from '@angular/common/http';
import { TestBed } from '@angular/core/testing';
import { RouterModule } from '@angular/router';

import {
provideTanStackQuery,
QueryClient,
} from '@tanstack/angular-query-experimental';

import { AppComponent } from '~/app.component';
import { getAppConfig } from '~/app.config';

Expand All @@ -9,6 +15,18 @@ describe('AppComponent', () => {
await TestBed.configureTestingModule({
...getAppConfig,
imports: [AppComponent, RouterModule.forRoot([])],
providers: [
provideHttpClient(),
provideTanStackQuery(
new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
},
},
}),
),
],
teardown: { destroyAfterEach: false },
}).compileComponents();
});
Expand Down
19 changes: 16 additions & 3 deletions interfaces/Portalicious/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import {
Component,
inject,
LOCALE_ID,
OnDestroy,
OnInit,
} from '@angular/core';
import { RouterOutlet } from '@angular/router';

import { MessageService, PrimeNGConfig } from 'primeng/api';
import { ToastModule } from 'primeng/toast';
import { Subscription } from 'rxjs';

import { AuthService } from '~/services/auth.service';
import { ToastService } from '~/services/toast.service';
import { Locale } from '~/utils/locale';

Expand All @@ -24,10 +27,12 @@ import { Locale } from '~/utils/locale';
templateUrl: './app.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent implements OnInit {
export class AppComponent implements OnInit, OnDestroy {
private primengConfig = inject(PrimeNGConfig);
private locale = inject<Locale>(LOCALE_ID);
private readonly authService = inject(AuthService);
private authSubscriptions: Subscription[] = [];
toastKey = ToastService.TOAST_KEY;
primengConfig = inject(PrimeNGConfig);
locale = inject<Locale>(LOCALE_ID);

ngOnInit() {
this.primengConfig.setTranslation({
Expand All @@ -40,5 +45,13 @@ export class AppComponent implements OnInit {
apply: $localize`:@@generic-apply:Apply`,
clear: $localize`:@@generic-clear:Clear`,
});

this.authSubscriptions = this.authService.initializeSubscriptions();
}

ngOnDestroy(): void {
for (const subscription of this.authSubscriptions) {
subscription.unsubscribe();
}
}
}
2 changes: 2 additions & 0 deletions interfaces/Portalicious/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from '@tanstack/angular-query-experimental';

import { routes } from '~/app.routes';
import { AuthService } from '~/services/auth.service';
import { Locale } from '~/utils/locale';

export function getAppConfig(locale: Locale): ApplicationConfig {
Expand All @@ -34,6 +35,7 @@ export function getAppConfig(locale: Locale): ApplicationConfig {
},
}),
),
...AuthService.APP_PROVIDERS,
{ provide: LOCALE_ID, useValue: locale },
],
};
Expand Down
8 changes: 8 additions & 0 deletions interfaces/Portalicious/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { authCapabilitiesGuard } from '~/guards/auth-capabilities.guard';
import { projectPermissionsGuard } from '~/guards/project-permissions-guard';

export enum AppRoutes {
authCallback = 'auth-callback',
changePassword = 'change-password',
login = 'login',
project = 'project',
Expand All @@ -27,6 +28,13 @@ export const routes: Routes = [
loadComponent: () =>
import('~/pages/login/login.page').then((x) => x.LoginPageComponent),
},
{
path: AppRoutes.authCallback,
loadComponent: () =>
import('~/pages/auth-callback/auth-callback.page').then(
(x) => x.AuthCallbackPageComponent,
),
},
{
path: AppRoutes.changePassword,
title: $localize`:Browser-tab-title@@page-title-change-password:Change password`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ export class UserApiService extends DomainApiService {
});
}

getCurrent() {
return this.generateQueryOptions<{ user: User }>({
path: [`${BASE_ENDPOINT}/current`],
queryKey: [BASE_ENDPOINT],
});
}

createUser({
username,
displayName,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<app-page-layout>
<div class="flex w-full flex-col items-center justify-center">
<p-progressSpinner />
<h2 i18n>Redirecting to 121&hellip;</h2>
</div>
</app-page-layout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
ChangeDetectionStrategy,
Component,
inject,
OnInit,
} from '@angular/core';

import { ProgressSpinnerModule } from 'primeng/progressspinner';

import { PageLayoutComponent } from '~/components/page-layout/page-layout.component';
import { AuthService } from '~/services/auth.service';

@Component({
selector: 'app-auth-callback',
standalone: true,
imports: [PageLayoutComponent, ProgressSpinnerModule],
templateUrl: './auth-callback.page.html',
styles: ``,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AuthCallbackPageComponent implements OnInit {
private authService = inject(AuthService);

ngOnInit() {
this.authService.handleAuthCallback();
}
}
8 changes: 7 additions & 1 deletion interfaces/Portalicious/src/app/pages/login/login.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ <h1 i18n>Log in</h1>
>
Use your work email to log in.
</p>
<ng-container *ngComponentOutlet="LoginComponent"></ng-container>
<ng-container
*ngComponentOutlet="
LoginComponent;
inputs: { returnUrl: returnUrl() }
"
></ng-container>
<app-form-error [error]="authError" />
<p class="mt-2">
<a
href="https://manual.121.global/privacy-policy/"
Expand Down
31 changes: 29 additions & 2 deletions interfaces/Portalicious/src/app/pages/login/login.page.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { NgComponentOutlet, NgOptimizedImage } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';

import { ToolbarModule } from 'primeng/toolbar';

import { FormErrorComponent } from '~/components/form-error/form-error.component';
import { LanguageSwitcherComponent } from '~/components/language-switcher/language-switcher.component';
import { LogoComponent } from '~/components/logo/logo.component';
import { CookieBannerComponent } from '~/pages/login/components/cookie-banner/cookie-banner.component';
import { AuthService } from '~/services/auth.service';
import { AUTH_ERROR_IN_STATE_KEY, AuthService } from '~/services/auth.service';

@Component({
selector: 'app-login',
Expand All @@ -18,13 +25,33 @@ import { AuthService } from '~/services/auth.service';
CookieBannerComponent,
LanguageSwitcherComponent,
NgComponentOutlet,
FormErrorComponent,
],
templateUrl: './login.page.html',
styles: ``,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoginPageComponent {
private authService = inject(AuthService);
private router = inject(Router);
private route = inject(ActivatedRoute);
authError: string | undefined;
returnUrl = computed(() => {
const returnUrl: unknown = this.route.snapshot.queryParams.returnUrl;
if (typeof returnUrl !== 'string') {
return undefined;
}
return returnUrl;
});
constructor() {
const currentNavigation = this.router.getCurrentNavigation();
const authError: unknown =
currentNavigation?.extras.state?.[AUTH_ERROR_IN_STATE_KEY];

if (typeof authError === 'string') {
this.authError = authError;
}
}

LoginComponent = this.authService.LoginComponent;
}
Loading

0 comments on commit 773f217

Please sign in to comment.