ADFS RelayState issue - adfs

I am using PingFederate for SP-initiated SSO and ADFS 3.0 for user authentication. It displays a login page where user enters credentials. This is failing only if the login page is idle for 10 or more minutes and then when users clicks on sign-in, the below error message shows up. If the login is before 10 minutes, there are no issues. I have noticed that the login page url has a RelayState guid. During successful authentication, it creates MSISContext cookie along with RelayState guide attached to it. When it errors out, from what I have read online, the response comes back to ADFS, ADFS checks for a cookie with the new RelayState name that doesn't exist. What can I do to fix this issue when the login page is idle for 10 or more minutes. Is there a timeout value that I can change somewhere?
Encountered error during federation passive request.
Additional Data
Protocol Name:
Saml
Relying Party:
Exception details:
Microsoft.IdentityServer.Web.CookieManagers.InvalidContextException: MSIS7001: The passive protocol context was not found or not valid. If the context was stored in cookies, the cookies that were presented by the client were not valid. Ensure that the client browser is configured to accept cookies from this website and retry this request.
at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolHandler.GetOriginalRequestFromResponse(ProtocolContext context, Boolean deleteCookie)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.ProcessProtocolRequest(ProtocolContext protocolContext, PassiveProtocolHandler protocolHandler)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.OnGetContext(WrappedHttpListenerContext context)

RelayState gets added to the name of the cookie. If the relay state changes, the response will check for a cookie with the new RelayState name that does not exist. Make sure that it has not changed and ensure that you enabled RelayState on your ADFS.
There are different service properties you may also need to include depending on your Windows Server version. See the threads below:
AD FS in Windows Server 2012 R2, for instance, includes a %systemroot%\ADFS\Microsoft.IdentityServer.Servicehost.exe.config file. If that is what you are using, create an element with the same syntax as the web.config file element: <useRelayStateForIdpInitiatedSignOn enabled="true" />. Include this element as part of <microsoft.identityserver.web> section of the Microsoft.IdentityServer.Servicehost.exe.config file.
https://social.technet.microsoft.com/Forums/Lync/en-US/8d692a29-92e0-47e0-be70-d7f9335ab95a/adfs-30-relay-state-issue?forum=winserverDS
https://social.msdn.microsoft.com/Forums/en-US/25239ff7-a33d-4f3e-a7a8-5a3c47d733f7/relaystate-support-in-adfs-30?forum=Geneva
https://nzpcmad.blogspot.com/2016/01/this-post-follows-on-from-idp-initiated.html

Related

How to use end client state parameter in IdentityServer 3?

