@robdmoore

Blog about software engineering, web development, agile/lean/Continuous Delivery, C#, ASP.NET and Microsoft Azure.

Rob is the Chief Technology Officer of MakerX where he is building out a vision 6 years in the making, including the principles, culture, people, approaches, intellectual property and successful deliveries that form the basis of an amazing company. #makersmake

He is an Open Source Software contributor via his personal GitHub as well as TestStack and MRCollective.

He lives in Perth, Australia and has a BEng (Hons 1) (CompSysEng) and BSc (CompSc). You can find him on Twitter and GitHub as @robdmoore.

Robert is interested in agile methodologies, lean, continuous delivery, software engineering, web development, aircraft and floorball.

All source code on this blog and Robert’s Gists are Public Domain unless otherwise stated.

Posts - Page 6 of 22

Authenticating an ASP.NET MVC 5 application with Microsoft Azure Active Directory

  • 3 min read

This post outlines how to easily add Azure AD authentication to an existing (or new) ASP.NET MVC 5 (or 3 or 4) application.

Practical Microsoft Azure Active Directory Blog Series

This post is part of the Practical Microsoft Azure Active Directory Blog Series.

Add Azure AD Authentication

These instructions will help you easily add authentication to your new or existing ASP.NET application, based on what the Visual Studio Identity and Access tools do. It’s a basic setup for a single tenant. Read the next post in the series to understand what’s going on and ways that it can be extended. The links show either a commit from the example project or to relevant documentation.

