United Kingdom: +44 (0)208 088 8978

SAFE Authentication with Azure Active Directory – Part 1.

Authentication can be a complex topic, but getting started with the SafeStack and Azure is relatively simple and very similar to any other ASP .NET application.

Many software systems require users to log in to prove who they are.

This process is referred to as authentication.

Once a user has been authenticated, we can perform authorisation, which is enforcing what they can do.

When authenticating users, you have a few options - you could keep an internal database of users, or perhaps use one of the many identity services available.

Whatever you choose, you can easily plug it into your ASP.NET application and begin enforcing your access policies.

Active Directory

In this example we will look at Azure Active Directory which provides a rich suite of authentication services.

The first thing you will need to do is to create an Azure account and log into the portal.

Here you can create a tenant for your organisation and provision a directory which will allow you to manage user accounts and access policies.

App Registration

Once the directory has been set up, you will need to provision an App Service resource.

This acts as a host for your web application or mobile back-end. External parties communicate with the App Service, which in turn controls access to resources and forwards messages on to your application.

For an automated, repeatable and reliable way of deploying and updating your app resources using F#, check out Farmer. This is included in the SAFE V2 template. Simply execute dotnet fake build -t Azure to deploy your app.

After you have provisioned an app service you can register it with Active Directory, which will allow it to authenticate users.

You will need the login / logout callback URIs from here in the next step.

Configuration

In order for your app to connect to Active Directory, you must include some configuration data such as the ID of your directory and app (referred to as TenantId and ClientId respectively).

You will also need to include the callback urls from the registration step. Active Directory will redirect to these after a successful login or logout has occurred (referred to as CallbackPath and SignedOutCallbackPath).

For a deeper look at ASP .NET Core Configuration with the SAFE stack, including how to keep your secrets secure, check out my previous blog.

App Setup

Setting up authentication services in a SAFE app is very similar to any other ASP .NET Core application. You configure the app with IApplicationBuilder and register services in an IServiceCollection during setup.

You will need to run dotnet paket add Microsoft.Identity.Web -p Server to get access to the AddMicrosoftIdentityWebAppAuthentication IServiceCollection extension below.

The SAFE stack template has a file in its Server project called Server.fs. This contains the Saturn application builder and is the equivalent to the Setup class found in C# ASP .NET Core projects (frequently seen in Microsoft documentation).

open FSharp.Control.Tasks
open Fable.Remoting.Server
open Fable.Remoting.Giraffe
open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.Configuration
open Microsoft.Extensions.DependencyInjection
open Microsoft.Identity.Web
open Saturn
open Giraffe
open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Hosting
open System

open Shared

let configureApp (app:IApplicationBuilder) =
    app.UseAuthentication()

let configureServices (services : IServiceCollection) =

    let config = services.BuildServiceProvider().GetService<IConfiguration>()

    services
        .AddMicrosoftIdentityWebAppAuthentication (config, openIdConnectScheme = "AzureAD")
        |> ignore

    services

let app =
    application {
        url "http://0.0.0.0:8085"
        service_config configureServices
        app_config configureApp
        use_router routes
        memory_cache
        use_static "public"
        use_gzip
    }

run app

Routing

Now that you have enabled authentication, you just need to tell ASP .NET Core which endpoints to secure.

Giraffe, the functional wrapper around ASP which forms the foundation for Saturn, provides us with a simple helper function to achieve this, requiresAuthentication. You can easily specify whether a user should be blocked and shown an error or redirected to login when unauthorised.

Out of the box, the SAFE V2 template uses Fable Remoting to connect to the Client side of your application, and the generated API is connected directly to the use_router function in the application builder.

In order route requests through the authentication pipeline before they reach your API, you can simply define your own array of routes instead.

Due to restrictions on cross-site cookies requiring secure transport and the way SAFE runs with the webpack dev server, authentication during local development has issues on Chromium-based browsers. It has no issues when actually deployed to Azure however. As a workaround you can either use Firefox locally or selectively disable auth when in a dev environment as I have shown below.

let buildRemotingApi api next ctx = task {
    let handler =
        Remoting.createApi()
        |> Remoting.withRouteBuilder Route.builder
        |> Remoting.fromValue (api ctx)
        |> Remoting.buildHttpHandler
    return! handler next ctx }

let authScheme = "AzureAD"

let isDevelopment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") = Environments.Development;

let noAuthenticationRequired nxt ctx = task { return! nxt ctx }

let requireLoggedIn : HttpFunc -> HttpContext -> HttpFuncResult =
    if isDevelopment then
        noAuthenticationRequired
    else
        requiresAuthentication (RequestErrors.UNAUTHORIZED authScheme "My Application" "You must be logged in.")

let api ctx =
    { Endpoint = endpoint1Func }

let routes =
    choose [
        subRoute "/api" (requireLoggedIn >=> buildRemotingApi api)
    ]

This shows how to block unauthorised access to your API. It won't, however, redirect you to the login page. This requires some extra changes to the out-of-the-box SAFE webpack and FAKE configurations. Check out part two of this blog where I show you how to configure the 'auth challenge' scenario.

Conclusion

Although authentication and authorisation are deep topics which can get very complex, I hope you can see that getting started with the SAFE stack is broadly similar to any other ASP .NET application.

Once you have jumped through the setup hoops once or twice you can get a login service set up for your application very quickly and simply, allowing Azure to do the heavy lifting for you and giving you more time to focus on the problem you are actually trying to solve.