I have configured IdentityServer 3 to use external IdentityProvider which is pointing to AAD.
As of now, when I send a request to IdentityServer, I am properly redirected to the AAD for login, however, the 'state' parameter that I am sending to IdentityServer is overridden, and the value of OpenIdConnect.AuthenticationProperties is encrypted and sent to the AAD as the state in the query string.
For eg:
https://localhost:44333/idpaad/connect/authorize?client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&redirect_uri=https://localhost:44394/&response_mode=query&response_type=code&scope=openid%20email&state=9b0e82c3-e623-42f1-bede-493243c103e7
Here,
https://localhost:44333/idpaad/connect/authorize -> IdentityServer endpoint
state=9b0e82c3-e623-42f1-bede-493243c103e7 -> client generated GUID sent as querystring.
when I see in the "RedirectToIdentityProvider" middleware in the StartUp.cs of IdentityServer OpenIdConnectAuthenticationNotifications, the value of state is updated to
OpenIdConnect.AuthenticationProperties=(protected values) instead of the GUID and the same is also returned as a query string back to the Redirect URI.
enter image description here
Is there a way to send the original state and not override it by IdentityServer3?
While using wsFederation, I am not getting this issue and the same is forwarded directly to the IdP.
Any help is deeply appreciated.
Most of the time it's advisable for an Azure Active Directory integrated application to maintain an application state when sending request to Azure AD for login. And the recommended way to achieve this is to use the ‘state’ parameter as defined in the OpenID Connect standards.
If you check this document form OpenID, you will find that primary reason for using the state parameter is to mitigate CSRF attacks.
RECOMMENDED. Opaque value used to maintain state between the request and the callback. Typically, Cross-Site Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this parameter with a browser cookie.
The ‘state’ parameter is used for both preventing cross-site request forgery attacks and to maintain user’s state before authentication request occurs.
In an ASP.NET or ASP.NET CORE web application using OpenID Connect OWIN middleware, the ‘state’ parameter is maintained automatically by the middleware when sending out an authentication request, this is the only reason you are seeing the state parameter getting overridden in your case.
But if you want you can add custom data in your state parameter. Use the following code in OpenIdConnectNotifications’s RedirectToIdentityProvider event to inject custom data into the ‘state’ parameter.
var stateQueryString = notification.ProtocolMessage.State.Split('=');
var protectedState = stateQueryString[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.Add("MyData","123");
notification.ProtocolMessage.State = stateQueryString[0] + "=" + notification.Options.StateDataFormat.Protect(state);
Check this document and Microsoft identity platform and OpenID Connect protocol for detailed information.

Asp.net Core 2 Identity with IdentityServer4 and email confirmation: Correlation failed. cookie not found

I'm using IdetityServer with asp.net core identity. The issue is that i want a user to confirm his email after registration. To implement that i used guide from asp.net core website, so it's pretty much standard.
So user receives email with a link, which points to ItentityServer. After user clicks a link IdentityServer verifies token, finalizes registration and asks user to log in. And here, after the login, user is redirected to actual website, where he gets the error.
As i understand, website with oidc middleware expects special correlation cookies from IdentityServer which are obviously missed from the response since it initially came from confirmation email..
Maybe anyone faced with such case?
warn: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[15]
'.AspNetCore.Correlation.OpenIdConnect.3jB4rPx9WvoggXG4jjvHMcvub3BxPBU_tQN
zGyIH9KM' cookie not found.
info:
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[4]
Error from RemoteAuthentication: Correlation failed..
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0]
An unhandled exception has occurred while executing the request
System.Exception: Correlation failed.
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.
<HandleR
equestAsync>d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.
<Invoke>d__6.
MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
at Microsoft.AspNetCore.SpaServices.Webpack.ConditionalProxyMiddleware.
<Invoke>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
at Microsoft.AspNetCore.SpaServices.Webpack.ConditionalProxyMiddleware.
<Invoke>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.
<Invoke>
d__7.MoveNext()
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 265.0674ms 500 text/html; charset=utf-8
Rewritten to minimize client-app dependencies:
As noted below, OIDC requires that the client app initiates the login flow. Apart from defining your auth scheme in Startup.cs, the bare-minimum involvement with auth is going to be [Authorize] attributes on your controllers or handlers, but with a little extra data, you can leave the verification up to Identity Server.
I'm actually doing the same thing on a project I'm working on, to a point. First, I make the client app display a single link reading Register or Login. This way it's logical for users to start an OIDC flow to Identity Server regardless of whether they have an account yet.
The reason you want to register in the context of an OIDC login flow is that Identity Server is designed to service many clients. When your user starts a new session by clicking the validation link in email, that new session needs to know which client to contact upon successful validation. The OIDC context that Identity Server normally relies upon simply doesn't exist, so that's what you need to work around.
So, registering within the context of an OIDC flow from a known client, at the same time you generate the email-verification token, also store a redirect URL pointing back to an [Authorize]-protected URL on the client. Store the URL to the database alongside the new verification token. (For a completely "clean" implementation, you can add it to the Properties dictionary of the Identity Server Client in the ClientStore, and look it up at runtime by injecting that and the IdentityServerInteractionService. There are good examples of using that in the Identity Server Quickstart UI).
When the user clicks the link (new session) and Identity Server verifies the token, send the user to the redirect URL stored in that same record earlier. When the user's browser requests the page from your client app, your client's auth middleware will respond to the [Authorize] attribute by automatically starting a new OIDC login sequence, sending the user back to Identity Server for login.
Depending on how you set up the cookie expiration and whether you let the user's account remain logged in while verifying, this flow may result in transparent login (no UI presented). But if you're using the default session-expiration cookies, or if the login actually expired, or a few other edge-cases arise, the user will be presented with a regular login request. (With even more work, you could guarantee a transparent login for local accounts, but not for third-party auth.)

HTTP Error 401.1 when using WinHttp.WinHttpRequest.5.1 in classic ASP site