Note: Ignore the ...’s and replace the {SPECIFIED_VALUES} with the correct values.

  1. Create an Azure Active Directory tenant; note: AD tenants are not associated with your Azure Subscription, they are “floating” so add any live ids for people you want to administer it as Global Administrators
  2. Create an Application in your AD tenant with audience URL and realm being your website homepage (minus the slash at the end)
    • Record the name of your AD tenant e.g. {name}.onmicrosoft.com
    • Record the GUID of your AD tenant by looking at the FEDERATION METADATA DOCUMENT URL under View Endpoints
    • The image upload and Sign On URL are used for the Azure AD Applications Portal
  3. Create a user account in your tenant that you can use to log in with
  4. Install-Package Microsoft.Owin.Security.ActiveDirectory
  5. Install-Package System.IdentityModel.Tokens.ValidatingIssuerNameRegistry
  6. Add a reference to System.IdentityModel
  7. Add a reference to System.IdentityModel.Services
  8. Add a Startup.cs file (if it doesn’t already exist) and configure OWIN to use Azure Active Directory (edit for new version)
      using System.Configuration;
      using Microsoft.Owin.Security.ActiveDirectory;
      using Owin;
      namespace {YOUR_NAMESPACE}
      {
       public class Startup
       {
           public void Configuration(IAppBuilder app)
           {
               app.UseWindowsAzureActiveDirectoryBearerAuthentication(
                   new WindowsAzureActiveDirectoryBearerAuthenticationOptions
                   {
                       TokenValidationParameters = new TokenValidationParameters
                       {
                           ValidAudience = ConfigurationManager.AppSettings["ida:AudienceUri"]
                       },
                       Tenant = ConfigurationManager.AppSettings["AzureADTenant"]
                   });
           }
       }
      }
    
  9. Add the correct configuration to your web.config file; change requireSsl and requireHttps to true if using a https:// site (absolutely required for production scenarios)
     <?xml version="1.0" encoding="utf-8"?>
     <configuration>
       <configSections>
         <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
         <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
       </configSections>
       ...
       <appSettings>
         ...
         <add key="ida:AudienceUri" value="{YOUR_WEBSITE_HOMEPAGE_WITHOUT_TRAILING_SLASH}" />
         <add key="ida:FederationMetadataLocation" value="https://login.windows.net/{YOUR_AD_TENANT_NAME}.onmicrosoft.com/FederationMetadata/2007-06/FederationMetadata.xml" />
         <add key="AzureADTenant" value="{YOUR_AD_TENANT_NAME}.onmicrosoft.com" />
       </appSettings>
       ...
       <system.webServer>
         ...
         <modules>
           <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
           <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
         </modules>
       </system.webServer>
       ...
       <system.identityModel>
         <identityConfiguration>
           <issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
             <authority name="https://sts.windows.net/{YOUR_AD_TENANT_GUID}/">
               <keys>
                 <add thumbprint="0000000000000000000000000000000000000000" />
               </keys>
               <validIssuers>
                 <add name="https://sts.windows.net/{YOUR_AD_TENANT_GUID}/" />
               </validIssuers>
             </authority>
           </issuerNameRegistry>
           <audienceUris>
             <add value="{YOUR_WEBSITE_HOMEPAGE_WITHOUT_TRAILING_SLASH}" />
           </audienceUris>
           <securityTokenHandlers>
             <add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
             <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
           </securityTokenHandlers>
           <certificateValidation certificateValidationMode="None" />
         </identityConfiguration>
       </system.identityModel>
       <system.identityModel.services>
         <federationConfiguration>
           <cookieHandler requireSsl="false" />
           <wsFederation passiveRedirectEnabled="true" issuer="https://login.windows.net/{YOUR_AD_TENANT_NAME}.onmicrosoft.com/wsfed" realm="{YOUR_WEBSITE_HOMEPAGE_WITHOUT_TRAILING_SLASH}" requireHttps="false" />
         </federationConfiguration>
       </system.identityModel.services>
     </configuration>
    
  10. Configure AntiForgery to use the correct claim type to uniquely identify users
    // Global.asax.cs
              protected void Application_Start()
              {
                  ...
                  IdentityConfig.ConfigureIdentity();
              }
    
    // App_Start\IdentityConfig.cs
    using System.IdentityModel.Claims;
    using System.Web.Helpers;
    namespace {YOUR_NAMESPACE}
    {
        public static class IdentityConfig
        {
            public static void ConfigureIdentity()
            {
                AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
            }
        }
    }
    
  11. Configure the application to refresh issuer keys when they change
        public static void ConfigureIdentity()
        {
            ...
            RefreshIssuerKeys();
        }
        private static void RefreshIssuerKeys()
        {
            // http://msdn.microsoft.com/en-us/library/azure/dn641920.aspx
            var configPath = AppDomain.CurrentDomain.BaseDirectory + "\\" + "Web.config";
            var metadataAddress = ConfigurationManager.AppSettings["ida:FederationMetadataLocation"];
            ValidatingIssuerNameRegistry.WriteToConfig(metadataAddress, configPath);
        }
    
  12. Add LogoutController
    // Controllers\LogoutController.cs
    using System;
    using System.IdentityModel.Services;
    using System.Web.Mvc;
    namespace {YOUR_NAMESPACE}.Controllers
    {
        public class LogoutController : Controller
        {
            public ActionResult Index()
            {
                var config = FederatedAuthentication.FederationConfiguration.WsFederationConfiguration;
                var callbackUrl = Url.Action("Callback", "Logout", null, Request.Url.Scheme);
                var signoutMessage = new SignOutRequestMessage(new Uri(config.Issuer), callbackUrl);
                signoutMessage.SetParameter("wtrealm", config.Realm);
                FederatedAuthentication.SessionAuthenticationModule.SignOut();
                return new RedirectResult(signoutMessage.WriteQueryString());
            }
            [AllowAnonymous]
            public ActionResult Callback()
            {
                if (Request.IsAuthenticated)
                    return RedirectToAction("Index", "Home");
                return View();
            }
        }
    }
    
    <!-- Views\Logout\Callback.cshtml -->
    @{
        ViewBag.Title = "Logged out";
    }
    <h1>Logged out</h1>
    <p>You have successfully logged out of this site. @Html.ActionLink("Log back in", "Index", "Home").</p>
    
  13. Add logout link somewhere - @Html.ActionLink("Logout", "Index", "Logout")
  14. Add authentication to the app; do this as you normally would with [Authorize] to specific controller(s) or action(s) or globally by adding to GlobalFilters.Filters.Add(new AuthorizeAttribute());
  15. Load the site and navigate to one of the authenticated pages - it should redirect you to your Azure AD tenant login page whereupon you need to log in as one of the users you created and it should take you back to that page, logged in
  16. The usual User.Identity.Name and User.Identity.IsAuthenticated objects should be populated and if you want access to the claims to get the user’s name etc. then use something like ClaimsPrincipal.Current.FindFirst(ClaimTypes.GivenName).Value

Read More

Practical Microsoft Azure Active Directory Blog Series

  • 4 min read

