This might be a basic/dumb question, but I don't know the right keyword to Google. What I want is that when I authenticate the user to my web app, they can close the browser, and when they open it back, they can still use my website - they are not logged out (yet).
I have been following the tutorials in IdentityServer docs (https://identityserver4.readthedocs.io/en/latest/quickstarts/2_interactive_aspnetcore.html), and so far I have managed to get the whole IDP-API-Client working. I have inspect the token that I get from IDP, it's valid for 2 weeks, so what am I missing here, why do I get logged out when I close the browser?
My guess is that I need to store the token to the cookie, but how do I save it, and how do I force the web application to always check for the cookie?
The IS4 tutorial has this:
services.AddAuthentication(o =>
{
o.DefaultScheme = "Cookies";
o.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", o =>
{
o.Authority = "https://localhost:5001";
o.ClientId = "mymvcclient";
o.ClientSecret = "mymvcclientsecret";
o.ResponseType = "code";
o.SaveTokens = true;
o.Scope.Add("myapi");
o.Scope.Add("offline_access");
});
I assume that's just creating a cookie, but how do I specify for it to save my token, and read the token from the cookie when user opens my web application?
Yea there is an option called ExpireTimeSpan in your cookie handler, which defaults to 14 days. You can change it to anytime longer than that by just setting a value to it:
...
.AddCookie("Cookies", options => {
options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.SlidingExpiration = false;
})
.AddOpenIdConnect("oidc", options => {
...
options.UseTokenLifetime = false;
...
})...
The above sets the cookie expiration to 30 days, instead of the default 2 weeks.
You also want to make sure the UseTokenLifetime option from the OIDC is set to false, which is the default for now I think. Tokens coming back from the Identity Server, for example, tend to have short lives. If you set it to true, which was default before, then it would override whatever expiration you set earlier.
The best term to look for on this is probably "persistent sessions", or just session management in general. This is something handled by ASP.NET Core, not really IdentityServer. There are several mechanisms to maintain session state on the server, which you'd need for example not to lose all sessions when a server restart happens.
I've had the best luck using the ITicketStore interface, which allows persisting the sessions to a database. The cookie ends up with a session ID which is validated on each request for expiration.
Related
Ok, first question ever so be gentle. I've been struggling with this for about a week now and i am finally ready to accept defeat and ask for help.
Here's what is happening. I have an IdentityServer4 IDP, an API and an MVC Core Client. I am also interacting with 2 external OAuth2 IDPs provided by the business client.
My problem scenario is this:
The user logs in through my IDP(or potentially one of the external ones)
Once the user is in the Mvc Client they hit the back button on their browser
Which takes them back to the login page(whichever one they used)
They reenter the credentials(login again)
When redirected back(either to the MVC in the case of my IDP, or my IDP in the case of one of the external IDPs) i get RemoteFailure event with the message:correlation failed error
The problem seems, to me, to be the fact that you are trying to login when you are already logged in(or something). I've managed to deal with the case of logging in at my IDP, since the back button step takes the user to my Login action on the Controller(I then check if a user is authenticated and send them back to the MVC without showing them any page) but with the other two IDPs the back button does not hit any code in my IDP. Here are the config options for one of the OAuth2 external IDPs:
.AddOAuth(AppSettings.ExternalProvidersSettings.LoginProviderName, ExternalProviders.LoginLabel, o =>
{
o.ClientId = "clientId";
o.ClientSecret = "clientSecret";
o.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
o.CallbackPath = PathString.FromUriComponent(AppSettings.ExternalProvidersSettings.LoginCallbackPath);
o.AuthorizationEndpoint = AppSettings.ExternalProvidersSettings.LoginAuthorizationEndpoint;
o.TokenEndpoint = AppSettings.ExternalProvidersSettings.LoginTokenEndpoint;
o.Scope.Add("openid");
o.Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
//stuff
},
OnRemoteFailure = async context =>
{
if (!HostingEnvironment.IsDevelopment())
{
context.Response.Redirect($"/home/error");
context.HandleResponse();
}
}
};
}
The other one is the same. Since the error is exactly the same regardless of the IDP used, i am guessing it is not something native to OIDC but to OAuth middleware and the code(config options) they share, so i am not going to show the OIDC config on the MVC client(unless you insist). Given how simple the repro steps are i thought i would find an answer and explanation to my problem pretty fast, but i was not able to. Maybe the fix is trivial and i am just blind. Regardless of the situation, i would apreciate help.
I could reproduce your issue.
When the user goes back to the login screen after successfully logging in,
it might well be that the query parameters in the URL of that page are no longer valid.
Don't think this is an issue specific to Identity Server.
You may read
https://github.com/IdentityServer/IdentityServer4/issues/1251
https://github.com/IdentityServer/IdentityServer4/issues/720
Not sure how to prevent this from happening though.
In Visual Studio 2017RC I created ASP.NET Core MVC app with individual user accounts and successfully completed https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins tutorial to attach Google authentication. I'm now logged in via my Google account.
All I did was adding a few lines to the autogenerated code (in Configure method of Startup.cs):
app.UseGoogleAuthentication(new GoogleOptions
{
ClientId = "xxxx.apps.googleusercontent.com",
ClientSecret = "xxxx",
Scope = { "email", "openid" }
});
I now need to get the value of access token which was issued by Google (and stored in cookies by the app). I'll then use it to generate XOAuth2 key to access Google services. For instance, in HomeController's About method (auto-generated by the standard wizard) I want to display the number of unread emails in my inbox. With XOAuth2 key, I can log in my Gmail and proceed from here.
How can I get this token?
- Do I need to store access token in database during initial logging in via Google? If so, any clues how this can be done in the standard wizard-generated ASP.NET Core MVC app?
- Or, maybe I can always read the access token from cookies? If so, how?
Preferably, I'd read it from cookies (it's anyway there) and avoid duplicating this info in database but not sure if this approach is feasible (i.e. if it can be decrypted).
I did this for ASP.NET MVC once but in ASP.NET Core MVC things have changed a lot, the legacy code is of no use anymore.
OK, found it. SaveTokens property does the trick.
app.UseGoogleAuthentication(new GoogleOptions
{
ClientId = "xxxx.apps.googleusercontent.com",
ClientSecret = "xxxx",
Scope = { "email", "openid" },
SaveTokens = true,
AccessType = "offline"
});
I can then get access token in AccountController.ExternalLoginCallback
var token = info.AuthenticationTokens.Single(x => x.Name == "access_token").Value;
How can i get previouse url without magic and in backend?
Now i get it through policies:
module.exports = function(req, res, next) {
if (!req.session.previouseUrls) {
req.session.previouseUrls = [];
}
req.session.previouseUrl = req.session.currentUrl || "/";
req.session.currentUrl = req.url;
req.session.previouseUrls.push(req.session.previouseUrl);
next();
};
but it's uncomfortable. Can i get previouse Url from backend simpler?
If you need to know the history (or just previous page) of the user's page requests purely on the client-side, could you query native HTML5 History?
..or if you need to support older browsers, maybe History.js?
I would be concerned with how you're doing it now for 2 reasons:
It's going to start filling up your session store. This may not be a big issue if you have short-lives sessions or not many users.
It could record not just traditional page views "clicks" but any request. Unless you're very carful about scoping this policy you may end up adding an Ajax call that goes through this policy check and you probably didn't intend for that.
recently I've spent some time digging into AspNet.Security.OpenIdConnect.Server. I use MVC sample as a code base for my client/server apps.
The thing I need to implement now is obtaining a user's profile info from an external provider (Google) and saving the info into the server's database.
What is the right place for getting and saving a profile's info and a proper way to implement it?
Note: since ASOS (AspNet.Security.OpenIdConnect.Server) only handles the OAuth2/OpenID Connect server part, it's actually not involved in the authentication part, and thus doesn't directly deal with the external providers you configure.
To achieve what you want, the best approach is to configure the Google middleware to extract more information from the user object returned in the user profile response and to persist them in the authentication cookie so you can later retrieve them in your application code.
app.UseGoogleAuthentication(options => {
options.ClientId = "client_id";
options.ClientSecret = "client_secret";
options.Events = new OAuthEvents {
OnCreatingTicket = context => {
// Extract the "language" property from the JSON payload returned by
// the user profile endpoint and add a new "urn:language" claim.
var language = context.User.Value<string>("language");
context.Identity.AddClaim(new Claim("urn:language", language));
return Task.FromResult(0);
}
};
});
If the response doesn't include the data you need, nothing prevents you from using context.Backchannel to make another HTTP call to retrieve more data from a different Google endpoint.
I want to check the session expired or not.
SO what i decided is Create an action called IsServerExpired and have it return a json object containing a boolean value, and the redirect url.
SO the java script function will do an ajax request to this action with specified time interval ..
I have some basic questions ..
1.If i send an ajax request ,i think that will refresh the session time . So in effect the session will not expire if i am using this method. am i right ?
If it refreshes how can i check session expire using polling
There is more simple approach to log out user once session expired.
You can save SessionTimeout somewhere on the client side and run client side timer, once timer reach end redirect user to log out url.
Here is example. Model here containts SessionTimeout value.
$(document).ready(function () {
var timeOutInMinutes = #Model;
if(timeOutInMinutes > 0)
{
setTimeout(function() {
window.location =
'#Url.Action("Logout", "Authentication", new {area=""})';
},timeOutInMinutes * 1000 * 60);
}
});
More user friendly way is to show popup that will say that session will be expired wihtin one minute(if session timeout 15 mins then show it after 14 mins), so user will be able refresh page. and continue work.
I think you are confusing between an ASP.NET session and the authentication cookie. I suspect that you are talking about the authentication cookie expiration here. If you have set slidingExpiration to true in your web.config then polling AJAX requests will renew the timeout so they are not suitable. Phil Haack described a very elegant way to detect authentication cookie expiration in AJAX calls in this blog post.