{
  "$type": "site.standard.document",
  "canonicalUrl": "https://johnnyreilly.com/posts/dual-boot-authentication-with-aspnetcore",
  "description": "The article explains how to have different authentication methods for two classes of users accessing an app. Code snippets are provided.",
  "path": "/posts/dual-boot-authentication-with-aspnetcore",
  "publishedAt": "2020-03-22T00:00:00.000Z",
  "site": "at://did:plc:yy3apqjlms24kso7ahn7lbmb/site.standard.publication/3mova7c4nho2b",
  "tags": [
    "asp.net",
    "auth",
    "azure"
  ],
  "textContent": "This is a post about having two kinds of authentication working at the same time in ASP.Net Core. But choosing which authentication method to use dynamically at runtime; based upon the criteria of your choice.\n\n\n\nAlready this sounds complicated; let's fix that. Perhaps I should describe my situation to you. I've an app which has two classes of user. One class, let's call them \"customers\" (because... uh... they're customers). The customers access our application via a public facing website. Traffic rolls through Cloudflare and into our application. The public facing URL is something fancy like https://mega-app.com. That's one class of user.\n\nThe other class of user we'll call \"our peeps\"; because they are _us_. We use the app that we build. Traffic from \"us\" comes from a different hostname; only addressable on our network. So URLs from requests that we make are more along the lines of https://strictly4mypeeps.io.\n\nSo far, so uncontroversial. Now it starts to get interesting. Our customers log into our application using their super secret credentials. It's cookie based authentication. But for our peeps we do something different. Having to enter your credentials each time you use the app is friction. It gets in the way. So for us we have Azure AD in the mix. Azure AD is how we authenticate ourselves; and that means we don't spend 5% of each working day entering credentials.\n\nLet us speak of the past\n\nNow our delightful little application grew up in a simpler time. A time where you went to the marketplace, picked out some healthy looking servers, installed software upon them, got them attached to the internet, deployed an app onto them and said \"hey presto, we're live!\".\n\nWay back when, we had some servers on the internet, that's how our customers got to our app. Our peeps, us, we went to other servers that lived on our network. So we had multiple instances of our app, deployed to different machines. The ones on the internet were configured to use cookie based auth, the ones on our internal network were Azure AD.\n\nAs I said, a simpler time.\n\nA new hope\n\nWe've been going through the process of cloudifying our app. Bye, bye servers, hello Docker and Kubernetes. So exciting! As we change the way our app is built and deployed; we've been thinking about whether the choices we make still make sense.\n\nWhen it came to authentication, my initial thoughts were to continue the same road we're travelling; just in containers and pods. So where we had \"internal\" servers, we'd have \"internal\" pods, and where we'd have \"external\" servers we'd have external pods. I had the good fortune to be working with the amazingly talented Robski. Robski knows far more about K8s and networking than I'm ever likely to. He'd regularly say things like \"ingress\" and \"MTLS\" whilst I stared blankly at him. He definitely knows stuff.\n\nRobski challenged my plans. \"We don't need it. Have one pod that does both sorts of auth. If you do that, your implementation is simpler and scaling is more straightforward. You'll only need half the pods because you won't need internal _and_ external ones; one pod can handle both sets of traffic. You'll save money.\"\n\nI loved the idea but I didn't think that ASP.Net Core supported it. \"It's just not a thing Robski; ASP.Net Core doesn't suppport it.\" Robski didn't believe me. That turned out to a _very good thing_. There followed a period of much googling and experimentation. One day of hunting in, I was still convinced there was no way to do it that would allow me to look in the mirror without self loathing. Then Robski sent me this:\n\nIt was a link to the amazing David Fowler talking about some API I'd never heard of called SchemeSelector. It turned out that this was the starting point for exactly what we needed; a way to dynamically select an authentication scheme at runtime.\n\nShow me the code\n\nThis API did end up landing in ASP.Net Core, but with the name ForwardDefaultSelector. Not the most descriptive of names and I've struggled to find any documentation on it at all. What I did discover was an answer on StackOverflow by the marvellous Barbara Post. I was able to take the approach Barbara laid out and use it to my own ends. I ended up with this snippet of code added to my Startup.ConfigureServices:\n\nIf you look at this code it's doing these things:\n\n1. Registering three types of authentication: Cookie, Azure AD and \"WhichAuthDoWeUse\"\n2. Registers the default Scheme to be \"WhichAuthDoWeUse\".\n\n\"WhichAuthDoWeUse\" is effectively an if statement that says, _\"if this is an external Request use Cookies authentication, otherwise use Azure AD\"_. Given that \"WhichAuthDoWeUse\" is the default scheme, this code runs for each request, to determine which authentication method to use.\n\nAlongside this mechanism I added these extension methods:\n\nFinally, I updated the SpaController.cs (which serves initial requests to our Single Page Application) to cater for having two types of Auth in play:\n\nThe code above allows anonymous requests to land in our app through the AllowAnonymous attribute. However, it checks the request when it comes in to see if:\n\n1. It's an internal request (i.e. the Request URL starts \"https://strictly4mypeeps.io/\")\n2. The current user is _not_ authenticated.\n\nIn this case the user is redirected to the https://strictly4mypeeps.io/login-with-azure-ad route which is decorated with the Authorize attribute. This will trigger authentication for our unauthenticated internal users and drive them through the Azure AD login process.\n\nThe mystery of no documentation\n\nI'm so surprised that this approach hasn't yet been better documented on the (generally superb) ASP.Net Core docs. It's such a potentially useful approach; and in our case, money saving too! I hope the official docs feature something on this in future. If they do, and I've just missed it (possible!) then please hit me up in the comments.",
  "title": "Dual boot authentication with ASP.NET"
}