How to Integrate Azure Active Directory B2C in ASP.NET 4.5 WebAPI
Tags: webdev, programming, azure
Published: 2023-07-30 16:01:21

### Hey there, my fellow tech-savvy readers!

Now, picture this: _you've got a trusty ASP.NET 4.5 WebAPI application chugging along like a well-oiled machine,_ doing its thing, and you're thinking, "Why change something that works just fine?" 💼🕶️ 

**But as the tech world evolves faster than a cheetah chasing its morning coffee,** I started feeling the pressure once I set sails to incorporate Azure Active Directory B2C into my relatively older codebase. 💨☁️ 

_And there's the rub! 🌶️ Integrating ADB2C into this tried-and-true ASP.NET version came to me like introducing a jazz band to a black-tie ball. 😂🎷_

But fear not as I'm here to help you navigate these waters with a smile on your face and a skip in your step. 💃🕺 

I'll show you how to combine the old and the new in a seamless dance that'll make your ASP.NET 4.5 WebAPI look and feel like it's on cloud nine! ☁️💫 

So grab your coding wands, and let's sprinkle some Azure Active Directory B2C fairy dust over your beloved ASP.NET 4.5 WebAPI! 🧙‍♂️✨

## Assumptions
At this point, I'm assuming the following:
1. You have already set up your Azure Account
2. You already have the necessary information and credentials required to create the hand-shake between Azure and your ASP.NET App
3. You have already defined scopes, and policies

## Step 1
Start by adding the necessary credentials and information in your Web.config file. Per my experience, you're going to be needing the following

```
<add key="AZB2C:AadInstance" value="https://<YOUR_AZURE_DOMAIN>.b2clogin.com/{0}/{1}/v2.0/.well-known/openid-configuration" />
<add key="AZB2C:Tenant" value="<YOUR_AZURE_DOMAIN>.onmicrosoft.com" />
<add key="AZB2C:TenantId" value="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" />
<add key="AZB2C:ClientId" value="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" />
<add key="AZB2C:ClientSecret" value="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<add key="AZB2C:RedirectUri" value="http://localhost:3000/" />
<add key="AZB2C:SignUpSignInPolicyId" value="SIGN_IN_POLICY_NAME" />
<add key="AZB2C:ResetPasswordPolicyId" value="PASSWORD_RESET_POLICY_NAME" />
<add key="AZB2C:SOME_SCOPE_NAME" value="Scope.Something.Something" />
<add key="AZB2C:ANOTHER_SCOPE_NAME" value="Scope.Yes.AnotherSomething" />
```

We are going to be basing our code off of these credentials.

## Step 2
Create a **civilized ** way of using these environment variables in your code by creating a static class that extracts all the env variables from the Web.config into a Static Class Instance. This way, you can easily access these environment variables all across your application.

```
    public static class AzureEnvironmentVariables
    {
        public static string AadInstance = ConfigurationManager.AppSettings["AZB2C:AadInstance"];
        public static string Tenant = ConfigurationManager.AppSettings["AZB2C:Tenant"];
        public static string TenantId = ConfigurationManager.AppSettings["AZB2C:TenantId"];

        public static string ClientId = ConfigurationManager.AppSettings["AZB2C:ClientId"];
        public static string ClientSecret = ConfigurationManager.AppSettings["AZB2C:ClientSecret"];
        public static string RedirectUri = ConfigurationManager.AppSettings["AZB2C:RedirectUri"];

        public static string SignUpSignInPolicyId = ConfigurationManager.AppSettings["AZB2C:SignUpSignInPolicyId"];
        public static string ResetPasswordPolicyId = ConfigurationManager.AppSettings["AZB2C:ResetPasswordPolicyId"];

        public static string DefaultPolicy = SignUpSignInPolicyId;

        public static string FirstScope = AadInstance + ConfigurationManager.AppSettings["api:SOME_SCOPE_NAME"];
        public static string SecondScope = AadInstance + ConfigurationManager.AppSettings["api:ANOTHER_SCOPE_NAME"];
        public static string[] Scopes = new string[] { FirstScope, SecondScope };

        public static string B2CAuthority = string.Format(AadInstance, Tenant, DefaultPolicy);
        public static string WellKnownMetadata = $"{AadInstance}/v2.0/.well-known/openid-configuration?p={SignUpSignInPolicyId}";
    }
```

## Step 3
### Create your auth provider
No go ahead and create a new file in your `App_Start` folder called `Startup.Auth.cs`

In this file, you are going to create a Startup Extension Method that will be triggered on Startup time and register your Auth Provider, called Startup.Auth into your application.

```
public partial class Startup
    {
        public static string AadInstance = AzureEnvironmentVariables.AadInstance;
        public static string Tenant = AzureEnvironmentVariables.Tenant;
        public static string TenantId = AzureEnvironmentVariables.TenantId;
        public static string ClientId = AzureEnvironmentVariables.ClientId;
        public static string SignUpSignInPolicy = AzureEnvironmentVariables.SignUpSignInPolicyId;
        public static string DefaultPolicy = SignUpSignInPolicy;

        public void ConfigureAuth(IAppBuilder app)
        {
            TokenValidationParameters tvps = new TokenValidationParameters
            {
                ValidAudience = ClientId,
                AuthenticationType = Startup.DefaultPolicy,
                SaveSigninToken = true,
            };

            string metadataEndpoint = String.Format(AadInstance, Tenant, DefaultPolicy);

            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
            {
                AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(metadataEndpoint))
            });
        }
    }

    public class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityKeyProvider
    {
        public ConfigurationManager<OpenIdConnectConfiguration> _configManager;
        private string _issuer;
        private IEnumerable<SecurityKey> _keys;
        private readonly string _metadataEndpoint;

        private readonly ReaderWriterLockSlim _synclock = new ReaderWriterLockSlim();

        public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
        {
            _metadataEndpoint = metadataEndpoint;
            _configManager = new ConfigurationManager<OpenIdConnectConfiguration>(metadataEndpoint, new OpenIdConnectConfigurationRetriever());

            RetrieveMetadata();
        }


        public string Issuer
        {
            get
            {
                RetrieveMetadata();
                _synclock.EnterReadLock();
                try
                {
                    return _issuer;
                }
                finally
                {
                    _synclock.ExitReadLock();
                }
            }
        }

        public IEnumerable<SecurityKey> SecurityKeys
        {
            get
            {
                RetrieveMetadata();
                _synclock.EnterReadLock();
                try
                {
                    return _keys;
                }
                finally
                {
                    _synclock.ExitReadLock();
                }
            }
        }

        private void RetrieveMetadata()
        {
            _synclock.EnterWriteLock();
            try
            {
                OpenIdConnectConfiguration config = Task.Run(_configManager.GetConfigurationAsync).Result;
                _issuer = config.Issuer;
                _keys = config.SigningKeys;
            }
            finally
            {
                _synclock.ExitWriteLock();
            }
        }
    }
```

### Step 4
Now go ahead and call your Startup extension method in the `Configurations` function within your Startup.cs file to actually register your Auth Provider.

```
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);

            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config);
        }
    }
```

### Step 5
Finally, add the `Authorize` annotation in your controllers on the Endpoints that you'd like to protect and avoid any unauthorized access.


```
[Authorize]
public class YourAwesomeApiController: ApiController
```


And there you have it! 🎉✨ 

You've now unlocked the secrets of integrating Azure Active Directory B2C in your ASP.NET 4.5 WebAPI, proving once and for all that old dogs can learn some fantastic new tricks! 🐶🎩 

Happy coding and may your cloud adventures be filled with endless fun and success! 🌌💖✨