I have two problems. First and foremost, when someone logs into my site, it is supposed to route the to http://mysite.com/main. However, instead, it routes them to http://mysite.com/main#access_token=l4jpn-Ze3ATMbw123456789jyriv09Lr&scope=openid profile email&expires_in=7200&token_type=Bearer&state=ID7shkBDlJpbo0w.HIdTAU72kA5Ntw1B&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1123456789CI6Ik1VSXpNREEzTkRkRlF6SXlOVEJCUmpaRE5qYzVSRE0yUmpSRlFqWXhSRVUxTUVNek9VTkVPQSJ9.eyJodHRwOi8vc3RyYi11c2VyIjp7ImZpcnN0X25hbWUiOiJDaHJpcyIsImxhc3R123456789IlN0YW5sZXkifSwibmlja25hbWUiOiJjaHJpcyIsIm5hbWUiOiJjaHJpc0BjaHJpcy5pbSIsInBpY3R1cmUiOiJodHRwczovL3MuZ3JhdmF0YXIuY29tL2F2YXRhci8zYTE1ZmRmNmYzMjQ0YTNhNzJjZjAwMTM2NTNlMjA2ZD9zPTQ4MCZyPXBnJmQ9aHR0cHMlM0ElMkYlMkZjZG4uYXV0aDAuY29tJTJGYXZhdGFycyUyRmNoLnBuZyIsInVwZGF0ZWRfYXQiOiIyMDE5LTA2LTIxVDE4OjEyOjQ1LjExM1oiLCJlbWFpbCI6ImNocmlzQGNocmlzLmltIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJpc3MiOiJodHRwczovL3N0b3JiaWVzLmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHw1YzU1ZmRhY2EyY2E4YjdkNzQwOTYwNTYiLCJhdWQiOiI1RmxCWlFUa0YwMHlGblhRMUZGaVhucFJKTUlGYjQ2aCIsImlhdCI6MTU2MTE0MDc2NSwiZXhwIjoxNTYxMTc2NzY1LCJhdF9oYXNoIjoibkpzUmZWUjVFS0ZwZE11MWFtZGY2dyIsIm5vbmNlIjoiSjctak1PZm1YNVZFbHhHZGJDcFh1TmdHY0JtSkVtTjMifQ.lPGptBsfPdn-PP-6yzE6aJGrnTf0dmfvRnUvdYVTvCwOo02Xn6pFcEahjKodIZ88FCwKvlQqfga0IbzDW2Ileq0q5xKfg-kTu5Ncy2MnJApjEIBeIQaUCMHbbj8HepYPftAkJdOmBhiQgsuBNCmix_HVM9fbvUNEdceARzEO1l1XyOP47KH0DYeMSHknWtGGQFRYUEYN-XJ123456789eA55IqOVRlAzeUNgqe-niWi8gaRAn0lKgASYvPdr1QNeWKEPJFNRkPGB0jtYJ2cF5lGLx-Yx2GyOtCucevMkennXHXfZauanQ2jUpeATVKm7DA69uoQB60NrR13I5INyxQ
Seriously, what in the holy crap is all that?! I donât need that. All that information is safely stored in local storage. I purposely wrote my application to have shorter urls, so I absolutely cannot stand this unnecessary garbage being added. Please tell me how to stop this.
Now, thatâs just an annoying problem. Hereâs the horrific problem that WONâT ALLOW MY USERS TO LOG IN. Every time someone tries to log in, an alert pops up with âInvalid Token. See Console for details.â Checking the console, it says âstate does not match.â Wtf is that? Just log in the user. I donât give a â â â â about state. Just log in. Thatâs it. Iâm about to end usage of this God damned service, because it has been nothing but ridiculous issues.
It looks like there is an access token, id token, and some other sensitive information in the URL. Would you be able to post or DM me a code snippet of the auth0 implementation in your app?
Also, it is important not to store sensitive information in local storage.
For the record, I had edited the url I posted, replacing much of the data with just â123456789,â so your edit wasnât necessary. I wanted to keep the length to show how absolutely absurd the url was. Your edit does not exemplify the absurdity I wanted to showcase.
I am using this in an Angular app, and I have a navigation component with this code:
if (!this.auth.isAuthenticated() && this.location.path() !== '/home') {
if (!this.auth.handleAuthentication()) return this.router.navigate(['/home'])
}
It seems to me that the problem is the this.auth0.parseHash function is âslow,â and by the time it finishes what itâs doing (setting the user and tokens), the app itself has already navigated to the appropriate page. However, this.auth0.parseHash does not seem to be a Promise or Stream, so I canât actively wait for it to complete before navigating.
I reverted it to your original post so your point isnât lost.
The parseHash function is making an external call during the this.auth0.client.userInfo function. This explains the âwaitâ.
Have you correctly set your redirectUri? It seems like the problem with the URL is coming from wherever that /main redirect is happening, not the /home route. It is possible that is also the source of your state error, especially if that route is happening twice like this example.
Could you post the code associated with that route?
Yes, I correctly set my callback uri. The problem is that I donât want that uri to be accessible unless someone is logged in, and thanks to the wait period of the external call of this.auth0.client.userInfo that does NOT return a Promise or Stream, when auth0 routes to the callback url, it hasnât put the expires_at key into local storage yet, so this.authService.isAuthenticated returns false, and my nav component routes right back to /home. This is actually why I opted not to use an AuthGuard via CanActivate on my Angular app, because I had to create all kinds of Behavior Subjects and Streams to wait and see if that token ever gets set, and to see if the UserService was setting a user, but if someone manually routed to a non-public url, those things would never return a value, because nothing was setting them, and relying on a timeout is just hacky.
Can you try to separate the userInfo request from the handleAuthentication function all together? Then you can call a getProfile method in a profile component. Something like thisâŠ
AuthService:
public handleAuthentication(): void {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.localLogin(authResult);
this.router.navigate(['/home']);
} else if (err) {
this.router.navigate(['/home']);
console.log(err);
alert(`Error: ${err.error}. Check the console for further details.`);
}
});
}
private localLogin(authResult): void {
// Set the time that the access token will expire at
const expiresAt = (authResult.expiresIn * 1000) + Date.now();
this._accessToken = authResult.accessToken;
this._idToken = authResult.idToken;
this._expiresAt = expiresAt;
}
public getProfile(cb): void {
if (!this._accessToken) {
throw new Error('Access token must exist to fetch profile');
}
const self = this;
this.auth0.client.userInfo(this._accessToken, (err, profile) => {
if (profile) {
self.userProfile = profile;
}
cb(err, profile);
});
}
Again, the problem lies in the race condition. When do I get the user profile? When the redirect happens, the token still isnât set, so using the getProfile function will just throw the error. What needs to happen is that parseHash function needs to be a Promise or a Stream, so it can either be watched or subscribed to. Then once the authResult comes through, the rest of the app can verify the validation. In my version, I have it returning a boolean in the hopes that would somehow help, but then thereâs the state issue. Iâve tried returning it as an Observable (return of(this.auth0.parseHashâŠ)), and it still doesnât work.
Do you need the profile to load directly after login? How it is handled in the example app I linked above, the user has to visit a /profile route before that call is made. In essence, the user logs in, is redirected to a home page, then will have to navigate to a profile route before the getProfile method is called. This would be more efficient as well, since the /userinfo endpoint is not called on every authentication.
We also just released the new auth0-spa-js with auth code + pkce (the recent standard grant type for SPAs according to the OAuth2.0 Spec). It looks like they may have addressed your issue. Take a look at the new example here.
Sorry youâre having issues. Just to jump in here, take a look at our Angular aside code that we use in articles. It is up-to-date with best practices for the auth0.js SDK youâre using. It also demonstrates how to convert the Node calls in the SDK to observables to make them easier to work with in Angular.
For example, the functions for handling the callback (which would be called on your callback component load) look like this:
handleLoginCallback() {
if (window.location.hash && !this.isAuthenticated) {
this.parseHash$().subscribe(
authResult => {
this.localLogin(authResult);
this.router.navigate([this.onAuthSuccessUrl]);
},
err => this.handleError(err)
)
}
}
private localLogin(authResult) {
// Observable of token
this.token$.next(authResult.accessToken);
// Emit value for user data subject
this.userProfile$.next(authResult.idTokenPayload);
// Set flag in local storage stating this app is logged in
localStorage.setItem(this.authFlag, JSON.stringify(true));
}
Hopefully the sample code in that repo I mentioned will help clear up your issues.
As @dan.woda mentioned, though, the new SPA SDK that was just released is lightweight and uses a different auth flow, so it might solve your issues as well. We have a brand new Angular QuickStart that uses this new SDK. Check it out and see if it solves your problem.