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.
I'm trying to sign out from intranet application, which is also accessible from the internet with signing in using Active Directory Federation Services.
Firstly I just made a href to https://federation.mycompany.com/adfs/ls/?wa=wsignout1.0. The federation page shows and displays a message: "You have been successfully signed out!" But if I go back, I can access the web app again without signing in again. I also tried to append a redirect parameter, so that the user would be prompted to insert credentials again. But the redirect does not happen.
Later on I created a Action in one of my controllers. Here is the code:
public ActionResult Logoff()
{
string absoluteUrl = HttpContext.Request.Url.AbsoluteUri;
string replyUrl = absoluteUrl.Substring(0, absoluteUrl.LastIndexOf("/")+1);
WSFederationAuthenticationModule.FederatedSignOut(new Uri(#"https://federation.mycompany.com/adfs/ls/?wa=wsignout1.0"), new Uri(replyUrl));
return null;
}
But the problem is the same.
The third thing I tried was to delete cookies before signing out. But it seems that authentication cookie is under domain and not my app, so it is not accessible.
How do I solve this Sign out issue?
Please help.
I am only assuming but I suspect that your ADFS is configured for windows authentication while your application has the Login button.
If this is the case then you can't really log out from already authenticated ADFS and it happily reissues the token upon the next request from the application. You can't do anything about it since the authentication is performed with MTML/Kerberos and this is the browser that keeps your credentials until you close it.
The solution would be to change ADFS to Forms mode and see if it helps.
I have an application using the OWIN middleware for OpenIdConnect. The startup.cs file uses the standard implementation of app.UseOpenIdConnectAuthentication. The cookie is set to the browser, but it errors with:
IDX10311: RequireNonce is 'true' (default) but validationContext.Nonce is null. A nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'.
I've found that when running fiddler as I do for most debug projects this behavior happens. The error is returned, but if I go back to the site everything is working and my user is authenticated. Has anyone seen this behavior when running fiddler?
With fiddler:
SecurityTokenValidated notification in OpenIdConnect is executed twice.
After the second pass through the IDX10311 error is thrown
Browser contains the valid cookie, going back to the page I can view the valid User.Identity data.
Running without fiddler:
SecurityTokenValidated executes once in OpenIdConnect
No error thrown, proceeds to load up controller action for post authentication redirect Uri
Cookie also valid and User.Identity data correct.
Ideas? I can get around it without running fiddler, but when debugging it would be nice to also run fiddler to inspect traffic.
Maybe is this the cause?
Hello there, I think I found the root cause of this issue.
I'm summing up my discoveries:
The problem is in the OpenIdConnect.nonce.OpenIdConnect cookie
This cookie is set from the app (let's call this "ID Client") as soon as the OpenID Middleware init an authentication session
The cookie should be sent back from the browser to the "ID Client" as soon as the authentication has been completed. My assumption is that this cookie is needed to have a double check from the ID client point of view (i.e. did I really started an OpenID Connect authorization flow?)
A lot of confusion in me was caused by the "Nonce" term, used both in this cookie and in the OpenID Connect flow from the ID server.
The exception, in my case, was caused by the missing cookie (not the nonce of the ID Server), simply because it wasn't sent by the browser back to the "ID client"
So the main root, in my case, was this: OpenIdConnect.nonce.OpenIdConnect cookie was not sent back to the ID Client by the browser. In some cases (i.e. Chrome, Firefox and Edge) cookie was sent correctly, while in others (IE11, Safari) it wasn't.
After a lot of research, I discovered that the problem was on the Cookie restriction policy, defined on the browser. In my case, the "ID client" is embedded in an <iframe>. This cause the "ID Client" to be seen as a "third-party client", because the user didn't navigate to that URL directly in the main window. Because this is a third-party, for some browsers, it's cookies have to be blocked.
Indeed the same effect may be obtained on Chrome, by setting "Block third-party cookies".
So, I have to conclude that:
a) If iframe is a must (as in my case, because "ID Clients" are apps that must run inside the graphic content of the our main platform app), I think the only solution is to intercept the error, and handle it with a page, asking the user to enable third party cookies.
b) If iframe is not a must, it should suffice opening the "ID Client" in a new window.
Hope this helps somebody, because I got crazy!
Marco
I had the same problem but switching back the Microsoft.Owin.Security.OpenIdConnect to version 3.0.1 solved the issue
For anyone else who gets here in 2021, you'll likely get this issue if:
You're redirecting http -> https
Or you've changed your app's host domain.
Both of these aren't an issue with the middleware or your app, but it's about the combination of two issues:
The fact that your app is still hosted on the old old domain or protocol. You want to prevent browsers hitting that by implementing a redirect on the web server.
The redirect URI (sometimes know as reply URL) in Azure or whichever OpenIdConnect authorization server you're authenticating with. You want to get this updated to the new protocol or domain.
Our example: We had https://old.example.com/app/ that was now also hosted at https://new.example.com/app/. We wanted users' previous bookmarks to still work.
Our solution:
We updated the redirect URI (reply url) to point to the new domain for the app (https://new.example.com/app/signin-endpoint). Ideally, make sure there is only one URI listed for your app and that it's https.
We added the new domain binding to the site in IIS (we're old school, but do the same for your hosting of choice 😊)
We added an IIS redirect to the new domain (new.example.com) so that users' bookmarks still work. Again if you're not on IIS, implement a permanent redirect in the web server of your choice.
Until we had the final step above, we were seeing the error in the OP's post. It's the same process if you're forcing http -> https.
Here is the IIS re-write for those who are also "old school":
<rewrite>
<rules>
<rule name="Redirect old.example.com to new.example.com" enabled="true" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{HTTP_HOST}" pattern="old.example.com" />
</conditions>
<action type="Redirect" url="https://new.example.com{REQUEST_URI}" />
</rule>
</rules>
</rewrite>
It goes in the <system.webServer> section of your web.config file. Enjoy!
For me changing reply url in Azure active directory works.
This happens when you enable SSL because it changes only the sign on URL to the HTTPS URL while the reply URL remains the same HTTP URL.
When you try to access your app using the https URL, it sets a cookie with a unique number(nonce) in your browser and hits Azure AD for authentication. After authentication, the browser has to give access to that cookie. But since the sign on URL and reply URL are different the browser does not recognize your app and does not give access to that cookie and hence the application throws this error.
I know its an old post but I had this problem and nothing was working for me, after lose my mind behind a solution to make my enterprise application works I end up fixing it by setting the multi-tenanted option to yes in azure (in Azure select: app registration>settings>properties, set multi-tenanted to yes and click save).
hope it helps someone, couldn't see nobody mentioning it.
I noticed this error when running IIS Express in the background when I had switched to hosting in full IIS. When I disabled the IIS Express, my error went away.
A cookies rewrite rule in the web.config to ensure samesite cookies gave this cryptic exception. Disabling that rule solved it.
A temporary solution which worked for me for an app secured via Azure Active Directory was to signout (by going to the sites/Account/SignOut page) and then I was able to return to the home page and sign in ok. Hope this helps someone.
I know it's been a while on this one. My specific issue was with the IDX10311 error in relation to authenticating with IdentityServer while Fiddler (traffic inspector proxy) was running. I added a custom owin middleware to catch and absorb the IDX13011 in the case where the hostname contained "localhost". Ignoring this exception allowed us to use the site with fiddler as a workaround. I think it causes breaks in the authentication process though where we have to press enter in the browser address bar on the callbacks to get it going again, but this only affects development.
Here's the invoke method we used in the middleware to absorb the error. I should note though that we have seen this error in production occasionally as well. No explanation for a cause, but I have a feeling it is related to users on IE browsers.
public override async Task Invoke(IOwinContext context) {
try {
await Next.Invoke(context);
} catch (Exception ex) {
_errorHandling = new ErrorHandling();
if (ex.Message.Contains("IDX10803")) {
//do something here to alert your IT staff to a possible IdSvr outage
context.Response.Redirect("/Error/IdSvrDown?message=" + ex.Message);
} else if(ex.Message.Contains("IDX10311") && context.Request.Host.Value.Contains("localhost")) {
//absorb exception and allow middleware to continue
} else {
context.Response.Redirect("/Error/OwinMiddlewareError?exMsg=" + ex.Message + "&owinContextName=" + lastMiddlewareTypeName);
}
}
}
For me it was a different problem. My site was working with both of the urls below
https://www.example.com
and
https://example.com
But my redirect url was https://www.example.com.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigurationManager.AppSettings["ClientId"].ToString(),
Authority = ConfigurationManager.AppSettings["Authority"].ToString(),
RedirectUri = ConfigurationManager.AppSettings["RedirectUri"].ToString();//https://www.example.com
}
Users who use https://example.com the mentioned exception occurs.
The cookie generated for www.example.com and example.com are different. So after the login when it redirects, the cookie doesn't contain the correct nonce to validate and the exception occurs.
The solution for the problem is to set the redirect URL dynamically
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigurationManager.AppSettings["ClientId"].ToString(),
Authority = ConfigurationManager.AppSettings["Authority"].ToString(),
RedirectUri = ConfigurationManager.AppSettings["RedirectUri"].ToString(),//https://www.example.com
,
// sample how to access token on form (when adding the token response type)
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = async n =>
{
var uri = n.Request.Uri; //From request URL determine the RedirctUri and set below
n.ProtocolMessage.RedirectUri =""//Set the url here
}
}
}
The same issue can happen with https://www.example.com and http://www.example.com
Users where getting this issue when Edge was set to IE compatibility mode, removed it from IE compatibility and it solved the issue. Setting / list of sites is controlled under edge://compat.
I end up allowing the Owin to skip to the next Middleware on AuthentificationFaild callback function. I'm checking if the error message contains nonce error Id and call SkipToNextMiddleware function from the context. With that, I'm restarting the sign-in process so if the user cookies were not set there will be a second call that will set the cookie.
The code is written in vb.net
Dim oidcAuthOpt= New OpenIdConnectAuthenticationOptions()
oidcAuthOpt.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = Function(n)
If (n.Exception.Message.StartsWith("OICE_20004") Or n.Exception.Message.Contains("IDX10311")) Then
n.SkipToNextMiddleware()
Return Task.FromResult(0)
End If
Return Task.FromResult(0)
End Function
}
I have an intranet application using OWIN self hosting to serve a SPA/WebAPI 2 type site. Everything works great as localhost:port, and IP:port, but hostname:port fails for IE and Chrome. Yet the application works with FireFox.
I believe this is due to the integrated authentication. Chrome returns ERR_INVALID_AUTH_CREDENTIALS, and IE returns 400 bad request. Fiddler doesn't show much difference in the headers of the request/response, except that the Authentication comes through and I get a 200 in FF, a 400 in IE and a 401 in Chrome.
Most of my code was "borrowed" from SO articles or MSDN tutorials.Here's my Program.cs:
string baseAddress = "http://*:9000/";
using (WebApp.Start<Startup>(new StartOptions(baseAddress) { ServerFactory = "Microsoft.Owin.Host.HttpListener" }))
{
// Keep server operational till stopped
Console.WriteLine("Started, Press any key to stop.");
Console.ReadKey();
Console.WriteLine("Stopped");
}
Here's my startup.cs:
public void Configuration(IAppBuilder appBuilder)
{
var listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
appBuilder.UseWebApi(config);
appBuilder.UseFileServer(new FileServerOptions
{
RequestPath = new PathString(string.Empty),
// path to static files.
FileSystem = new PhysicalFileSystem("./public"),
EnableDirectoryBrowsing = false
});
}
I also made sure to set the url reservation using netsh:
netsh http add urlacl url=http://*:9000/ user=Everyone
Both IE and Chrome attempt the intranet model of Integrated Authentication and attempt to send the credentials immediately in all of our intranet applications. FF always prompts for credentials and never auto-authenticates. This has me leaning towards authentication as the problem.
Does anyone have an idea for what I can check to validate the authentication side of things? Fiddler didn't illuminate the request very well. Breakpoints never seem to get touched in the app when requesting against the host.
I think there could be a group/local policy that is getting in the way of proper authentication, but I'm not certain where to start looking.
*edit - updated tags
*update
Some further testing indicates that forcing auth to NTLM (versus integrated) allows Chrome to auto-logon, FF to work the same as it always did, and IE in a weird place.
IE will autologon, but then fail to load the JS on the page, or Prompt for a user and then login properly depending on FQDN vs shortname. I believe this is due to the default IE behaviors and trusted/intranet site settings.
I now have a work around, but it'd still be nice to figure out kerberos if anyone has any ideas.
Change the code to
HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
In Self Host Web API, it solved the issue.
I want to use the domain: aaaa.com to have a login form for the site at domain: cccc.com.
Note, I have full control of the server at cccc.com and have setup CORS on the server at cccc.com. I essentially have full control of the server at aaaa.com as well.
I am using jquery's $.ajax to send a POST to the cccc.com asp.net mvc 3 server. It looks like I get the right response back and I see the ASP.NET_SessionId and .ASPXAUTH cookies in the response. When I get the correct response in javascript with no login errors, I want to redirect to cccc.com/Home/Index using window.location. Everything seems to be working up to this point. Authentication, getting a correct response, etc. However when javascript redirects, cccc.com still wants me to login again. Why is this happening?
Is it because the authentication cookies belong to aaa.com? How can I work around this?
Thanks
Yes, the authentication cookies will belong to the other site, and are not shared.
If you had a subdomain of cccc.com instead of a completely separate domain, it would work if you set a domain-wide cookie.
As it is though, you will have to copy the cookie upon login, logout, and any other authentication methods that modify how the cookie is stored. If you're on a different server, you would also lose your ability to do sessions unless you have a session state server.
You could try copying the auth cookies with javascript after your POST to log in completes.