manfredsteyer / angular-oauth2-oidc

Support for OAuth 2 and OpenId Connect (OIDC) in Angular.
MIT License
1.89k stars 688 forks source link

prevent showing the wildcard route component while doing authentication. #1388

Open innoveltec opened 8 months ago

innoveltec commented 8 months ago

I am having an issue which was already mentioned a time ago but was closed in the mean time. => issue#450

We currently have a wildcard route {path: '**', component: PageNotFoundComponent}. While doing authentication we also see the page not found component from the wildcard route for a second before the correct page is loaded. Does anyone already implemented a solution/workaround for this? Currently just showing a component with a loading text on the wildcard route instead of the page not found component to not show the page not found to the users but this is not a proper solution :-)

Thanks,

Gerry

primalfear commented 5 months ago

I had the same issue, and was able to resolve like this: Use an a "APP_INITIALIZER" to deal with the authentication and then allow the app to render. So this is what i came up with:

This allows the Application to wait for the promises to be resolved before the application loads.

auth-initalizer.ts

import { AuthConfig, OAuthService } from "angular-oauth2-oidc";
import { environment } from "environments/environment";
import { Observable } from "rxjs";

const authConfig: AuthConfig = {
    issuer: environment.authentication.authority,
    redirectUri: window.location.origin + '/index.html',
    silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html',
    clientId: environment.authentication.appId,
    dummyClientSecret: environment.authentication.appSecret,
    responseType: 'code',
    scope: environment.authentication.scopes,
    showDebugInformation: environment.authentication.showDebug,
    sessionChecksEnabled: environment.authentication.enableSessionChecks,
    requireHttps: environment.authentication.requireHttps,
    silentRefreshShowIFrame: environment.authentication.silentRefreshShowIFrame,
    oidc: true,
};

export function AuthInitalizer(oauthService: OAuthService, router: Router) {

    return () => {
        oauthService.configure(authConfig);
        oauthService.setupAutomaticSilentRefresh();
        return new Promise((resolve, reject) => {
            initialize(oauthService).subscribe((loggedIn) => {
                if (!loggedIn) {
                    reject('could not login the user');
                    return;
                }
                resolve(true);
                if (oauthService.state) {
                    const uri = decodeURIComponent(oauthService.state);
                    router.navigateByUrl(uri);
                } else {
                    router.navigateByUrl('/');
                }
            })
        });
    };
}

function initialize(oauthService: OAuthService): Observable<boolean> {
    return new Observable<boolean>((observer) => {
        oauthService.loadDiscoveryDocumentAndTryLogin().then((loaded) => {
            if (!loaded) {
                observer.next(false);
                observer.complete();
            }
            if (!oauthService.hasValidIdToken() || !oauthService.hasValidAccessToken()) {
                const path = window.location.pathname;
                oauthService.initCodeFlow(path);
            } else {
                observer.next(true);
                observer.complete();
            }
        });
    });
}

The in the app component (if you're using standalone) or app.module you can just added use it like this.

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    provideOAuthClient({
      resourceServer: {
        allowedUrls: ['https://api.domain.com'],
        sendAccessToken: true
      }
    }),
    { provide: APP_INITIALIZER, useFactory: AuthInitalizer, deps: [OAuthService, Router], multi: true, }
  ],
};