OpenIdConnectProtocolValidator - nonce error - c#

I'm using OpenIdConnect authentication on my azure website (azure active directory, c#, MVC) and I'm randomly getting this error
IDX10311: requireNonce is true (default) but validationContext.Nonce
is null. A nonce cannot be validated. If you dont need to check the
nonce, set OpenIdConnectProtocolValidator.RequireNonce to false
I am using the KentorOwinCookieSaver which as far as I understand, was a solution to this problem, but obviously I'm wrong because it keeps on happening. How can I stop this ?
In the ConfigureAuth method I have this line
app.UseKentorOwinCookieSaver();

According to your description, I followed this tutorial and used this code sample to check this issue. The initialization for authentication middle ware would look as follows:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
Using fiddler to capture the network traces when logging, you could find the OpenIdConnect.nonce cookie would be issued to the browser before the OpenID Connect middleware starting the authentication request as follows:
After user entered the credentials and consent the permissions, the authorization_code,id_token,state would be posted to your specified RedirectUri, then some validation would be executed and generate the new cookie and remove the previous OpenIdConnect.nonce cookie as follows:
IDX10311: requireNonce is true (default) but validationContext.Nonce is null. A nonce cannot be validated. If you dont need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to false
I used Microsoft.Owin.Security.OpenIdConnect 3.0.1 to test this issue. Per my understanding, you need to make sure your OpenIdConnect.nonce cookie has been successfully issued to your browser. For example, if your cookie issued to https://localhost:44353/, while the RedirectUri is set to http://localhost:4279, then I would encounter the similar issue:
Or you could try to explicitly set OpenIdConnectProtocolValidator.RequireNonce to false to disable check the nonce.

Solution 1:
This is the "Katana bug", install Kentor.OwinCookieSaver from nuget
package manager
Add below lines inside the configuration method
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions());
My complete config FYR:
public void Configuration(IAppBuilder app)
{
try
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOktaMvc(new OktaMvcOptions()
{
OktaDomain = ConfigurationManager.AppSettings["okta:OktaDomain"],
ClientId = ConfigurationManager.AppSettings["okta:ClientId"],
ClientSecret = ConfigurationManager.AppSettings["okta:ClientSecret"],
AuthorizationServerId = ConfigurationManager.AppSettings["okta:AuthorizationServerId"],
RedirectUri = ConfigurationManager.AppSettings["okta:RedirectUri"],
PostLogoutRedirectUri = ConfigurationManager.AppSettings["okta:PostLogoutRedirectUri"],
GetClaimsFromUserInfoEndpoint = true,
Scope = new List<string> { "openid", "profile", "email" },
});
}
catch (Exception ex)
{
//Error
}
}
Solution 2:
Check whether you have SSL in your application if yes check whether
binding happened properly.
If application runs on http it'll cause this error.

Related

Renewing session using Azure AD authentication in ASP.NET MVC project

I have configured Azure AD authentication in an ASP.NET MVC project using OpenId Connect. The authentication works, but the problem is, that after 60 minutes the session is not valid anymore. In my application, it's common that the user is idle for quite some time, and should be able to continue work without having to relogin. This is how I've setup the authentication in Startup.auth.cs:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
CookieManager = new SystemWebCookieManager()
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = (context) =>
{
return System.Threading.Tasks.Task.FromResult(0);
}
}
});
I have tried to add session refreshing logic as suggested at https://www.cloudidentity.com/blog/2016/07/25/controlling-a-web-apps-session-duration-2/. The problem in that approach is that login window cannot be displayed in a frame.
How can I keep the session valid until user closes browser window? This seems like a common issue that should have some common solution, but I could not find one.
You can add the below highlighted parameter in your code that under ‘new OpenIdConnectAuthenticationOptions’ section before the class: -
app.SetDefaultSignInAsAuthenticationType
(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
CookieManager = new SystemWebCookieManager()});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = (context) =>
{
return System.Threading.Tasks.Task.FromResult(0);
}
UseTokenLifetime = false
}
});'
The authentication session lifetime (e.g., cookies) should match that of the authentication token. The problem is that you need to extend token lifetime in AAD which is set to one hour by default.
Also, please find links to below threads for your reference: -
AzureAD and OpenIdConnect session expiration in ASP.net WebForms
Cookie expiry in ASP NET Core Authentication using Azure AD OpenIdConnect and custom middleware
Thanking you,

Implementing Sliding Expiration in OpenIdConnect with Azure

