skip to Main Content

I have an angular application deployed on Azure App Services and have enabled Azure’s built-in authentication on it (aka Easy Auth). I am able to successfully login to my identity provider (AAD) but after being redirected to {domain}/.auth/login/aad/callback, I am again further redirected to {domain}/.auth/login/done. Any attempt to navigate back to the base URL just loops back around (since presumably whatever code or token produced hasn’t been sent back to the application).

/done endpoint:

done url

App registration redirect URIs:
redirect uris

Network request captured, showing the matching redirect URI:
network tab

Some more context: I only get this issue in the deployed instances. When building the front-end locally, the redirect URI is just set to localhost:4200 (defined in the SPA platform) and everything works as intended. However, just having {domain}/.auth/login/aad/callback under the "SPA" platform led to a PKCE error, so I followed this post and added both "web" and "single-page application" platforms.

I have seen the following posts but they appear to be doing some authentication on the server side:

  1. Azure Active Directory always redirects to ‘~/.auth/login/done’
    when deployed to Azure despite working on localhost
  2. Web application with Azure Active Directory always redirects to the ‘~/.auth/login/done’ URL

2

Answers


  1. I created a simple Angular application with Azure AD and successfully deployed it to Azure App Service.

    • Make sure to configure your Azure Web App’s URL in your app.module.ts and auth-config.ts as the redirectUri before deploying it to Azure App Service.

    app.module.ts:

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { RouterModule } from '@angular/router';
    import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
    import {
      MsalModule,
      MsalInterceptor,
      MsalService,
      MSAL_INSTANCE,
      MsalGuard,
      MSAL_GUARD_CONFIG,
      MSAL_INTERCEPTOR_CONFIG,
      MsalGuardConfiguration,
      MsalInterceptorConfiguration
    } from '@azure/msal-angular';
    import { PublicClientApplication, InteractionType } from '@azure/msal-browser';
    
    import { AppComponent } from './app.component';
    import { HomeComponent } from './home/home.component';
    import { LoginComponent } from './login/login.component';
    
    export function MSALInstanceFactory() {
      return new PublicClientApplication({
        auth: {
          clientId: '<client-id>',
          authority: 'https://login.microsoftonline.com/<tenant-id>',
          redirectUri: '<your-azure-web-app>',
        },
        cache: {
          cacheLocation: 'localStorage',
          storeAuthStateInCookie: true,
        },
      });
    }
    export function MSALGuardConfigFactory(): MsalGuardConfiguration {
      return {
        interactionType: InteractionType.Redirect,
        authRequest: {
          scopes: ['user.read']
        }
      };
    }
    export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
      return {
        interactionType: InteractionType.Redirect,
        protectedResourceMap: new Map([
          ['https://graph.microsoft.com/v1.0/me', ['user.read']]
        ])
      };
    }
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        LoginComponent,
      ],
      imports: [
        BrowserModule,
        HttpClientModule,
        RouterModule.forRoot([
          { path: 'translate', component: HomeComponent, canActivate: [MsalGuard] },
          { path: '', component: LoginComponent },
        ]),
        MsalModule.forRoot(
          MSALInstanceFactory(),
          MSALGuardConfigFactory(),
          MSALInterceptorConfigFactory()
        )
      ],
      providers: [
        MsalService,
        MsalGuard,
        {
          provide: HTTP_INTERCEPTORS,
          useClass: MsalInterceptor,
          multi: true
        }
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    
    

    auth-config.ts:

    import { Configuration, BrowserCacheLocation, LogLevel } from '@azure/msal-browser';
    const isBrowser = typeof window !== 'undefined';
    export const msalConfig: Configuration = {
      auth: {
        clientId: '<client-id>', 
        authority: 'https://login.microsoftonline.com/<tenant-id>', 
        redirectUri: '<your-azure-web-app>', 
      },
      cache: {
        cacheLocation: BrowserCacheLocation.LocalStorage,
        storeAuthStateInCookie: isBrowser && isIE(), 
      },
      system: {
        loggerOptions: {
          loggerCallback(logLevel, message, containsPii) {
            if (containsPii) { 
              return; 
            }
            switch (logLevel) {
              case LogLevel.Error:
                console.error(message);
                return;
              case LogLevel.Info:
                console.info(message);
                return;
              case LogLevel.Verbose:
                console.debug(message);
                return;
              case LogLevel.Warning:
                console.warn(message);
                return;
            }
          },
          piiLoggingEnabled: false,
        }
      }
    };
    export const loginRequest = {
      scopes: ["User.Read"]
    };
    function isIE(): boolean {
      return isBrowser && (window.navigator.userAgent.indexOf("MSIE ") > -1 ||
          window.navigator.userAgent.indexOf("Trident/") > -1);
    }
    

    app.component.ts:

    import { Component, OnInit } from  '@angular/core';
    import { MsalService } from  '@azure/msal-angular';
    import { Router } from  '@angular/router';
    @Component({
    selector:  'app-root',
    templateUrl:  './app.component.html',
    })
    export  class  AppComponent  implements  OnInit {
    title(title:  any) {
    throw  new  Error('Method not implemented.');
    }
    constructor(private  authService:  MsalService, private  router:  Router) {}
    ngOnInit() {
    this.authService.handleRedirectObservable().subscribe({
    next: (result) => {
    if (result  !=  null  &&  result.account  !=  null) {
    this.authService.instance.setActiveAccount(result.account);
    this.router.navigate(['/translate']);
    }
    },
    error: (error) =>  console.log(error)
    });
    }
    }
    
    • Make sure the Redirect URI in the Single-page application section matches the one in your code.

    enter image description here

    • Configure the below startup command in the configuration section of the Azure Web App.
    pm2 serve /home/site/wwwroot/dist/my-angular-app/browser --no-daemon --spa
    

    enter image description here

    Here’s the output after deployment:

    enter image description here

    enter image description here

    enter image description here

    Login or Signup to reply.
  2. If you’re using MSAL angular try disabling Azure’s built in authentication for the client side apps in app services.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search