This project has moved and is read-only. For the latest updates, please go here.

Example for OWIN to connect with ADFS 2.0?

Nov 27, 2013 at 6:42 PM
Hi

I'm trying to use the SPA Web Form template with Individual User Accounts (http://www.asp.net/visual-studio/overview/2013/creating-web-projects-in-visual-studio) as basis. I'd like to add additional option besides facebook/google to login using our domain account, which is federated with ADFS 2.0. I've followed the sample here
http://www.cloudidentity.com/blog/2013/10/25/securing-a-web-api-with-adfs-on-ws2012-r2-got-even-easier/#comment-9609
(Which needs to update all the OWIN packages to 2.0.2.0 in order to have access to ActiveDirectory package.)

To add the Microsoft.Owin.Security.ActiveDirectory package as well as adding following line to my Startup.Auth.cs
            app.UseActiveDirectoryFederationServicesBearerAuthentication(new ActiveDirectoryFederationServicesBearerAuthenticationOptions
            {
                Audience = "http://localhost:39687",
                Realm = "http://localhost:39687",
                BackchannelHttpHandler = new WebRequestHandler()
                {
                    ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true
                },
                MetadataEndpoint = "https://staging/federationmetadata/2007-06/federationmetadata.xml",
                Description = new AuthenticationDescription
                {
                    AuthenticationType = "Active Directory",
                    Caption = "Active Directory"
                }
            });
This manages to display a button for Active Directory as a login option, however it doesn't seem to do anything after clicking on it. I was expecting it to redirect to ADFS SSO page, am I missing something?

In addition, when I'm using the Google sign in option, the sample application redirects me to select one of my google account to login, but after clicking on the account. A null pointer exception is thrown on
var loginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo();
in RegisterExternalLogin. Anyone have similar experience and perhaps workaround/fixes?

Note the facebook login seems to be working fine.
Nov 27, 2013 at 7:11 PM
Have you tried setting up the template using the Organization Accounts option to see how it populates the AD setup?
Nov 27, 2013 at 7:42 PM
Edited Nov 27, 2013 at 7:43 PM
I tried using the Organization Account withe SPA template. It seems to work out of the box with wsFederation and ADFS.
However, I'd like to provide similar login experience since the ADFS would be just one of the login methods we want to support.

Namely Form, google, facebook, and AD

Is there any way to bridge between wsFederation and OWIN in the same application?
Nov 27, 2013 at 7:58 PM
The wsFederation package hasn't actually been released yet. The Org SPA template should only have ADFS (Azure Active Directory). I was just suggesting you copy the Org auth from the Org template and paste it along side the individual auth from the individual template. i.e. mash the two templates together.
Nov 28, 2013 at 4:35 PM
Thanks for the suggestion, however the Org SPA template redirects the user to ADFS page directly upon accessing the site. Is there any suggestion to allow Org auth as well as forms auth, google, facekbook etc with button selection?
Nov 28, 2013 at 9:56 PM
What I've found out so far is that in OpenAuthProvider.ascx.cs of the SPA, the following line seems to redirect the user to google/facebook respectively

Context.GetOwinContext().Authentication.Challenge(properties, provider);

While when using UseActiveDirectoryFederationServicesBearerAuthentication it doesn't seem to redirect to anywhere. A 401 unauthorized is observed instead.
And a blank page of /Account/Login is presented.

Thoughts?
Nov 29, 2013 at 11:26 PM
Did anyone manage to get this working at all?
Dec 3, 2013 at 11:59 PM
I've asked around and the recommendation appears to be to wait for the new Microsoft.Owin.Security.Federation package that supports WsFed. There's an early prototype on our nightly feed, but no guarantees how well it works. The real thing should be coming in Jan.
Dec 4, 2013 at 7:48 PM
Hi Tratcher,

Thanks for the suggestion, so is this Microsoft.Owin.Security.Federation supposedly going to replace Microsoft.Owin.Security.ActiveDirectory?
Also, is there any test/examples of using Microsoft.Owin.Security.Federation with ADFS 2.0?
Dec 4, 2013 at 7:56 PM
The Federation package is just a rough prototype, so there aren't any samples or finalized implementation plans just yet.
Dec 9, 2013 at 1:41 PM
I wanted to update the SPA template to be able to signin using multi organizational accounts. So I started out creating a owin extension ontop of Microsoft.Owin.Security.Federation that sets all the properties correctly for this kind of sign on.

This is work in progress, but it looks like this sofar:
        public static IAppBuilder UseOrganizationalAuthentication(this IAppBuilder app,
            OrganizationalAuthenticationOptions options)
        {
            if (app == null)
            {
                throw new ArgumentNullException("app");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }
            var fedOptions = new FederationConfiguration(true);
            fedOptions.WsFederationConfiguration.Realm = options.AppId;
            fedOptions.WsFederationConfiguration.RequireHttps = options.RequireHttps;
            fedOptions.WsFederationConfiguration.Issuer = "https://login.windows.net/common/wsfed";
            fedOptions.WsFederationConfiguration.PassiveRedirectEnabled = options.PassiveRedirectEnabled;
            fedOptions.CookieHandler.RequireSsl = options.RequireHttps;

            //This need some work.
            fedOptions.WsFederationConfiguration.Reply = options.ReplyUrl ?? "" + options.CallbackPath ?? "";

            app.UseFederationAuthentication(new FederationAuthenticationOptions()
            {
                AuthenticationType = options.AuthenticationType,
                FederationConfiguration = fedOptions,
                ReturnPath = options.CallbackPath,
                SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(),
                AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
                Provider = new FederationAuthenticationProvider(),
                Description = new AuthenticationDescription { Caption = options.Caption, AuthenticationType = options.AuthenticationType }
            }
            );

            return app;
        }
