I have a .NET 4.5 application with custom Identity implemented. I log in with my user, and then at some point later on, the application makes me log in again. I have verified that the auth cookie is still alive and not expired. After doing much research, it appears that the SecurityStampValidator is expiring which invalidates my user and forces another login. I have tried numerous changes, but nothing works.
Ideally, what I am looking for is to only use the auth cookie expiration, and completely disable the SecurityStampValidator. I have not found a way to do that.
I've created a custom user and user manager:
public class CustomUser : IUser<int>
public class CustomUserManager : UserManager<CustomUser, int>
This is my startup.cs:
public void Configuration(IAppBuilder app)
{
// create the data access object we are using
app.CreatePerOwinContext(() => new CustomAccountDataAccess());
// create the custom user manager
app.CreatePerOwinContext<CustomUserManager>((opt, cont) => new CustomUserManager(cont.Get<CustomAccountDataAccess>()));
// create the standard signin manager
app.CreatePerOwinContext<SignInManager<CustomUser, int>>((opt, cont) =>
new SignInManager<CustomUser, int>(cont.Get<CustomUserManager>(), cont.Authentication));
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// use cookie authentication
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = TimeSpan.FromDays(500),
Provider = new CookieAuthenticationProvider()
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<CustomUserManager, CustomUser, int>(
validateInterval: TimeSpan.FromDays(500),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),
getUserIdCallback: (id) => (id.GetUserId<int>()))
}
});
}
If I set validateInterval to some short duration like a minute or two, it works as expected, and my user is logged out after that time. I've set breakpoints in GenerateUserIdentityAsync, and it never gets called. If I set it to some high duration as above, then at some point my user gets logged out anyway. I test this by logging in, and then coming back the next day, and my user is logged out.
I've tried turning off SecurityStampValidator in my CustomUserManager, but that had no effect.
public override bool SupportsUserSecurityStamp => false;
Incidentally, I've loaded the same app code on two different web servers. One server runs .NET 4.5, and the other runs the latest .NET (at least 6.0). The server running .NET 4.5 works exactly as I want. The user is logged in, and stays logged in. On the other server, the user gets logged out as described above.
At this point, I have no idea how to change this so my user stays logged in until the cookie expires. I've seen similar issues posted here, but nothing seems to fix this for me.
I finally figured this out. I was running two web apps (one .NET 4.5 MVC, and the other .NET 6.0 Blazor Server), and neither would persist the login. I then came across this ticket:
ASP.Net Core Cookie Authentication is not persistant
Which had this comment: "another thought... Is your server storing cookie decryption keys permanently? If not, whenever the server or the app pool restarts, users will have to login again..."
Sure enough, when I tested an application pool cycle, I lost my authentication. It turns out that my .NET 4.5 application was missing machinekey from the web.config. I added that in, and it fixed the issue of losing my auth for that application.
I was also running a Blazor Server App (.NET 6.0). For that one, it does not use machinekey I needed to add DataProtection in the startup.cs:
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("C:\storagePathtosavekey"))
.SetApplicationName("BlazorApp")
.SetDefaultKeyLifetime(TimeSpan.FromDays(500));
Adding that code fixed auth there. Things seem to be working now, with the auth persisting as I would expect.
Related
Security is one of those things that once you set it up, you kinda forget about it, until something doesn't work. I have a .Net Core application that uses OpenId Connect with Azure. The sign in works fine, but we noticed a few days ago that the signout wasn't working. You could sign out of the application, but then login again directly without entering credentials. So, I have been looking around and found out that it's not good enough to clear the cookies and session, you need to go to the "end_session_endpoint" to actually clear the credentials. I have looked in a number of places, but I can't find a simple code example of how to to this. And the code examples I have tried don't seem to work. When I run locally, I can logout and it says it logged me out, but when I start the application again, I am logged in automatically right away. I understand the concept, I just don't know how to do it. Below is what my authentication looks like in the Startup.cs file:
services.AddAuthentication(options =>
{
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority = Configuration["Authentication:Microsoft:OAuth"];
options.RequireHttpsMetadata = true;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.UsePkce = false;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("email");
options.SaveTokens = true;
options.CallbackPath = new PathString(Configuration["Authentication:Microsoft:Callback"]);
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
};
// MetadataAddress represents the Active Directory instance used to authenticate users.
options.MetadataAddress = Configuration["Authentication:Microsoft:Meta"];
options.ClientId = Configuration["Authentication:Microsoft:ApplicationId"];
options.ClientSecret = Configuration["Authentication:Microsoft:Password"];
});
Does anyone have a straight forward code example for this?
RP INITIATED LOGOUT
This is mostly a case of sending the standard message with these parameters.If I remember rightly, Azure may require the third of these to log you out successfully.
client_id
post_logout_redirect_uri
id_token_hint
EXAMPLES
Here is a C# example that sends this type of request. It uses the older .Net framework, but should be easy to follow.
In .Net Core, you can set a SignedOutCallbackPath in the OIDC properties, which must match a post_logout_redirect_uri registered against the client in Azure AD. You can then call this code in the controller and it should send the standard mesaage.
await HttpContext.SignoutAsync():
Personally I find this .NET syntax and C# layer a little unintuitive, but ultimately it is just a wrapper around the standard messages.
My Azure SPA example does an RP initiated logout, and I can confirm that the above parameters work fine with Azure AD.
ADVANCED OPTIONS
Sometimes logout can be a very tricky area to get the best usability behaviour, depending on the provider. In case useful later, another option is to use the max-age parameter from OpenID Connect during sign in. You then get an auth_time claim in the ID token. On the next redirect after auth_time has expired you can use this data to send a prompt=login parameter if required, to force the user to re-authenticate. The .NET way to do this is described in this post.
UPDATE
Here is what my SPA config looks like, with the post logout redirect URI, to check against your own configuration. RP (relying party) is your C# web app, OP (openid connect provider) is Azure AD (I never liked this type of terminology). The logout mechanics are just focused on removing the SSO cookie that Azure AD sets. Azure AD logout behaviour may not be easy to control fully though, since logout in OAuth technologies can sometimes get complex and involve multiple apps.
TEST OPTION
One option you could try is logging in and out using OAuth Tools, which is useful for troubleshooting this type of problem without needing to write code. Once you get the correct behaviour here, you'll be clearer about what you need to code:
First find your OpenID Connect metadata URL - this is my URL
Next select Add Environment, give it a name like my and enter the metadata URL - then make sure this environment is selected
Add https://oauth.tools/callback/code to Azure AD as a redirect URI
Then select Code Flow in OAuth tools, enter your development Azure AD client ID and client secret, then select Run, to trigger a redirect
Then scroll down and click Redeem Authorization Code, which will get tokens, including an ID token which might be needed for logout
Then run a Logout Flow, for which you'll need to set https://oauth.tools in Azure AD as the front channel logout URL
If you can't get logout to work as you'd like, try the prompt=login option, which will force a new login
Q & A
So OIDC logout is working but a second factor time to live is not. This is outside the OIDC specs and probably cannot be influenced by code. Try it in a new Incognito browser window and see if you have to enter the second factor. If so then it will be a cookie that you may be able to identify if you trace the HTTP traffic. Look for a configuration setting somewhere that controls this - something similar to the Curity TTL setting in this article. If there is no such setting then it may be outside your control.
Leftover browser sessions. In some ways if users let other people use their desktop sessions then all bets are off. You can get too hung up on this. Just aim to be as good as other apps. Eg are you as secure as Office 365 itself, Gmail etc?
By default (without a post logout redirect URI) the user is dumped back in the Azure login screen, which is typically not what the user wants. Instead a post logout landing screen gives your app control over the UX. A common option is to just display something like You are signed out, click here to log back in. In a C# website this would just return a view to the browser.
Its very difficult to ask questions on ID4. Also all relevant discussions I could google point to non-existing links for code samples.
What I have: an angular client uses ID4 auth made up of MVC pages provided by Microsoft, hosted in ASP.NET Core.
Startup.cs
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<AppDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, AppDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
. . .
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
authorize.service.ts
const settings: any = await response.json();
settings.automaticSilentRenew = true;
settings.includeIdTokenInSilentRenew = true;
this.userManager = new UserManager(settings);
Use case: a user logs in on the webpage, ticks Remeber Me checkbox and uses the auth'ed website parts. If the server restarts - the tokens lost and user interaction with the website becomes broken - e.g. there are no errors in the webbrowser console and nothing happens if user tries to access any server-side data. It looks like nothing is there. Not as if there is issue with the auth.
It can only be fixed by manually logout/login. Or by opening new tab and going to the app again.
Is there simple explanation on to how can I a) detect that client token is broken and b) request new token?
EDIT if a user continues to use website next working day or opens new browser tab (without server restart) the ID4 works as expected - no login required.
The fiddler:
The short answer is: nothing should break if the server process (either application backend or identityserver4-based IDP) restarts so chances are you're missing some persistence (for persisted grants) and/or shared config (e.g. token signing keys and ASP.Net data protection keys) and things are being regenerated on startup and in-memory data is being lost.
This article covers the things you need to consider to deploy a viable production service: https://docs.identityserver.io/en/latest/topics/deployment.html
With that all in place though tokens will still expire and there are a couple of ways to refresh them depending on the context and grant type in use.
To detect if a token is not valid anymore:
Use it and detect if you get a 401 response from the endpoint you're calling
Check the exp claim inside the token yourself
Use the expires_in value returned with the token and calculate the expiry time based on that
To renew it (and some libs will automate this):
Use the iframe-based silent renewal mechanism (authorize endpoint with prompt=none) - note that third part cookie restrictions come into play for this
Use a refresh token via the token endpoint (not recommended for client side apps due to the need to persist a refresh token in the client side)
I have made a C# Asp.net Core application it's working fine. But problem is that it will automatically logout user after 10 to 20 minutes or after one operation is over then automatically user logout from the system.
I am using Azure database.
Most login schemes on .NET CORE use CookieAuthentication somehow. Most likely your problem lies there.
If you check out the documentation about cookie authentication, you'll see that you can set a timespan for which the cookie should be valid.
cookie options
app.UseCookieAuthentication(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
});
Should fix your problem.
You can also make it "sliding", that means your cookie will get extended when half of the timespan has passed and the user makes a new request.
I've been searching for an answer to this for a while but the problem seems quite complex and I'm struggling to find an answer.
I'm a beginner software developer working for a start up company and have just completed the first version system for use by multiple users. Locally Testing the software had no problems, but since publishing the software to a windows 2012 server on iis I have found a major problem with the Login system.
When the program is uploaded initially multiple users can log in and use the program with no problems, however (seemingly) at random the login system completely stops functioning on all computers that are currently logged out. Those who are logged in can logout and log back in with their account or any other, but those who were logged out at this moment complete lose access to the system.
When using the developer tools on Chrome it appears that all these computers completely stop generating the cookie created when logging in and just redirect back to the login screen.
The systems still recognise incorrect logins and it happens with different computers each time I upload the program.
I appreciate that this is a very vague question, but I'm pulling my hair out over it!
As I said I am a beginner and am completely new to hosting on business servers and don't have much experience with Identity or Login systems in general so any help is much appreciated.
I mainly want to know is the problem most likely iis, if so where in iis should I be looking? Or the servers security settings?
Is there an efficient why to debug this while its running on the server?
If the problem sounds like a coding issue where identity files have been edited let me know what class it could be and Ill post the code.
Thanks!
Edit:
Global.asax.cs
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
WebApiConfig.Register(GlobalConfiguration.Configuration);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//Creates roles and adds an admin on first start
RoleCreator rc = new RoleCreator();
rc.CreateRoles();
rc.AddAdmin();
}
}
Startup.Auth.cs
public partial class Startup {
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(UnitContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName="TrackerCookie",
LoginPath = new PathString("/Login/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
Problem is now Solved.
For anyone with the same problem, the issue is caused by a bug called 'katana bug #197'.
The easiest fix is to download 'kentor.OwinCookieSaver' NuGet Package. and add app.UseKentorOwinCookieSaver(); above your Application cookie config in startup.
https://github.com/KentorIT/owin-cookie-saver
// kentor.OwinCookieSaver for 'katana bug #197' (login cookies being destroyed on logout!)
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName="LoginCookie",
LoginPath = new PathString("/Login/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Microsoft are aware of the issue and it will be resolved in 2015.
The ASP.NET team has shipped new samples showing how to use the identity packages. They are contained in the following nuget package: Microsoft Asp.Net Identity Samples
The samples are very helpful, but there have been loads of changes from how things were done originally in the templates that shipped.
My specific question: In the original SPA template, there was the following code:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
...
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
But in the new samples in the nuget package, that code is gone, and in its place is this code:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(validateInterval: TimeSpan.FromMinutes(30), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Can anyone help me understand the difference between app.UseOAuthBearerTokens and app.UseCookieAuthentication (and why this change was made)? They both seem to allow the application to behave in the same way and I could use some clarification on this change.
Thanks...
-Ben
OAuth is used for API security, IN API you would not use cookies and this is essentially stateless. However on say a normal MVC or forms website, you would use a normal session cookie. unless you are creating an API I would not worry about OAuth, and just go with the traditional cookie based auth.
If you want to create an API, then you need to, and I would say MUST DO OAuth for your validation. You would then send a token with your request, post, put, delete. This token is decoded backend by the handlers to reveal permissions, User ID etc
Thought it would be best to extend this and explain the problems, and why OAuth solves it.
Usually an api would be on a separate domain to the UI, be that an APP, Website etc. If you did manage to be given a cookie from an API ( for example facebook.com ) You would only be able to use this cookie on facebook. But your website would be www.myblog.com. There are settings in Ajax to enable the passing of cookies with ajax request, however the domain must be the same, and this is still rather sketchy.
So Oauth Is born, essentially creating what could be best described as a string based cookie, that can be stored however you like, so long as it is passed back, with your requests, within the request headers.
You can in browser applications use javascript to create a cookie, and save your token within this cookie. This would allow you to take advantage of the persistent storage. However it is probably better to use available local storage. So for a browser based app, this would be LocalStorage API, for desktop apps you could use temp storage, local db, etc. and phone apps will have something similar.