Auth callback continually redirecting
See original GitHub issueIssue type
I’m submitting a … (check one with “x”)
- bug report
- feature request
Issue description
Current behavior: I’ve followed the documentation for setting up OAuth authentication (which I’ve done in the ngx-admin starter app) - instead of Google however I’m setting up Azure AD B2C authentication.
The login request works fine, it goes to Microsoft’s auth servers and then returns with an id_token
in the callback url. However, when the callback happens and the router directs to the nb-oauth2-callback.component.ts
, the ‘Authenticating…’ message shows up briefly, and then instead of redirecting to the homescreen as I’ve defined it to redirect to (as per the docs), it instead goes through the login process again, repeating the process in a never-ending loop.
The url of the callback from the Microsoft auth server looks like this: https://localhost:5001/auth/callback#id_token=eyJ0eXAiOiJKV1Q-REST-OF-TOKEN
I’ve tried to do some debugging, and have noticed a couple of things when going through the code -
There’s an exception in the token-parceler.js
file:
Cannot read property 'name' of null at NbAuthTokenParceler.push...
And a ‘no match’ exception in the router.js
file with the url parameter in 23. this.Recognizer
showing as ‘auth/callback#id_tokeneyJ0eX-REST-OF-TOKEN’.
Not sure if this is relevant to the redirect looping issue however seems like the token isn’t being recognised/intercepted from the url header and that the router is treating it as a url fragment.
Really appreciate any help in figuring this out!
Expected behavior:
Steps to reproduce:
In the ngx-admin starter app, I’ve created a nb-oauth2-login.component.ts
like so:
import { Component, OnDestroy } from '@angular/core';
import { NbAuthOAuth2Token, NbAuthResult, NbAuthService } from '@nebular/auth';
import { takeWhile } from 'rxjs/operators';
@Component({
selector: 'ngx-oauth2-login',
template: `
<nb-layout>
<nb-layout-column>
<nb-card>
<nb-card-body>
<p>Current User Authenticated: {{ !!token }}</p>
<p>Current User Token: {{ token|json }}</p>
<button class="btn btn-success" *ngIf="!token" (click)="login()">Sign In</button>
<button class="btn btn-warning" *ngIf="token" (click)="logout()">Sign Out</button>
</nb-card-body>
</nb-card>
</nb-layout-column>
</nb-layout>
`,
})
export class NbOAuth2LoginComponent implements OnDestroy {
alive = true;
token: NbAuthOAuth2Token;
constructor(private authService: NbAuthService) {
this.authService.onTokenChange()
.pipe(takeWhile(() => this.alive))
.subscribe((token: NbAuthOAuth2Token) => {
this.token = null;
if (token && token.isValid()) {
this.token = token;
}
});
}
login() {
this.authService.authenticate('AzureADB2C')
.pipe(takeWhile(() => this.alive))
.subscribe((authResult: NbAuthResult) => {
});
}
logout() {
this.authService.logout('AzureADB2C')
.pipe(takeWhile(() => this.alive))
.subscribe((authResult: NbAuthResult) => {
});
}
ngOnDestroy(): void {
this.alive = false;
}
}
And a nb-oauth2-callback.component.ts
like so:
import { Component, OnDestroy } from '@angular/core';
import { NbAuthResult, NbAuthService } from '@nebular/auth';
import { Router } from '@angular/router';
import { takeWhile } from 'rxjs/operators';
@Component({
selector: 'ngx-oauth2-callback',
template: `
Authenticating...
`,
})
export class NbOAuth2CallbackComponent implements OnDestroy {
alive = true;
constructor(private authService: NbAuthService, private router: Router) {
this.authService.authenticate('AzureADB2C')
.pipe(takeWhile(() => this.alive))
.subscribe((authResult: NbAuthResult) => {
if (authResult.isSuccess() && authResult.getRedirect()) {
this.router.navigateByUrl(authResult.getRedirect());
}
});
}
ngOnDestroy(): void {
this.alive = false;
}
}
Set up my core.module.ts
file with an authentication strategy like so:
export const NB_CORE_PROVIDERS = [
...DataModule.forRoot().providers,
...NbAuthModule.forRoot({
strategies: [
NbOAuth2AuthStrategy.setup({
name: 'AzureADB2C',
clientId: '************************************',
clientSecret: '',
authorize: {
// tslint:disable-next-line:max-line-length
endpoint: 'https://login.microsoftonline.com/tfp/pixeldr.onmicrosoft.com/b2c_1_pxdrsignin/oauth2/v2.0/authorize',
responseType: NbOAuth2ResponseType.TOKEN, // have changed in the nebular/auth package (oauth2-strategy.options.js) from 'token' to 'id_token',
scope: 'https://PixelDr.onmicrosoft.com/api/profile openid profile',
redirectUri: 'https://localhost:5001/auth/callback',
},
token: {
endpoint: 'id_token',
class: NbAuthJWTToken, // changed from NbAuthOAuth2Token as ADB2C returns JWT
},
redirect: {
success: '/pages/overview',
},
}),
],
forms: {
login: {
socialLinks: socialLinks,
},
register: {
socialLinks: socialLinks,
},
},
}).providers,
And finally modified the app-routing.module.ts
like so:
const routes: Routes = [
{ path: 'pages', loadChildren: 'app/pages/pages.module#PagesModule' },
{
path: 'auth',
component: NbAuthComponent,
children: [
{
path: '',
component: NbOAuth2LoginComponent,
},
{
path: 'login',
component: NbLoginComponent,
},
{
path: 'register',
component: NbRegisterComponent,
},
{
path: 'logout',
component: NbLogoutComponent,
},
{
path: 'request-password',
component: NbRequestPasswordComponent,
},
{
path: 'reset-password',
component: NbResetPasswordComponent,
},
{
path: 'callback',
component: NbOAuth2CallbackComponent,
},
],
},
{ path: '', redirectTo: 'pages', pathMatch: 'full' },
{ path: '**', redirectTo: 'pages' },
];
const config: ExtraOptions = {
useHash: false,
};
This is all using the latest Nebular release.
Thanks again in advance for any help!
Issue Analytics
- State:
- Created 5 years ago
- Comments:13
Top GitHub Comments
Hi @nnixaa - thank you, after comparing the above I noticed for some reason that I was missing
map(fragment => this.parseHashAsQueryParams(fragment)),
which looks like it was the key ingredient - now working 😃 I’ll do some tests over the next few days and integrate identity properly to see if all of the values are parsed properly, but it’s not continually redirecting anymore which is great news.Can’t thank you enough for your help! 😃
Hey @jjgriff93, I guess your router setting
useHash
is set to false, isn’t it? I most likely missed that part modifying that example. This one works fine on my side (note I have to remove some of imported files as I don’t have those):To sum up, the main issue is “where to look for the token”:
As some of the backend services return it as url
hash
(fragment), some asquery params
, so we need to be very careful with this and somehow take this into account inside of the strategy itself.