Multi-Organizational sign-in with WsFederation owin middleware

This is the first of three blog posts on how we can use Windows Azure Active Directory in our web apps to empower our self. First Microsoft.Owin.Security.WsFederation is covered and how to sign into our WAAD, and how we can allow 3th party companies to sign up for our WAAD web app, so we can have multi organizational signin for targeting the B2B market. Second will be about asking for profile info from WAAD so you can get info about the customer that has signed in. Third will be about talking with windows azure management api from our web api to deploy and make changes to the customers windows azure subscription.

Disclaimer: We are using the alpha release of wsFederation so things might change and there are some features still missing.

You may also take a look at the owin app sample I made for these blog posts: https://gist.github.com/s093294/9145353

WsFederation

The alpha release was just pushed to nuget a few days ago and it allow us to sign into our Windows Azure Active Directories. I have done my sample in an owin app, so you can run it by simply creating a blank Asp.Net app in visual studio and copy in the file and add the nuget dependencies. A good start is to follow the stuff outlined here: http://www.cloudidentity.com/blog/2014/02/20/ws-federation-in-microsoft-owin-componentsa-quick-start/ and just take a blank app instead of a MVC app. It also outlines how to create your app on the azure management portal.

I have focused around making it possible for third party people to sign up with their WAAD and use my application so I have set the “Application is multi-tenant” to yes.

The first part of the app is to register the owin middleware to do the authentication. At this point I am still missing a few steps here on validation of the issuer. The basic idea is that when a administrator of some company signs up their WAAD to your app, then you store their tenant id for validating it in the IssuerValidator. If not everyone that signs up to the app on the azure portal can log into your app. Alternative you can do as the Visual studio templates do with social logins to use it as pre-step in the signup process so users still have to convert this external signin to a local account.

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
    });
    app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
    {
        TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
        {
            //ValidateIssuer  = false -> this causes IssuereValidator to not be run. 
            IssuerValidator = (issuer, token) =>
            {
                return false; // it still sign in the user even though its false.
                  
                return DatabaseIssuerNameRegistry.ContainsTenant(issuer);

            }
        },
        Wtrealm = "https://owindemo.s-innovations.net",
        MetadataAddress = "https://login.windows.net/802626c6-0f5c-4293-a8f5-198ecd481fe3/FederationMetadata/2007-06/FederationMetadata.xml"


    });

Inspired with the simplicity of doing these samples in a owin app, from http://leastprivilege.com/2014/02/21/test-driving-the-ws-federation-authentication-middleware-for-katana/ i have used the same login/logout code from his blog post. See it there or in the gist linked at the top. So to do the signup the following code should do. 

    app.Map("/signup", map =>
    {
        map.Run(async ctx =>
        {
            string signupToken = Guid.NewGuid().ToString();
            UriBuilder replyUrl = new UriBuilder(ctx.Request.Uri);
            replyUrl.Path = "/signup-callback";
            replyUrl.Query = "signupToken=" + signupToken;

            DatabaseIssuerNameRegistry.CleanUpExpiredSignupTokens();
            DatabaseIssuerNameRegistry.AddSignupToken(signupToken: signupToken,
                expirationTime: DateTimeOffset.UtcNow.AddMinutes(5));

            ctx.Response.Redirect(CreateConsentUrl(
                clientId: AppPrincipalId,
                requestedPermissions: "DirectoryReaders",//, CompanyAdministrator",
                consentReturnUrl: replyUrl.Uri.AbsoluteUri));
        });
    });

I have configured my app to ask for permissions to read data from the logged in users active directory, so when a WAAD administator is navigating to the /signup url he will be navigated to the signup page that look like this:

Caption: The consent screen of allowing my app to read stuff from 3th party WAAD

Notice that it is only administrators of the WAAD that are allowed to grant access. When accepting its navigated back to the signup callback where I register the tenant in my application.

    app.Map("/signup-callback", map =>
    {
        map.Run(async ctx =>
        {
            string tenantId = ctx.Request.Query["TenantId"];
            string signupToken = ctx.Request.Query["signupToken"];

            if (DatabaseIssuerNameRegistry.ContainsTenant(tenantId))
            {
                ctx.Response.Redirect("/login");
            }

            string consent = ctx.Request.Query["Consent"];

            if (!String.IsNullOrEmpty(tenantId) &&
                String.Equals(consent, "Granted", StringComparison.OrdinalIgnoreCase))
            {
                if (DatabaseIssuerNameRegistry.TryAddTenant(tenantId, signupToken))
                {

                    ctx.Response.Redirect("/login");
                }
            }
            await ctx.Response.WriteAsync(@"Sorry, you have to press accept :)");
        });
    });

And now people from the signed up organization can log into my applicaiton with their organizational accounts and I can start selling services to B2B this way. In the next post we will take a look at how we can ask the AD for info about the user and after that we will try to do stuff on the users azure subscription.



comments powered by Disqus