We have a ASP.Net MVC and have been using OpenIdConnect authentication with Azure AD as the authority. On successful authentication we set the "AuthenticationTicket" Expiry to 8hrs (below i have set to 15 minutes for testing). This 8hrs is fixed, meaning even if user performs activity on the application it won't slide.
But ideally we would want the expiration to slide as a user is active with the system.
Tried setting "SlidingExpiration" to True, even that didn't help. Documentation is not detailed around this topic.
So, How do we implement sliding expiration with OpenIdConnect Authentication?
Below is our code of startup.
namespace TestApp
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieManager = new SystemWebCookieManager(),
SlidingExpiration = true,
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
//
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
//
AuthorizationCodeReceived = (context) =>
{
context.AuthenticationTicket.Properties.ExpiresUtc = DateTime.UtcNow.AddHours(8);
context.AuthenticationTicket.Properties.AllowRefresh = true;
context.AuthenticationTicket.Properties.IsPersistent = true;
return Task.FromResult(0);
},
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Home/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
},
}
});
}
}
}
Hopefully someone from the ASP.net or Owin team can jump in with a better way to do this, but below is how I got around the exact same problem.
In order for the cookie authentication to take precedence and return a 401 or redirect when the cookie is expired, you need to set the cookie authentication mode to active and the Open Id authentication mode to passive.
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromMinutes(15),
CookieSecure = CookieSecureOption.Always,
LoginPath = Microsoft.Owin.PathString.FromUriComponent("/Logout")
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
PostLogoutRedirectUri = postLogoutRedirectUri,
}
);
After changing from active to passive, I was getting a 401 response when requesting an authorized resource, but refreshing the page would simply log me back in using the token saved from the OpenID call which is not expired when the token expires. To fix this, I set an action in the logout controller (specified with the LoginPath on the cookie options) to sign the user out of both OpenID and Cookie
[Route("Logout")]
public ActionResult Logout()
{
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
return View("~/Views/Index/Logout.cshtml");
}
Keep in mind, with Open ID set to passive and cookie set to active, all requests will be redirected as unauthorized if they don't contain the cookie (even if they contain a token). Open ID requests will have to specifically call the Open ID handler to be authorized and create the cookie
sources:
https://msdn.microsoft.com/en-us/library/microsoft.owin.security.cookies.cookieauthenticationoptions(v=vs.113).aspx
https://coding.abel.nu/2014/06/understanding-the-owin-external-authentication-pipeline/

Token issues in b2c-dotnet-webapp-and-webapi