I finally had a chance to play with Microsoft Azure Active Directory in a recent project. I found the experience to be very interesting - Azure AD itself is an amazing, powerful product with a lot of potential. It certainly has a few rough edges here and there, but it’s pretty clear Microsoft are putting a lot of effort into it as it’s forming the cornerstone of how it authenticates all of it’s services including Office 365.

Azure AD gives you the ability to securely manage a set of users and also gives the added benefit of allowing two-factor authentication (2FA), single-sign-on across applications, multi-tenancy support and ability to allow external organisations to authenticate against your application.

This blog series will outline the minimum set of steps that you need to perform to quickly and easily add Azure AD authentication to an existing ASP.NET MVC 5 (or 3 or 4) site (or a new one if you select the No Authentication option when creating it) as well as configure things like API authentication, role authorisation, programmatic logins and deployments to different environments.

There are already tools and libraries out there for this - why are you writing this series?

Microsoft have made it fairly easy to integrate Azure AD authentication with your applications by providing NuGet packages with most of the code you need and also tooling support to configure your project in Visual Studio. This is combined with a slew of MSDN and technet posts covering most of it.

When it comes to trying to understand the code that is added to your solution however, things become a bit tricky as the documentation is hard to navigate through unless you want to spend a lot of time. Also, if you have Visual Studio 2013 rather than Visual Studio 2012 you can only add authentication to a new app as part of the File -> New Project workflow by choosing the Organizational Authentication option:

ASP.NET Organizational Authentication option
Visual Studio: File > New Project > ASP.NET > Change Authentication > Organizational Authentication

If you have an existing ASP.NET web application and you are using Visual Studio 2013 then you are out of luck.

Furthermore, the default code you get requires you to have Entity Framework and a database set up, despite the fact this is only really required if you are using multiple Azure AD tenants (unlikely unless you are creating a fairly hardcore multi-tenant application).

If you then want to add role-based authentication based on membership in Azure AD groups then there is no direction for this either.

For these reasons I’m developing a reference application that contains the simplest possible implementation of adding these features in an easy to follow commit-by-commit manner as a quick reference. I will also provide explanations of what all the code means in this blog series so you can understand how it all works if you want to.

You can see the source code of this application here and an example deployment here. The GitHub page outlines information such as example user logins and what infrastructure I set up in Azure.

What are you planning on covering?

This will be the rough structure of the posts I am planning in no particular order (I’ll update this list with links to the posts over time):

I’m notoriously bad at finishing blog series’ that I start, so no promises on when I will complete this, but I have all of the code figured out in one way or another and the GitHub should at least contain commits with all of the above before I finish the accompanying posts so *fingers crossed*! Feel free to comment below if you want me to expedite a particular post.

More resources

I came across some great posts that have helped me so far so I thought I’d link to them here to provide further reading if you are interested in digging deeper:

Read More

Announcing repave.psm1

  • 2 min read

So after 18 months of not repaving my machine and occasionally (especially lately) having to deal with the machine filling up and slowing down I’m finally at the point where it’s time to repave. I wanted to do it ages ago, but I avoided it because of how painful it is to do.

This time around I’ve decided to bite the bullet and finally do something I’ve been meaning to do all along - create a script to make it much easier / quicker as well as form documentation about what programs / what setup I want for my machine.

I’ve been interested in Chocolatey and Boxstarter for ages to do this very thing. In this instance I didn’t bother using Boxstarter since I didn’t have any restarts in there, but I encourage people to look into it particularly if doing VM install scripts - it’s AMAZING.

I started writing this crazy PowerShell script to automate all the installs and settings I wanted and eventually I refactored it until it became like this. I think it’s really readable and maintainable and acts really well as documentation for myself.

While developing it I initially had a bunch of cinst calls, but the problem with that is each call incurs a 2s startup cost for some reason - this made developing it painful. In order to develop the script incrementally (I was doing it inside of a VM so I could trash it and start from the beginning again) I wanted three things:

  • Speed (if something is already installed I want it to skip it instantly, not wait for cinst to spin up for 2s)
  • Idempotency (I want to run and re-run the script again and again and again after making small changes to see their effect)
  • Fail fast (if something is wrong I want it to just fail and print an error so I can see what happened - I don’t want it to continue trying to install other things that might be dependent on the thing that failed)