General information
Operating System: Windows Server 2003 R2 Service pack 2
Webserver: IIS 6
NTAuthenticationProviders: NTLM only
Webapplication: Classic ASP
Browsers used: IE7, IE8, IE9
There’s a Classic ASP web application called knowledgebase, within an IIS website called eblcplaza like so: eblcplaza/knowledgebase/.
eblcplaza has anonymous access AND Integrated Windows Authentication enabled.
knowledgebase has anonymous access disabled and Integrated Windows Authentication enabled
knowledgebase is a Classic ASP application has its own Application pool which runs under the predefined Application pool identity “Network service”
When I’m logged in with my NT account I can access any page I want just fine. The problem is with the WinHttp.WinHttpRequest.5.1 component. It’s used in some parts of knowledgebase to do a server side request to retrieve content from some .asp scripts which reside within the web application.
The problem started when Anonymous access was turned off on knowledgebase . Note, turning it back on is not an option.
Example of a request using WinHttpRequest:
set WinHTTPRequest = Server.CreateObject("WinHttp.WinHttpRequest.5.1")
WinHTTPRequest.SetTimeouts 20000, 20000, 20000, 20000
call WinHTTPRequest.Open("POST", someUrlToAspScript, false)
WinHTTPRequest.SetAutoLogonPolicy 0
WinHTTPRequest.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
WinHTTPRequest.Send strQueryString
Response.Write(WinHTTPRequest.ResponseText)
With SetAutoLoginPolicy set to 0, I get the following error message on the pages where WinHttpRequest is used:
You do not have permission to view this directory or page using the credentials that you supplied.
HTTP Error 401.1 - Unauthorized: Access is denied due to invalid credentials.
Internet Information Services (IIS)
With SetAutoLoginPolicy set to 2 (Do not automatically send user credentials according to MSDN), I get the following error message on the pages where WinHttpRequest is used:
You do not have permission to view this directory or page using the credentials that you supplied because your Web browser is sending a WWW-Authenticate header field that the Web server is not configured to accept.
HTTP Error 401.2 - Unauthorized: Access is denied due to server configuration.
I know for a fact that my NT user account has the proper rights to access those .asp scripts and so does the Network Service account.
I tried figuring out what could be the problem for several days know, tried setting the NTAuthenticationProviders to only NTLM and both Negotiate and NTLM amongst other things, but nothing worked so far.
Please help me out, It’s starting to drive me crazy.
Regards,
Bart
I guess the pages in knowledgebase are accessed with the anonymous account where you start from at eblcplaza. Try to enable NTLM only on the page in eblcplaza where you use the request, you can do that on that file only. Like that your credentials get passed to knowledgebase. On both pages log the Session("username") variable.
First of all let's clear up what it is you asking the server to do. It will have demanded your credentials from the client with which it is now impersonating you for security purposes. The WinHTTP request it is making to a service (WinHTTP doesn't know that its the exact same application) that now demands credentials. What you want this impersonating thread to do is use your creds to authenticate against an "external" service.
I suspect that what is happening here is that the server is not cleared to re-use your credentials in this way. If I recall correctly (which may not be that certain) a server needs to be granted the right to delegate in order to do that. It may also be possible to allow this if Kerberos is used instead of NTLM to perform windows integrated security.
However all that may be academic. You should understand that an app making a http request to itself has the potential to hang when under load in a way that would require a recycle to release.
Consider this alternative. Given that ServicePage.asp is a page used both directly by the browser and by an internal ClientPage.asp do the following.
Rip out the service code from ServicePage.asp and place in a VBScript class in a new ServiceInclude.asp. Now add the this ServiceInclude.asp as an include file in ServicePage.asp where ServicePage.asp only contains the plumbing necessary to instance the class and use it to generate its output.
Modify ClientPage.asp so that instead of attempting WinHttp to ServicePage.asp it simply includes the ServiceInclude.asp, instances the contained class and uses the class to provide the service required.

ASP.NET Forms Authentication failed for the request. Reason: The ticket supplied has expired