I have created one b2c-dotnet-webapp-and-webapi type application. But After 20 min or after sometime(nearly 30 min not sure) My WebApp is throwing exception during the Ajax call saying 401(Unauthorized). This exception is coming when ajax call hitting the WebApp controller So this error is coming from OWIN middleware not sure why.
My Startup.cs settings are
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(_aadB2CPasswordResetPolicy));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(_aadB2CSignInPolicy));
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
if (context.Exception is OpenIdConnectProtocolInvalidNonceException &&
context.Exception.Message.Contains("IDX10316"))
{
// Redirect to the originally requested URL
context.Response.Redirect(context.Request.Uri.PathAndQuery);
}
else
{
var trackingId = Guid.NewGuid().ToString("N");
_telemetry.TrackException(
context.Exception,
new Dictionary<string, string> {{"SignInErrorTrackingId", trackingId}});
context.Response.Redirect($"/Home/SignInError?trackingId={trackingId}");
}
return Task.FromResult(0);
}
private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
{
return new OpenIdConnectAuthenticationOptions
{
// For each policy, give OWIN the policy-specific metadata address, and
// set the authentication type to the id of the policy
MetadataAddress = string.Format(_aadInstance, _tenant, policy),
AuthenticationType = policy,
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = _clientId,
RedirectUri = _redirectUri,
PostLogoutRedirectUri = _redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
},
Scope = "openid",
ResponseType = "id_token",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
SaveSigninToken = true,
},
};
}
}
If I am modifying the code
app.UseCookieAuthentication(new CookieAuthenticationOptions { SlidingExpiration = true, ExpireTimeSpan = TimeSpan.FromMinutes(60) });,
And adding the below setting in OpenIdConnectAuthenticationOptions
UseTokenLifetime = false,
Then My WebApp is working for 1 hour and after that again I am facing the 401 Unauthorized. This time my WEAPI giving this error because by default token is valid for 1 hour I guess.
Question : How can I manage the token issues if the token is expired after 1 hour during ajax call ? and What is best setting that I should have So that my middleware will not give me 401 after 20 min or some random time ?
Kindly Ignore if something I am doing terribly wrong. I am very new to this and don't have much idea.
You're on the right track! There are a couple things that you need to be aware of:
All tokens returned by Azure AD B2C have an expiration time. After x minutes, your id token expired. You need to get a new id token again by going through the sign-in process again.
Because it is annoying to sign in every x minutes, you can request a refresh token when a user logs in for the first time. When the id token expires, you can send the refresh token to Azure AD B2C to get a new id token.
Part of the refresh token implementation is automated by the library, but this is what you need to implement:
Request a code in addition to an id token by adding 'code' in the responseType variable, and adding 'offline_access' in the scope variable.
Exchange the code for a refresh token and store the token inside a cache.
Get the token from the cache before making a call to the web api. If the token is expired, the library will automatically renew it for you before pulling it out of the cache.
All of this is done in this example. You just need to update the scope (the default responseType when you don't specify it is "code id_token").
Note: We enabled the use of access tokens and updated the sample with it. Please use this updated sample. Your code reflects the old sample. I also recommend using access tokens instead of id tokens when calling the web API.

IdentityServer3 + AzureAD and RedirectUri Confusion

I'm struggling to understand how IdentityServer3, AzureAD and a Private Database all work together. The biggest problem is how the Redirect URIs are being handled.
My scenario is I have a stand alone IdentityServer3. It's job is to authenticate users against either AzureAD or a private DB. Within the Startup.cs file on the ID3 server, I have the following OpenID Connect code:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/identity", s3App =>
{
s3App.UseIdentityServer(new IdentityServerOptions
{
SiteName = "3S",
SigningCertificate = Certificate.Load(),
Factory = new IdentityServerServiceFactory()
.UseInMemoryUsers(InMemoryUsers.Get())
.UseInMemoryClients(InMemoryClients.Get())
.UseInMemoryScopes(InMemoryScopes.Get()),
AuthenticationOptions = new AuthenticationOptions
{
EnablePostSignOutAutoRedirect = true,
EnableSignOutPrompt = false,
IdentityProviders = ConfigureAdditionalIdentityProviders
}
});
});
}
public static void ConfigureAdditionalIdentityProviders(IAppBuilder app, string signInAsType)
{
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "AzureAd",
Caption = "Login",
ClientId = "4613ed32-xxxx-xxxx-xxxx-xxxxxxxxxxxx", // GUID of registered application on Azure
Authority = "https://login.microsoftonline.com/our-tenant-id/",
PostLogoutRedirectUri = "https://localhost:44348/identity",
RedirectUri = "https://localhost:44348/identity",
Scope = "openid email profile",
ResponseType = "id_token",
AuthenticationMode = AuthenticationMode.Passive,
SignInAsAuthenticationType = signInAsType,
TokenValidationParameters = new TokenValidationParameters
{
AuthenticationType = Constants.ExternalAuthenticationType,
ValidateIssuer = false
}
});
}
I don't understand why the ID3 Server would need to have either RedirectUri or PostLogoutRedirectUri...shouldn't that be "passed through" from the application requesting the authentication? After all, we want to get back to the application, not the ID3 Server. Granted, I don't think this is what's causing my problem, but it would be nice to understand why these are here.
I will say, I've gotten "close" to this working.
When my application requiring authentication requests authentication against AzureAD, I'm redirected to the Microsoft Account login screen to enter my username/password for my work account. I submit my credentials and then get redirected back to either the ID3 server or my application, depending on which RedirectUri has been used in the above code.
For the sake of argument, let's say I use my application for the RedirectUri. I will be sent back to the application, but not to the page that initially prompted the authentication challenge, and if I click on a page that requires authentication, I'm sent back to the AzureAD server to log in again, only this time AzureAD recognizes me as already logged in.
Unfortunately, it doesn't appear that the SecurityTokenValidated notification is being acknowledged/set after the redirect from AzureAD.
Here's the code found in the application Startup.cs:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44348/identity",
ClientId = "3af8e3ba-5a04-4acc-8c51-1d30f8587ced", // Local ClientID registered as part of the IdentityServer3 InMemoryClients
Scope = "openid profile roles",
RedirectUri = "http://localhost:52702/",
PostLogoutRedirectUri = "http://localhost:52702/",
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
var id = n.AuthenticationTicket.Identity;
var givenName = id.FindFirst(Constants.ClaimTypes.GivenName);
var familyName = id.FindFirst(Constants.ClaimTypes.FamilyName);
var sub = id.FindFirst(Constants.ClaimTypes.Subject);
var roles = id.FindAll(Constants.ClaimTypes.Role);
var nid = new ClaimsIdentity(
id.AuthenticationType,
Constants.ClaimTypes.GivenName,
Constants.ClaimTypes.Role
);
nid.AddClaim(givenName);
nid.AddClaim(familyName);
nid.AddClaim(sub);
nid.AddClaims(roles);
nid.AddClaim(new Claim("application_specific", "Some data goes here. Not sure what, though."));
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
n.AuthenticationTicket = new AuthenticationTicket(nid, n.AuthenticationTicket.Properties);
return Task.FromResult(0);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType != OpenIdConnectRequestType.LogoutRequest)
return Task.FromResult(0);
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.Response.Redirect("/Error/message=" + context.Exception.Message);
//Debug.WriteLine("*** AuthenticationFailed");
return Task.FromResult(0);
},
}
});
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
}
}
You'll notice that the OpenIdConnectAuthenticationOptions also contain a RedirectUri and a PostLogoutRedirectUri which point to the application, but those don't seem to matter.
Of course, everything works perfectly when I'm logging in using the "cookies" side of things - I see all of my claims for the user. And, spending some time on the phone with Microsoft, they proposed a solution outside of ID3 which worked, but is not the way we need to go. We will have multiple applications authenticating against our ID3 so we need to contain and control the flow internally.
I really need some help trying to figure out this last mile issue. I know I'm close, I've just been staring at this so long that I'm probably staring right at my error and not seeing it.
10/22/2016 Edit
Further testing and enabling Serilog revealed an issue with the RedirectUri and PostLogoutRedirectUri resulted in my adding the /identity to the end of the URIs which corresponds to the value set in app.Map. This resolved the issue of my being returned to the "blank" page of IdentityServer3, I'm now returned to the IdentityServer3 login screen. Azure AD still thinks I'm logged in, I'm just not getting the tokens set properly in my application.
Since the authenticate flow is a little complex, I am trying illustrate it using a figure below:
First the redirect URL need to register to the identity provider, so the server will match the RedirectURL in request to ensure that the response is redirected as expected instead of redirect to like Phishing site( security consideration).
And as the figure demonstrate, to use the Azure AD as the external identity provider for IdentityServer3, we need to register the apps on Azure AD. However since the app is used to communicate with Identity Server, the redirect URL register on the Azure Portal should redirect to the IdentityServer3 instead of URL of app.
For example, the URL of my identity server 3 is https://localhost:44333 then I use code below to add the additional identity providers. And this URL is the redirect URL on the Azure portal:
public static void ConfigureAdditionalIdentityProviders(IAppBuilder app, string signInAsType)
{
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "aad",
Caption = "Azure AD",
SignInAsAuthenticationType = signInAsType,
Authority = "https://login.microsoftonline.com/04e14a2c-0e9b-42f8-8b22-3c4a2f1d8800",
ClientId = "eca61fd9-f491-4f03-a622-90837bbc1711",
RedirectUri = "https://localhost:44333/core/aadcb",
});
}
And the URL of my app is http://localhost:1409/ which is register on the IdentyServer3 and below code is the web app use OWIN OpenId Connect to add the IdentyServer3 as the identity data provider:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
SignInAsAuthenticationType = "cookies",
Authority = "https://localhost:44333",
ClientId = "mvc",
RedirectUri = "http://localhost:1409/",
ResponseType = "id_token",
Scope = "openid profile email"
});

