Auth0 requires you to whitelist callback URLs after authentication so you can't just login on any page in your application with URLs like /thing/1, /thing/1001 as there's no way to wildcard the thing IDs.
This Github conversation points to neat solution which I have interpreted in Angular 2 as:
In my auth.service.ts:
lock = new Auth0Lock('Client_ID', 'Domain', {
auth: {
redirectUrl: window.location.origin + '/login',
responseType: 'token',
params: {
scope: 'openid name email',
state: JSON.stringify({pathname: window.location.pathname})
}
}
});
I can then whitelist my /login route in Angular 2. I tried getting my login.component.ts to read and then navigate to the pathname returned by Auth0 in the call back like this:
this.route
.queryParams
.subscribe(params => {
this.state = params['state'];
this.router.navigate(this.state, {preserveQueryParams: true});
});
...but params always seems to be empty. As far as I can tell this is because the call back URL is of this form:
...and Angular 2 routing automatically strips off the # parameters before they get to the LoginComponent.
I then spotted that authResult on lock.authenticated still contains my state parameter in auth.service.ts so attempted to navigate to it as follows:
this.lock.on("authenticated", (authResult:any) => {
localStorage.setItem('id_token', authResult.idToken);
let state: string = JSON.parse(authResult.state);
this.router.navigate([state.pathname], {});
This seemed to work initially but, it turns out, not reliably...it seems to redirect me unpredictably anywhere from
/thing/1001 to
/things or even
/
...and I can't work out why. Any help very much appreciated.
EDIT In response to @shusson's answer:
Working code based on @shusson's answer is in auth.service.ts:
export class Auth {
// Configure Auth0
lock = new Auth0Lock('Client_ID', 'Domain',{
auth: {
redirectUrl: location.origin + '/login',
responseType: 'token',
}
});
constructor(private router: Router, route: ActivatedRoute) {
// Add callback for lock `authenticated` event
this.lock.on("authenticated", (authResult:any) => {
localStorage.setItem('id_token', authResult.idToken);
let state: any = JSON.parse(authResult.state);
this.router.navigate([state.pathname], {});
...
}
public login() {
// Call the show method to display the widget.
this.lock.show({
auth: {
params: {
scope: 'openid name email',
state: JSON.stringify({pathname: this.router.url})
}
}
});
};
EDIT: based on this comment re: passing paths in callback being a CSRF vulnerability:
Final working code in my auth.service.ts is:
import { Injectable } from '@angular/core';
import { tokenNotExpired } from 'angular2-jwt/angular2-jwt';
import { Router, ActivatedRoute } from '@angular/router';
import { UUID } from 'angular2-uuid/index';
// Avoid name not found warnings
declare var Auth0Lock: any;
@Injectable()
export class Auth {
// Configure Auth0
lock = new Auth0Lock('Client_ID', 'Domain',{
auth: {
redirectUrl: location.origin + '/login',
responseType: 'token',
}
});
//Store profile object in auth class
userProfile: Object;
constructor(private router: Router, route: ActivatedRoute) {
// Set userProfile attribute of already saved profile
this.userProfile = JSON.parse(localStorage.getItem('profile'));
// Add callback for lock `authenticated` event
this.lock.on("authenticated", (authResult:any) => {
localStorage.setItem('id_token', authResult.idToken);
let pathname_object: any = JSON.parse(authResult.state);
let pathname: any = localStorage.getItem(pathname_object.pathname_key);
//get rid of localStorage of url
localStorage.removeItem(pathname_object.pathname_key);
//navigate to original url
this.router.navigate([pathname], {});
// Fetch profile information
this.lock.getProfile(authResult.idToken, (error:any, profile:any) => {
if (error) {
// Handle error
alert(error);
return;
}
localStorage.setItem('profile', JSON.stringify(profile));
this.userProfile = profile;
});
});
}
public login() {
//generate UUID against which to store path
let uuid = UUID.UUID();
localStorage.setItem(uuid, this.router.url);
// Call the show method to display the widget.
this.lock.show({
auth: {
params: {
scope: 'openid name email',
state: JSON.stringify({pathname_key: uuid})
}
}
});
};
...
}