I have an angular app (ver. 20.2, zoneless, standalone), running locally for now at https://localhost:4200. The app uses Angular Auth OIDC Client.
As per docs, I use autoLoginPartialRoutesGuard for app's routes:
export const routes: Routes = [
{
path: "register",
component: IlgRegisterUser
},
{
path: "login",
component: IlgLogin
},
{
path: "users",
component: IlgUsersList,
canActivate: [autoLoginPartialRoutesGuard]
},
{
path: "unauthorized",
component: IlgUnauthorized
}
];
The app.config.ts looks like this:
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideZonelessChangeDetection(),
provideHttpClient(withInterceptors([authInterceptor()])),
provideAuth(authConfig, withAppInitializerAuthCheck()),
provideRouter(routes) // , withEnabledBlockingInitialNavigation()
]
};
The auth for Azure AD B2C is the following:
export const authConfig: PassedInitialConfig = {
config: {
authority: "https://my-domain.b2clogin.com/{TenantId}/v2.0/",
authWellknownEndpointUrl: "https://my-domain.b2clogin.com/my-domain.onmicrosoft.com/B2C_1_si/v2.0/.well-known/openid-configuration",
redirectUrl: "https://localhost:4200",
postLogoutRedirectUri: window.location.origin,
triggerAuthorizationResultEvent: false,
unauthorizedRoute: "/unauthorized",
clientId: "{ClientId Guid}",
scope: "openid offline_access",
responseType: "code",
silentRenew: true,
useRefreshToken: true,
ignoreNonceAfterRefresh: true,
maxIdTokenIatOffsetAllowedInSeconds: 600,
issValidationOff: false, // this needs to be true if using a common endpoint in Azure
autoUserInfo: false,
logLevel: LogLevel.Debug,
customParamsAuthRequest: {
prompt: "select_account" // login, consent
}
}
};
The app.component.ts uses checkAuth() to check authentication status (even though I use
provideAuth(authConfig, withAppInitializerAuthCheck()) in app.config.ts):
@Component({
selector: "ilg-root",
imports: [AsyncPipe, JsonPipe, RouterOutlet, MatSidenavModule, MatButtonModule, MatIconModule, MatDividerModule, IlgNavBar],
templateUrl: "./app.html",
styleUrl: "./app.scss"
})
export class App implements OnInit {
ngOnInit(): void {
this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, userData, accessToken, idToken, configId }) => {
console.log(`isAuthenticated: ${isAuthenticated}`);
});
}
protected readonly oidcSecurityService = inject(OidcSecurityService);
protected authenticated = this.oidcSecurityService.authenticated;
protected userData$ = this.oidcSecurityService.userData$;
}
The app.component.html shows the auth info:
<p>User is {{ oidcSecurityService.authenticated().isAuthenticated ? "Authenticated" : "NOT authenticated" }}</p>
<br />
UserData
<pre>{{ userData$ | async | json }}</pre>
When I open the /users page, auth kicks in and I see the Azure AD B2C log in page. I enter my user/pass and then I get back to the https://localhost:4200, which shows me now:
The problem is if I go to the /users/ page again, being authenticated, it redirects me to Azure B2C Auth login page! And whatever I do it works this way only!
I ended up using Microsoft's own MSAL.js. They have examples and their code just works. Here's part of my signal store for Auth:
export const AuthStore = signalStore(
{ providedIn: "root" },
withState(initialState),
withMethods(
(
store,
msalGuardConfig: MsalGuardConfiguration = inject<MsalGuardConfiguration>(MSAL_GUARD_CONFIG),
authService: MsalService = inject(MsalService)
) => ({
setAuth(account: AccountInfo | null): void {
patchState(store, {
account,
idToken: account?.idToken ?? null,
idTokenClaims: account?.idTokenClaims as IdTokenClaims | null
});
},
clearAuth(): void {
patchState(store, initialState);
},
loginRedirect(): void {
if (msalGuardConfig.authRequest) {
authService.loginRedirect({
...msalGuardConfig.authRequest
} as RedirectRequest);
} else {
authService.loginRedirect({ scopes: ["openid", "profile", "email"] });
}
},
loginPopup(): void {
if (msalGuardConfig.authRequest) {
authService.loginPopup({ ...msalGuardConfig.authRequest } as PopupRequest).subscribe((response: AuthenticationResult) => {
authService.instance.setActiveAccount(response.account);
});
} else {
authService.loginPopup().subscribe((response: AuthenticationResult) => {
authService.instance.setActiveAccount(response.account);
});
}
},
logout(popup?: boolean): void {
if (popup) {
authService.logoutPopup({
mainWindowRedirectUri: "/"
});
} else {
authService.logoutRedirect();
}
this.clearAuth();
}
})
)
);
MSAL.js is supported and actively maintained. Unlike other libs. Some turn to garbage, some introduce paid licenses.