In this Post I will (try to) shortly explain how to Implement Web Sign on with Active Directory Federation Services under ASP.NET MVC and OWIN/Katana as Middleware.
There is plenty of Resources (read Code Snippets) on the Net about this subject, but what I actually found as important as the Code Snippets is actual Configuration of AD FS Server. The Configuration of ADFS is highly sensitive on every identifier, endpoint definition and even on every character like underscores, slashes, and of course prefixes like http/https are all exactly to be defined and used in your Web App!
ADFS Server Configuration
That is why I will first show some screenshots and Configuration of my ADFS Server running on Azure VM Development Environment:
Under Authentication Policies, you should enable Forms Authentication for Extranet users. That way it will be possible for users outside your Domain or on a Public Computer to get a nice Forms Authentication provided by ADFS and still get authenticated.
You should create new Relying Party Trust with following Configuration: Display Name you can write whatever you want of course. Under Relaying party identifiers, you should add exactly your Web Application URL (Including correct prefix and slash at the end. If you have more Bindings on you Web Application, define all of them.
NOTE: Some users had problems if their Web Application URLs contains underscores, so if you can, try to avoid them.
Under Endpoint Tab, add a WS-Federation Passive Endpoint with the same URL of your Web Application as in Relying party identifiers.
Note: You can also define multiple if you have more the one Binding, but only one can be Default.
Now knowing your exact ADFS Configuration, you will definitely save time and avoid configuration conditioned Problems so we can continue and finally write some Code.
ASP.NET Web Application with OWIN Middleware
First we will start off with OWIN Startup.cs Class. We have the Authentication Configuration Method outsourced into separate Class called Startup.Auth.cs, like the Default ASP.NET MVC Web Application Template do it, but many of you have it all directly under Startup.cs.
Startup.cs
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Security.WsFederation;
using Owin;
[assembly: OwinStartupAttribute(typeof(adfs.demo.Startup))]
namespace adfs.demo
{
public
partial
class
Startup
{
public
void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
Startup.Auth.cs
using System.Configuration;
using Microsoft.Owin.Security.Cookies;
using Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.WsFederation;
namespace adfs.demo
{
public
partial
class
Startup
{
public
void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(
new
CookieAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
});
app.UseWsFederationAuthentication(
new
WsFederationAuthenticationOptions
{
MetadataAddress = ConfigurationManager.AppSettings["ida:ADFSMetadata"],
Wtrealm = ConfigurationManager.AppSettings["ida:Wtrealm"]
});
app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
}
}
}
Both of this entries I have placed in Web.Config and it looks like this:
<appSettings>
<add
key="ida:ADFSMetadata"
value="http://your-adfs-server.com/FederationMetadata/2007-06/FederationMetadata.xml" />
<add
key="ida:Wtrealm"
value="https://your-exact-site-url/" />
</appSettings>
Now that should be enough to have OWIN Security running with our ADFS Server. All you have to do now is to add the [Authorize] Tags on needed Controllers or [AllowAnonymous] on single Actions that needs to be accessed anonymously.
Here is one very simple way of using a Templated AccountController.cs with our ADFS Implementation:
using System.Web;
using System.Web.Mvc;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.WsFederation;
namespace adfs.demo.Controllers
{
[Authorize]
public
class
AccountController : Controller
{
[AllowAnonymous]
public
void Login(string returnUrl)
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new
AuthenticationProperties { RedirectUri = "/adfs.demo" },
WsFederationAuthenticationDefaults.AuthenticationType);
}
}
public
void LogOff()
{
string callbackUrl = Url.Action("Index", "Home", routeValues: null, protocol: Request.Url.Scheme);
HttpContext.GetOwinContext().Authentication.SignOut(
new
AuthenticationProperties { RedirectUri = callbackUrl },
WsFederationAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}
}
}
Conclusion
It was actually pretty easy to implement the ADFS Authentication Provider in OWIN once you understand and have access the whole configuration of ADFS Server. So far it looks really fast and stable, beside few problems I had with Chrome and Firefox Browser that I will explain below. So as I promised, this was actually short post and I hope you will get your ADFS running smoothly just as I did.
Chrome / Firefox Redirection Loop Problem
After my first Implementation of ADFS under OWIN, it looked like everything is working well. As soon as I tested it on Chrome and Firefox, I figured out that after Login, I am being redirected to ADFS Forms Authentication Web Page and back to my Default Return URL few times before getting Error Message (that was not telling me much about Error) on my ADFS Forms Authentication Web Page.
In ADFS Event Log, there was this Error event:
Microsoft.IdentityServer.Web.InvalidRequestException: MSIS7042: The same client browser session has made '6' requests in the last '1' seconds. Contact your administrator for details.
There are many explanations and workarounds for the Redirection Loop Problem under OWIN Security, most of them have something to do with CookieManager under OWIN but none of them has solved my problem.
My Solution:
As soon as I have defined "fresh" new Relying Party Trust (see first part of the Post) with exactly the same Relying Party Identifier as an WS-Federation Passive Endpoint
URL (and I really mean exactly the same, same prefix, same URL and / at the end of URL, also see first part of the post) and then updated my Web.Config with the exact Values, it all started working as a Charm.
Posted
Oct 06 2016, 04:08 PM
by
Armin Kalajdzija