I am getting this error many times in the event log and users are logged out.
Event code: 4005
Event message: Forms authentication failed for the request. Reason: The ticket supplied has expired.
Event time: 3/10/2011 3:35:22 PM
Event time (UTC): 3/10/2011 8:35:22 PM
Event ID: fc2f70cc85014b0ca7dbb01471617b66
Event sequence: 3392
Event occurrence: 1
Event detail code: 50202
Thoughts:
I am not using web forms.
I do not think the app pool is recycling.
I compared the Process ID in several events and it is equal.
My machine key is not AutoGenerate.
AS Scott mentioned here http://weblogs.asp.net/scottgu/archive/2010/09/30/asp-net-security-fix-now-on-windows-update.aspx
After windows installed security update for .net framework, you will meet this problem.
just modify the configuration section in your web.config file and switch to a different cookie name.
Sounds like an error you would get when your forms authentication ticket has expired. What is the timeout period for your ticket? Is it set to sliding or absolute expiration?
I believe the default for the timeout is 20 minutes with sliding expiration so if a user gets authenticated and at some point doesn't hit your site for 20 minutes their ticket would be expired. If it is set to absolute expiration it will expire X number of minutes after it was issued where X is your timeout setting.
You can set the timeout and expiration policy (e.g. sliding, absolute) in your web/machine.config under /configuration/system.web/authentication/forms
Here is a good article from Microsoft http://www.iis.net/learn/troubleshoot/security-issues/troubleshooting-forms-authentication that covers various cases and scenarios.
I've had the same issue after using a web.config from another machine. The problem was related with an invalid MachineKey. To solve the problem, I modified the web.config to use the correct MachineKey of my server.
This MSDN blog post shows how to generate a MachineKey.
I was getting this same error, in our case it was caused by a load balancer. We hade to make sure that the persistance was set to Source IP. Otherwise the login form was opened by one server, and processed by the other, which would fail to set the authentication cookie correctly. Maybe this helps someone else

Can I abandon an InProc ASP.NET session from a session different than one making the request?

We have an application that does single sign-on using a centralized authentication server (CAS). We'd like to do single sign-out, such that if the user logs out of one application (say a front-end portal), the user is automatically signed out of all applications using the same single sign-on ticket.
The expectation would be that each application would register a sign-out hook (URL) with the CAS at the time of logon to that application. When the CAS receives the sign out request from one of the applications, it invokes the sign-out hook for all the application sharing the SSO ticket.
My question is this: is there a way to abandon an InProc session from a different session? I presume, since the HTTP request will be coming from the CAS server, that it will get its own session, but it is the session of the user that I want to terminate. I have pretty good idea of how to do this using a separate session state server, but I'd like to know if it is possible using InProc session state.
Haha, well... It looks like you can. I was wondering myself if there was any way to do this, turns out, there is.
When you use InProc, the InProcSessionStateStore (internal class) persist the session state in an internal (non public) cache. You can access this cache through reflection and remove the session state manually.
using System;
using System.Reflection;
using System.Web;
object obj = typeof(HttpRuntime).GetProperty("CacheInternal",
BindingFlags.NonPublic | BindingFlags.Static)
.GetValue(null, null);
if (obj != null)
{
MethodInfo remove = obj.GetType()
.GetMethod("Remove", BindingFlags.NonPublic | BindingFlags.Instance,
Type.DefaultBinder, new Type[] { typeof(string) }, null);
object proc = remove.Invoke(obj, new object[] { "j" + state.SessionID });
}
The end result is, that the next request will take on the same SessionID, but the HttpSessionState will be empty. You'll still get the Session_Start and Session_End events.
After doing a bit of digging around and considering the answers provided so far I've come up with an alternative that lets me continue to use InProc session. Basically, it consists of extending the HttpModule that already handles single sign-on to detected CAS sign outs and redirect the browser to the application sign out page.
Outline:
Sign-On:
For each new single sign-on request, create a new SSO cookie and encode a unique value in it to identify the session (not the session id, so it isn't leaked).
Construct the the sign-out callback url, encoded with the identifier, and register it with the CAS server.
Sign-Out:
When a sign-out request is received from the CAS server, decode the identifier and store it in an application-wide cache. This needs to be pinned in the cache at least long enough for the session to expire naturally.
For each request, look for the SSO cookie and check its value against the cached, signed-out session identifiers. If there is a hit, remove the SSO cookie and redirect the browser to the application's sign-out url.
For each sign-out, check to see if there is an SSO cookie, if so, forward the sign-out request to the CAS. In any event, abandon the user's session, and sign them out of the application.
Page_Load:
Check for the presence of the SSO cookie. If there isn't one, redirect to the sign out page.
No can do.
http://forums.asp.net/p/416094/416094.aspx#416094
With InProc SessionState, you won't be able to access the data... With StateServer, you still will have a sticky scenario trying to access the correct API to remove the session.
You will most likely want to use a database backed state solution like the pre-packaged SqlServer state provider or a third party solution like DOTSS: http://codeplex.com/dotss
With the database backed solution, you will be able to lookup the state record in a table by session id and mark it as completed. These techniques will vary based on the provider you choose.

Resources