I have a few comments that I learned from doing this but not sure where to pass it.

In ApplyResponseChallengeAsync() the SignInRequestMessage is created, and ofcause it makes sense to do:
message.Reply = _federationConfiguration.WsFederationConfiguration.Reply;
Cant say if this is general, but for my setup I have added reply urls on my Azure AD App, and not to specify the reply at the app startup, it would make sense to generate it based on the current application request and public PathString ReturnPath { get; set; } from options. This is because InvokeReplyPathAsync validates that ReturnPath have been set and if so checks if its equal the Request.Path, and only if then it proceeds with AuthenticateAsync().

So to use it as a external signin and in passive mode. I have to specify both a ReturnPath in options , and this should be equal to the reply property of FederationConfiguration: reply="https://localhost:44305/signin-organizational" Would be nice if the FederationAuthenticationHandler would just set the the reply in the signin message to : Request.Scheme + "://" + Request.Host + Request.PathBase + Options.ReturnPath if its in passive mode.

Hope it makes sense.
Dec 9, 2013 at 5:34 PM
Edited Dec 9, 2013 at 5:38 PM
Hi pksorensen,

Thanks for the example, it helped a lot!
Dec 9, 2013 at 9:15 PM
Hi pksorensen,

To add to your comments, I was able to get OWIN configure to allow using passive login to work with ADFS 2.0. Following are the changes I needed to change in FederationAuthenticationHandler.
  1. Since I'm using a self-signed certificate, SecurityTokenHandlers will throw exception, even when I already have following in my web.config
    <certificateValidation certificateValidationMode="None" />
Therefore I used this line to disable all configuration validations, this is for dev only.
identityConfiguration.SecurityTokenHandlers.Configuration.CertificateValidator = X509CertificateValidator.None;
  1. ApplyResponseChallengeAsync doesn't seem to obtain the correct RedirectUri from the clallenge.Properties, so I've modified to following
            var extra = challenge.Properties;
            if (string.IsNullOrEmpty(extra.RedirectUri))
            {
                string redirectUri = prefix + Request.Path + Request.QueryString;
                extra.RedirectUri = redirectUri;
            }
    
One last thing, since Microsoft.AspNet.Identity's GetExternalLoginInfo REQUIRES that nameidentifier to be present, otherwise GetExternalLoginInfo will simply return null similar to login failed.

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
Dec 9, 2013 at 10:17 PM
<certificateValidation certificateValidationMode="None" />
You need to initialize the federation configuration to make it update its internal configuration and apply the None validator.
First I added
        public FederationAuthenticationHandler(ILogger logger, FederationConfiguration federationConfiguration)
        {
            _logger = logger;
            _federationConfiguration = federationConfiguration;
            if (!_federationConfiguration.IsInitialized)
                _federationConfiguration.Initialize();
        }
but then I decided to avoid changing in the Microsoft.Owin.Security.Federation to make it easier to transition to it when it go public and I just initialize it before passing it to the options. My setup is specific to multi organizational login on WAAD so I decided to configure the
            var fedOptions = new FederationConfiguration(false);
            fedOptions.WsFederationConfiguration.Realm = options.AppId;
            fedOptions.WsFederationConfiguration.RequireHttps = options.RequireHttps;
            fedOptions.WsFederationConfiguration.Issuer = "https://login.windows.net/common/wsfed";
            fedOptions.WsFederationConfiguration.PassiveRedirectEnabled = options.PassiveRedirectEnabled;
            fedOptions.CookieHandler = new ChunkedCookieHandler() { RequireSsl = options.RequireHttps };

            fedOptions.IdentityConfiguration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(options.AppId));
           
            foreach(var remove in fedOptions.IdentityConfiguration.SecurityTokenHandlers.OfType<SessionSecurityTokenHandler>().ToList())
            {
                   fedOptions.IdentityConfiguration.SecurityTokenHandlers.Remove(remove);
            }
            if (!fedOptions.IdentityConfiguration.SecurityTokenHandlers.OfType<MachineKeySessionSecurityTokenHandler>().Any())
                fedOptions.IdentityConfiguration.SecurityTokenHandlers.Add(new MachineKeySessionSecurityTokenHandler());    
            
            fedOptions.IdentityConfiguration.CertificateValidationMode = X509CertificateValidationMode.None;
            DatabaseIssuerNameRegistry.Factory = options.TenantFactory;
            fedOptions.IdentityConfiguration.IssuerNameRegistry = new DatabaseIssuerNameRegistry();   

            if (!string.IsNullOrEmpty(options.ReplyUrl) && options.CallbackPath.HasValue)
                fedOptions.WsFederationConfiguration.Reply = options.ReplyUrl.Trim('/') + options.CallbackPath;
            
            fedOptions.Initialize();
But even with the false in the constructor there need to be an empty:
  <system.identityModel>
    <identityConfiguration>
    </identityConfiguration>

  </system.identityModel>