You’re probably tasked with modernizing a bunch of legacy apps for the cloud. For your .NET estate, that means you’re staring down a big ol’ anti-pattern for cloud-native architecture: domain joins and integrated windows authentication.
These components are an anti-pattern because they go against several of the pillars of a 12-factor app. In particular, they violate fast startups, dev/prod parity, isolating dependencies, and treating dependencies as a [loosely] coupled service.
Now, there’s nothing wrong with eschewing a few of these factors in the name of incremental benefits. But it’s really hard to boost agility if you don’t modernize this part of your .NET app.
So what are your options? Are your peers solving this problem head-on, or are they working around it? Let's look at three common scenarios of an application relying on Active Directory. Then we’ll take a moment to tackle Integrated Windows Authentication and dive in to strategies for removing the domain dependency.
Scenario 1: Shared domain auth between a web service and SQL database
This might be one of the most common use cases I’ve seen. The last thing a developer wants to do is fiddle with connection strings or revolving passwords. Meanwhile, the last thing a database admin wants to do is deal with credentials. Put domain authentication between them and both worlds are solved.
An alternate (and most cloud-native) solution is to move to SQL authentication. This means asking the database admin to establish credentials within the database, and share the username and password. If the connection string is held within web.config
, you can simply add the new creds in the string, and switch parameters like “Integrated Security” to false.
But don’t stop there! Putting creds in a config file is not a good idea. To continue our push to viable, cloud-native solutions, add the connection string to a secret store (like CredHub) instead of web.config. This simply “points” to the secrets, instead of storing them in plain text. Feed that pointer to the app through an environment variable. Then use Steeltoe to get the environment variable, and use the value to lookup the connection in the secret store.
Now the app can move between environments (and databases) easily, without changing the code! We’ve created portability by simply adjusting the environment variable’s value, as well as loosely coupling the backing service.
Scenario 2: An intranet website using domain auth
One of the greatest benefits to using domains is that identity management just works. On a domain-joined machine, one can navigate domain resources, like file shares and internal websites, without extra authentication steps. The user has already been validated by logging on to the desktop.
Intranet websites use Integrated Windows Authentication (IWA) to deliver this simplicity. This handy IIS module lets developers declare what domain permissions are required to access different parts of the site.
The question is: How can you decouple the app from the domain and IWA? Use single sign-on (SSO) services! With SSO, you don’t have to care how user identities are authenticated. When you choose industry standards like OAuth2 or OpenID Connect, and don’t worry about how the auth was done, or by what technology. Instead, treat identity management as a bound service to the app.
Modules like Pivotal Single Sign-On empower an operator to choose the identity provider and deliver a cloud-friendly option for the developer. You can also use Steeltoe’s cloud security SSO feature to manage the token exchange overhead. For more information on that, read about Microsoft’s Authentication with Common Data Service web services and Steeltoe’s security provider component.
Scenario 3: A web service communicating with another service using domain auth
It’s a classic design: One application depends on another app to perform a task. The identity of each service is established via the hosting IIS environment. Then, Active Directory (AD) manages the permissions for them to connect. The challenge is that neither app can function without its AD backing service. You've lost portability and loosely coupled backing services.
The modern alternative, similar to our second scenario, is single sign-on. The SSO service, if you’re using products that support OAuth and OpenID Connect, can abstract identity management away.
Replacing integrated windows auth with sign sign-on (SSO)
It might sound daunting, but these concepts are more similar than you think. Sign sign-on and windows auth both manage identities for you. Both are token-based, act as a bound service, and use “roles” and “groups.”
To use windows auth as the identity provider, you bind your app to the service by setting <authentication mode="Windows"/>
in web.config
. The negotiation with the domain is done between the host and Active Directory, not your app. In fact your app never formally collects a user credential. Instead, it is discovered within the browser (or they are challenged by some other mechanism). Once authenticated, the user’s authorization(s) are presented back to your app as roles. Your app can either expose its functions corresponding to that user’s role, or allow full interaction to any authenticated user. It’s a model that has served the .NET community very well.
With SSO, binding to an identity provider is done as an environment variable (not domain join). The variable’s value provides the service’s address (URL). On a respectable application platform (like Cloud Foundry), the variable is created and managed automatically by the platform when an application is bound to marketplace services. If you’re using the public cloud, you’ll need to wire this up manually.
With the SSO’s service address, a .NET Framework app uses OWIN capabilities to watch incoming requests for credentials and redirect to the identity provider. Similar to windows auth, once the user has been authenticated with the provider, they are redirected back to your app—bringing with them authorizations as a collection of roles. SSO in .NET Core has a very similar flow, but the runtime has a few more built-in SSO capabilities. Check out this documentation from Microsoft to learn more about using OWIN in ASP.NET Core.
If Windows auth and SSO are so similar, where are they different? Implementation. You are not moving a single application to the cloud; you’re moving a bunch. Changing the identity provider to SSO in every app is a monumental task.
Let’s look at strategies for modernizing a portfolio of applications to SSO, away from IWA.
Strategy 1: Buildpacks providing environment values
If you are using an application platform that uses buildpacks to create the container, then you have options. Within this strategy is the ability to “intercept” the container build. That means you can do things like writing environment variable values to web.config before boot. This creates a bridge between a legacy .NET app and a modern container platform, with no code change!
In our SSO case, you could intercept the container build and write the identity provider’s address to web.config. OWIN could consume this URL for handling negotiations.
Strategy 2: Spring Cloud Gateway with SSO
Yes, I said “Spring” in a post about .NET. Drop your assumptions about Spring only being for Java. It’s a polyglot world.
If you’re using the Pivotal Platform with SSO and Spring Cloud services installed, Spring Cloud Gateway can unleash all kinds of goodness for your .NET applications. The biggest: Your app doesn’t need to know anything of authentications or authorizations. Not a single line of code.
For your [legacy] .NET framework, that means not have to refactor your compiled app. Just turn off auth in web.config
( <authentication mode="None"/>
) and bind to an instance of Spring Gateway. During the bind you'll provide a (json) definition of which endpoints should be secured. The Gateway knows how SSO has been implemented on the platform and takes care of the rest! You can learn more about Spring Cloud Gateway here.
Strategy 3: Container with a sidecar
This is a very modern approach and probably my favorite. We normally think of a container as a single application running within. The idea with sidecars is to run a second complementing process also within the container. This second process is meant to offload some task from the primary app. In our case, it is meant to monitor incoming requests and start SSO negotiations. Once the identity provider passes everything back, the sidecar process forwards it on to the application process.
The application doesn’t have any authentication services directly bound. Instead, it just expects things to happen magically, similar to the Spring Gateway strategy. You can read more about Microsoft’s description of the side car pattern here, and also check out how to implement side cars in Pivotal Platform.
In the container community, the sidecar approach has gained significant adoption, very quickly. Service meshes like Istio find tremendous value in the pattern. But this is a new approach that hasn’t had time to “bake." That means you will have help, but the choices between how your .NET Core apps and .NET Framework apps use this will probably be a custom solution.
Get started with these strategies today
If you want to get started today, try Pivotal Web Services for free! This will give your .NET Core apps access to a complete modern runtime with SSO services ready to go.
.NET cloud-native training
Want to get deeper into creating cloud-native .NET apps? Attend the Pivotal Platform Acceleration Lab’s 4 -day .NET developer course. You’ll get hands-on cloud-native .NET training, learn best practices when creating microservices, and become a Steeltoe ninja!
Continue learning
Learn Cloud-Native .NET: Core Configuration Fundamentals with Steeltoe [webinar]