By following the tutorial in IdentityServer-ClientQuickstart
i m using this client configuration:
```
document.getElementById("login").addEventListener("click", login, false);
document.getElementById("logout").addEventListener("click", logout, false);
var config = {
authority: "http://localhost:5000",
client_id: "js-local",
redirect_uri: "http://localhost:5003",
response_type: "id_token",
scope: "openid profile",
post_logout_redirect_uri: "http://localhost:5003",
filterProtocolClaims: true,
loadUserInfo: true
};
var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
}
else {
log("User not logged in");
}
});
function login() {
mgr.signinRedirect();
}
function logout() {
mgr.signoutRedirect();
}
```
The index.html page is the same provided in the quickstart.
After clicking the "Login" button, my client is successfully authenticated by the Identity Server and redirected to "localhost:5003". I can see the id_token in the URL. It contains the userid and profile info as requested, but the UserManager.GetUser() is always returning a null object.
Feel free to ask more infos if i forgot to mention anything .
Thanks
Adding some logs:
Log.js:55 no user storageString
Log.js:55 user not found in storage
Log.js:55 OidcClient.createSigninRequest
Log.js:55 MetadataService.getAuthorizationEndpoint
Log.js:55 MetadataService._getMetadataProperty authorization_endpoint
Log.js:55 MetadataService.getMetadata
Log.js:55 getting metadata from http://localhost:5000/.well-known/openid-configuration
Log.js:55 JsonService.getJson http://localhost:5000/.well-known/openid-configuration
Log.js:55 HTTP response received, status 200
Log.js:55 json received
Log.js:55 metadata recieved
Log.js:55 Received authorization endpoint http://localhost:5000/connect/authorize
Log.js:55 SigninState.toStorageString
Log.js:55 WebStorageStateStore.set 57340415234d42948f156a394449ed2a
I am then redirected to the IdentityServer login page .
And after begin logged in , i am redirected again to the initial page (index.html), and this is the log:
Log.js:55 no user storageString
Log.js:55 user not found in storage
From the log, it seems that the WebStateStore initially set, is null after redirection
I have been able to get the user only after processing the signin reponse.
Any idea why?
Is this the correct workflow?
manager.processSigninResponse().then(function (response) {
log("signin response success", response);
localStorage.setItem('oidc.user:' + settings.authority + ':' + settings.client_id, JSON.stringify(response));
}).catch(function (err) {
});
Something about how it's being stored on your callback page -- debug into that.
What are you coding against: Angular2, ReactJS, JQuery, ES7???
ES7.
By debugging i noticed that the "storagestring" was empty when being redirected from the IdServer login page. That s why i m setting manually the localstorage.
Only after setting the localstorage manually, i m able to get the user.
Is this intended to be?
thanks
Below is an angular example, maybe you can use it as a guide.
public userLoadededEvent: EventEmitter<User> = new EventEmitter<User>();
this.mgr.getUser()
.then((user) => {
if (user) {
this.loggedIn = true;
this.currentUser = user;
this.userLoadededEvent.emit(user);
} else {
this.loggedIn = false;
}
})
.catch((err) => {
this.loggedIn = false;
});
Hi
thanks for the piece of code. The problem is that the User is always null, if i don't set the local storage as shown during the Signinresponse.
@brockallen @GDehnke-Turpin @alessalessio
I am currently having the same issue in my AngularJS web application.
My flow is that I call getUser() after successfully authenticating and being directed back to my site.
Then, if getUser returns a null user, I trigger a new login.
In Chrome and IE a new login consistently gets triggered 2 or 3 times before getUser returns user info. In FireFox, I get an infinite loop, with the user always returning as null.
Did anyone figure out what causes this, or what can be done to work around the issue?
Thank you!
As with anything, I'd revert back to the smallest/simplest sample that shows it's working and build up from there.
Hi @brockallen ,
the simplest/smallest example i m using comes directly from the IdentityServer-ClientQuickstart (as pointed out in the beginning).
And it doesn't work if i don't set the localstorage as described.
@alessalessio does it not work in all browsers, or just one?
@brockallen - for me, it looped a couple of times in all browsers, but in FF it looped infinitely. In other words, there was a problem in all browsers, though the looping happens so fast it's hard to see without debugging.
Do the samples work for you? For example, this one: https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Clients/src/JsOidc
The sample works. I ll figure out where's the difference with mine. Thanks
To generate the User object you need to call mgr.signinRedirectCallback(window.location.href). But that won't actually give you the User object, you can either generate the User object afterward with mgr.getUser() or subscribe a callback in advance with mgr.events.addUserLoaded( (user)=>{...} )
The example only works if the user logs in using a button.
If i want to "protect" the whole page and redirect the user to the identity server during page login, adding the line
authorize('openid', 'id_token');
at the end of the app.js file in your JsOidc sample, the app loops until it crashes.
What's the viable solution if a client html page needs to be authorized this way?
Basically everything I know about using this library is here: https://github.com/IdentityModel/oidc-client-js/issues/339
I had the same experience as @alessalessio - worked with the button, but looped when tried to protect entire page.
Am I the only one who think's this is hoaky?
@GDehnke-Turpin
authorization, and the whole user identity game are not simple subjects. also the standards that we are dealing with were written to try and make multiple companies agree on a common standard even though each of them have different versions of how they do the same thing.
and security is a real mother to do right.
so this package and the related OIDC and OAuth stuff are not as simple as we often wish they were.
no this package is not perfect , but the author is not getting paid to write and update it.
and he does some really good work in this area.
@figuerres
This package is meant to be a starter for something that can/could be much better, no company should implement this as a final solution and probably should fork it to a private repo. And yes, doing security is a pain in the arse...
I don't believe anyone is saying he is getting paid for this and it shouldn't be taken as a final solution.
Cheers!
I agree with you @GDehnke-Turpin, and you're not being disrespectful. "user" is still null, getUser() isn't working
Firstly, thank you @brockallen - you've done great work. This library is a big time saver.
@alessalessio
I ended up adding a separate login view / link, with the redirect_uri as a _separate html page_ as in the sample you mentioned and the one @brockallen suggested. That solved the problem. The issue is only when trying to log in using the asynchronous getUser method and setting redirect_uri to my base website url.
Here's what I have for my settings (notice auth.html):
authority: 'https://whatever.whatever.net',
client_id: 'jsclientid',
redirect_uri: __env.siteUrl + 'auth.html',
post_logout_redirect_uri: __env.siteUrl,
response_type: 'id_token token',
scope: 'openid api',
silent_redirect_uri: __env.siteUrl + 'silent-renew.html',
automaticSilentRenew: true,
accessTokenExpiringNotificationTime: 4,
filterProtocolClaims: true,
loadUserInfo: true
And here's what's in auth.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1 id="waiting">Waiting...</h1>
<script src="../node_modules/oidc-client/dist/oidc-client.min.js"></script>
<script src="./scripts/auth.js"></script>
</body>
</html>
And the essential part of auth.js:
new Oidc.UserManager().signinRedirectCallback().then(function () {
window.location = "/";
})
I hope this helps you.
I solved a similar problem by changing the redirect URIs to include a query, and making the page behave differently depending on the query. So there's only one page for all redirects, but it can handle each of them appropriately. (First I tried to distinguish by the query generated by the auth server, but I couldn't find a way to do that.)
I had a similar problem with getUser. In my case, i found solution to problem.
You can look at getUser code (https://github.com/IdentityModel/oidc-client-js/blob/e21b167a30712f9672603e2d7b9478cd5bf4686f/src/UserManager.js).
getUser() {
Log.debug("UserManager.getUser");
return this._loadUser().then(user => {
if (user) {
Log.info("user loaded");
this._events.load(user, false);
return user;
}
else {
Log.info("user not found in storage");
return null;
}
});
}
getUser returns null when this._loadUser () returns null. _loadUser code from same file:
_loadUser() {
Log.debug("_loadUser");
return this._userStore.get(this._userStoreKey).then(storageString => {
if (storageString) {
Log.debug("user storageString loaded");
return User.fromStorageString(storageString);
}
Log.debug("no user storageString");
return null;
});
}
_loadUser returns null when no value in storage found.
See, is value in session storage. If there is nothing there, then it is not surprising that getUser returns null.
Before calling getUser, you need to store the value in the session storage. This is done by following code:
_storeUser(user) {
if (user) {
Log.debug("_storeUser storing user");
var storageString = user.toStorageString();
return this._userStore.set(this._userStoreKey, storageString);
}
else {
Log.debug("_storeUser removing user storage");
return this._userStore.remove(this._userStoreKey);
}
}
_storeUser is called in _signinEnd, and _signinEnd is called in signinRedirectCallback. Do you call signinRedirectCallback anywhere?
All set on this issue -- can we close?
Most helpful comment
Firstly, thank you @brockallen - you've done great work. This library is a big time saver.
@alessalessio
I ended up adding a separate login view / link, with the redirect_uri as a _separate html page_ as in the sample you mentioned and the one @brockallen suggested. That solved the problem. The issue is only when trying to log in using the asynchronous getUser method and setting redirect_uri to my base website url.
Here's what I have for my settings (notice auth.html):
And here's what's in auth.html:
And the essential part of auth.js:
I hope this helps you.