OWIN Challenge() method not executed when using multiple ADFS + Cookies

My ASP.Net app uses OWIN/Katana/Claims, and allows login using:
Traditional username/password (exists for all users)
Google
Azure AD
It works perfectly, and all the necessary redirects/claims transfers work well (the user NameIdentifier/Provider(/tenant) details are passed back to my app so the unique id values can be linked up). Note that users do not sign up/register for the app - access is provisioned by their organisation's super-user, and a username/password sent to them which they can then hook up with Google/Azure.
However, I now need to extend this functionality to allow users to hook up to their organisation's ADFS provider. The only working example for this that's remotely close is here (tutorial/code), but it is strictly based on ADFS-only. When I adapt this into my project, it doesn't work.
My entire StartupAuth file is shown below. I appreciate that there may be configuration errors, but based on the scraps of samples I've found over the last six weeks, this is the best I've had.
public void Configuration(IAppBuilder app)
{
// STANDARD CODE FOR APP COOKIE AND GOOGLE - WORKS PERFECTLY
CookieAuthenticationOptions coa = new CookieAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
CookieName = "MyAppName",
ExpireTimeSpan = TimeSpan.FromMinutes(60),
SlidingExpiration = true,
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/login.aspx"),
CookieHttpOnly = true,
CookieSecure = CookieSecureOption.SameAsRequest,
Provider = new CookieAuthenticationProvider { OnValidateIdentity = context =>
{
dynamic ret = Task.Run(() =>
{
// Verify that "userId" and "customerId" claims exist, and that each has a valid value (greater than zero) - removed for brevity
return Task.FromResult(0);
});
return ret;
} }
};
app.SetDefaultSignInAsAuthenticationType(coa.AuthenticationType);
app.UseCookieAuthentication(coa);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions {
ClientId = "84***********************k3.apps.googleusercontent.com",
ClientSecret = "jue*****************Ppi"
});
// NEW CODE THAT FAILS TO WORK - SPECIFYING EACH CUSTOMER'S ADFS AS A NEW WSFED AUTH OPTION
WsFederation.WsFederationAuthenticationOptions Adfs_CompanyA = new WsFederation.WsFederationAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Passive,
MetadataAddress = "https://CompanyA.net/FederationMetadata/2007-06/FederationMetadata.xml",
AuthenticationType = AdfsAuthenticationTypes.CompanyA,
Wtrealm = "https://www.CompanyA.co.uk/MyAppName"
};
WsFederation.WsFederationAuthenticationOptions Adfs_CompanyB = new WsFederation.WsFederationAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Passive,
MetadataAddress = "https://CompanyB.net/federationmetadata/2007-06/federationmetadata.xml",
AuthenticationType = AdfsAuthenticationTypes.CompanyB,
Wtrealm = "http://www.CompanyB.co.uk/azure/MyAppName"
};
// User (who is logged in), route for hyperlink "Link my account with ADFS"
app.Map("/SSO/LinkUserAccount/ADFS/process", configuration => { configuration.UseWsFederationAuthentication(Adfs_CompanyA); });
// CompanyA ADFS - single sign-on route
app.Map("/SSO/Login/CompanyA/ADFS/Go", configuration => { configuration.UseWsFederationAuthentication(Adfs_CompanyA); });
// CompanyB ADFS - single sign-on route
app.Map("/SSO/Login/CompanyB/ADFS/Go", configuration => { configuration.UseWsFederationAuthentication(Adfs_CompanyB); });
}
}
Here is the code I use to issue an OWIN Challenge:
string provider = MyApp.SingleSignOn.GetCustomerAdfsAuthenticationType(customerName);
string redirectUrl = string.Format("{0}/SSO/Login/{1}/ADFS/Go", Request.Url.GetLeftPart(UriPartial.Authority), provider); // creates https://myapp.com/SSO/Login/CompanyA/ADFS/Go for CompanyA users
Context.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = redirectUrl }, provider);
Response.StatusCode = 401;
Response.End();
This is webforms, but please don't let that stop MVC pro's from contributing. The code is virtually identical anyway and I'm using routes.
The problem I have is that when the user clicks on the "sign-on with ADFS" link, e.g. URL becomes https://myapp.com/SSO/Login/CompanyA/ADFS
I get an 401 Unauthorized error, instead of the user being redirected to the ADFS login page.
In web.config, I allow unauthorized access to path "SSO". For some reason the Challenge() method never redirects the user, it simply gets ignored and the code reaches the point where it returns a 401. The value of string provider exactly matches the WsFederationAuthenticationOptions.AuthenticationType value defined in Startup.Auth.
I've been sturggling with this now for six weeks, so this is getting a bounty at the first opportunity, and a crate of beer delivered to your chosen address when it is solved.
I solved the problem. Amazingly, it was as simple as me missing this from the end of StartupAuth:
app.UseStageMarker(PipelineStage.Authenticate);
Have you set up OWIN logging? Any clues there?
Also Test driving the WS-Federation Authentication Middleware for Katana.
Have a look at the code in IdentityServer 3. There's a WS-Fed plugin there and the documentation is here (at the bottom).

Categories