I managed to achieve all of that and the other advantage I see in this approach is that it makes it really easy for me to reuse the script as an update mechanism if I decide to change things between re-paves. This is awesome and I think makes the script way more useful.

Long-story short: I’ve abstracted all of the main functionality into a PowerShell module and open-sourced it as repave.psm1 on GitHub. Check it out and feel free to fork it to create your own scripts and submit back a pull request with any fixes or additions.

It’s a bit rough around the edges since I’ve knocked it up in a hurry this weekend, but I did put in some initial documentation to describe all the functionality and there are two example scripts in there that use it.

Enjoy!

Read More

TeamCity deployment pipeline (part 3: using OctopusDeploy for deployments)

  • 3 min read

This post outlines how using OctopusDeploy for deployments can fit into a TeamCity continuous delivery deployment pipeline.

Maintainable, large-scale continuous delivery with TeamCity series

This post is part of a blog series jointly written by myself and Matt Davies called Maintainable, large-scale continuous delivery with TeamCity:

  1. Intro
  2. TeamCity deployment pipeline
  3. Deploying Web Applications
    • MsDeploy (onprem and Azure Web Sites)
    • OctopusDeploy (nuget)
    • Git push (Windows Azure Web Sites)
  4. Deploying Windows Services
    • MsDeploy
    • OctopusDeploy
    • Git push (Windows Azure Web Sites Web Jobs)
  5. Deploying Windows Azure Cloud Services
    • OctopusDeploy
    • PowerShell
  6. How to choose your deployment technology

Using another tool for deployments

If you can have a single tool to create your deployment pipeline and include continuous integration as well as deployments then there are obvious advantages in terms of simplicity of configuration and management (single set of project definitions, user accounts and permissions, single UI to learn, etc.). This is one of the reasons we created this blog series; we loved how powerful TeamCity is out of the box and wanted to expose that awesomeness so other people could experience what we were.

Lately, we have also been experimenting with combining TeamCity with another tool to take care of the deployments: OctopusDeploy. There are a number of reasons we have been looking at it:

  • Curiosity; OctopusDeploy is getting a lot of attention in the .NET community so it’s hard not to notice it - it’s worth looking at it to see what it does
  • If you are coordinating complex deployments then OctopusDeploy takes care of managing that complexity and the specification of the deployment process in a manageable way (that would start to get complex with TeamCity)
    • For instance, if you need to perform database migrations then deploy multiple websites and a background worker together then OctopusDeploy makes this easy
  • Visualising the deployment pipeline is much easier in OctopusDeploy, which is great when you are trying to get non-technical product owners involved in deployments
  • It gives you a lot more flexibility around performing deploy-time actions out-of-the-box and makes it easier to do build once packages
    • It is possible to do the same things with MsDeploy, but it is more complex to do
  • It has great documentation
  • It comes with plugins that make using it with TeamCity a breeze

We wouldn’t always use OctopusDeploy exclusively, but it’s definitely a tool worth having a good understanding of to use judiciously because it can make your life a lot easier.

Generating the package

OctopusDeploy uses the NuGet package format to wrap up deployments; there are a number of ways you can generate the NuGet package in TeamCity:

  • OctoPack (if you install OctoPack into your project then it’s dead easy to get the NuGet package - you can install the plugin to invoke it or pass through /p:RunOctoPack=true to MSBuild when you build your solution/project if using .NET)
  • TeamCity NuGet Pack step (if you are using a custom .nuspec file then it’s easy to package that up and automatically get it as an artifact by using TeamCity’s NuGet Pack step)
  • PowerShell or another scripting language (if you need more flexibility you can create a custom script to run that will package up the files)

Publishing the package to a NuGet feed so OctopusDeploy can access it

In order for OctopusDeploy to access the deployment packages they need to be published to a NuGet feed that the OctopusDeploy server can access. You have a number of options of how to do this from TeamCity:

  • Publish the package to TeamCity’s NuGet feed (this is easy - simply include the .nupkg file as an artifact and it will automatically publish to it’s feed)
  • Publish the package to OctopusDeploy’s internal NuGet server
  • Publish the package to some other NuGet server you set up that OctopusDeploy can access

Automating releases and deployments

In order to create releases and trigger deployments of those releases from TeamCity there are a number of options:

Read More