How to support multiple tenants and secret tokens in azure ad scim provisioning

1.6k views Asked by At

I am trying to create Azure AD provisioning for our Saas product (using scim2). I want multiple customers to be able to connect with their Azure AD tenant.

Microsoft has reference code here: https://github.com/AzureAD/SCIMReferenceCode

However, that is setup to only allow one tenant and also to not use the "secret token" that you set up in azure ad. Even tho the comment specifically states the secret token should not be left empty for production.

Here is the important piece of code from the reference project

// Leave the optional Secret Token field blank
            // Azure AD includes an OAuth bearer token issued from Azure AD with each request
            // The following code validates the Azure AD-issued token
            // NOTE: It's not recommended to leave this field blank and rely on a token generated by Azure AD. 
            //       This option is primarily available for testing purposes.
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.Authority = this.configuration["Token:TokenIssuer"];
                options.Audience = this.configuration["Token:TokenAudience"];
                options.Events = new JwtBearerEvents
                {
                    OnTokenValidated = context =>
                    {
                        // NOTE: You can optionally take action when the OAuth 2.0 bearer token was validated.

                        return Task.CompletedTask;
                    },
                    OnAuthenticationFailed = AuthenticationFailed
                };
            });

With that code it works assuming Token:TokenIssuer setting is https://sts.windows.net/<tenant_id>/ where tenant_id is the actual tenant id and TokenAudience is 8adf8e6e-67b2-4cf2-a259-e3dc5476c621 (non gallery app). But it only works if I leave the "Secret token" empty when I set it up in azure ad (non gallery app under Entrprise applications).

I have tried all sorts of things, adding OnChallenge tells me a challenge is sent if I set the "Secret token" but beyond that I am not getting much further.

Any sample code for handling multiple tenants and secret tokens here would be amazing

Update: Using options.TokenValidationParameters.IssuerValidator I can validate the issuer and thus make that work with multiple tenants. What I really can't get past right now is making a call work when I enter a "Secret token" here: (see picture) enter image description here

2

There are 2 answers

2
Harshita Singh On
4
Rickard Liljeberg On

So I figured out that what they want is a JWT token in that field that I generate.

So first I created a method that generates a web token

private string GenerateJSONWebToken()
    {
        // Create token key
        SymmetricSecurityKey securityKey =
            new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Token:TokenSigningKey"]));

        SigningCredentials credentials =
            new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        // Set token expiration
        DateTime startTime = DateTime.UtcNow;
        DateTime expiryTime = startTime.AddMinutes(120);
        
        // Generate the token
        JwtSecurityToken token =
            new JwtSecurityToken(
                configuration["Token:TokenIssuer"],
                configuration["Token:TokenAudience"],
                null,
                notBefore: startTime,
                expires: expiryTime,
                signingCredentials: credentials);

        string result = new JwtSecurityTokenHandler().WriteToken(token);
        return result;
    }

In my appsettings.json I added

{
"Logging": {
    ...
},
"Token": {
    "TokenAudience": "xxx-xxx-xxx-xxx",
    "TokenIssuer": "https://sts.windows.net/yyyy-yyyy-yyyy/",
    "TokenSigningKey": "zzz"
}
}
  1. Token Audience I set to 8adf8e6e-67b2-4cf2-a259-e3dc5476c621 as can be read about here https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups.

TL;DR The audience for the token will be the application template ID for the application in the gallery, the application template ID for all custom apps is 8adf8e6e-67b2-4cf2-a259-e3dc5476c621

  1. yyyy part of TokenIssuer is the tenant id of the azure ad tenant
  2. zzz from signing key is simply a key of your choosing.

Now finally I generated a token containing the values from appsettings.json. I then pasted this key into the "Secret token" field in Azure AD.

Finally, how to make this multi tenant (my next steps)

  1. Remove Token:TokenIssuer from appsettings.json
  2. When you call GenerateJSONWebToken send in the client Azure AD tenant ID and use that instead of static value from appsettings.json (Either your client will give you this, or you have it from connecting your app to them)
  3. In startup.cs notice I have already implemented IssuerValidator. Update this to validate not against appsettings.json but your data store.