dark-loop / functions-authorize

An ASP.NET Core based authentication and authorization middleware for HTTP triggered Azure Functions (In-Proc and Isolated)
Apache License 2.0
40 stars 5 forks source link

Support for multiple TokenValidators #68

Closed devrony closed 1 month ago

devrony commented 2 months ago

Hi, Is it possible to support multiple multiple token validators in Program.cs

Background: I have a requirement to allow logins for my Angular front-end app hosted in Azure Static Webs using Multitenancy accounts + Member/Guest Accounts. My App Registration is setup as Multitenancy and my Angular code is customized to determine proper Authority (i.e. https://login.microsoftonline.com/tenantId vs https://login.microsoftonline.com/organizations). But I am trying to setup the authorization check in the Function App. I've tried my tenantId, but errors when a Multitenant user authenticates.

Any suggestions on how to handle this? FYI, from my previous Issue post, you helped me setup the Policy to have an InWhitelistedTenants (InWhitelistedTenants) where I'm checking my list of allowed Tenant IDs I support. Ideally, I'd be able to leverage this list of Tenant IDs somehow to add multiple token validators or setup 1 that supports multiple Authorities (but only 1 Authority value exists today on Property).

Code-Snippet Example .AddJwtFunctionsBearer(options => { // this is what you should look for in a real-world scenario // comment the lines if you cloned this repository and want to test the library options.Authority = "https://login.microsoftonline.com/"; options.Audience = ""; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, }; });

Thanks. Any help always appreciated.

artmasa commented 2 months ago

@devrony, is the audience expected to be the same for all authorities? If that's the case, use https://login.microsoftonline.com/common as your single authority, which is Microsoft catch all tenants endpoint used specially for enterprise applications exposed to all EntraID tenants, and it should receive tokens from any Microsoft Azure tenant as the signing keys are all the same for any tenant. You already have the logic to ensure they only come from allowed tenants by using the Events logic.

devrony commented 2 months ago

Yes, the Audience should be the same which is my App Registration -> App (Object) Id. Will try this out and let you know. And yes, you are correct about the Tenant ID validation. Currently, I've set Validate values to false for now.

Thanks

devrony commented 1 month ago

I go the validation to work with multiple external tenants (i.e. Multitenant accounts) and with my own tenant (Guest and/or Member accounts). As you stated the I set the options.Authority = 'https://login.microsoftonline.com/common' or 'https://login.microsoftonline.com/organizations' (either work) instead of my own app's tenant id. Then, because I have array of Tenant IDs I allow, I added them to the TokaneValidationParameters.ValidIssuers array. I see in Fiddler, the validation logic makes the HTTP request to retrieve the proper signing keys (using the Authority) automatically so I believe all the default validation logic is working. Below is my setup. I tested from both a multitenant user and from a member user in my own app's tenant and no errors for validation.

Thanks again. I have finished all my validation and authorization logic for my app. This is awesome!

.AddJwtFunctionsBearer(options =>
{
      // Track a list of valid tenants used for authentication for this app
      List<string> validTenants = [settings.AppTenantId]; // <-- Add app's own tenant

     // Add the other external tenants (Multitenant)
     if (!string.IsNullOrWhiteSpace(settings.AllowListMultitenantIds))
    {
        // Remove any double spaces and extra spaces at begin/end, this could allow empty " " as valid tenant id
        validTenants.AddRange(settings.AllowListMultitenantIds.Trim().Replace("  ", "").Split(' '));
    }

    var audience = settings.AppClientId;
    var appTenantId = settings.AppTenantId;

    // Define the Authority for your identity provider (e.g., Azure AD, Auth0)
    // Authority URL is used for automatic discovery of token validation metadata (issuer, signing keys, etc.). 
    var authorityUrl = "https://login.microsoftonline.com/organizations/v2.0"; // <-- Organizations (or Common)

    // Define all the possible Issuers this application supports
    List<string> validIssuers = [

        // FUTURE: Example if a custom identity provider is used by organization (i.e. custom SAML/WS-Fed Idp)
        // $"https://sts.<org-domain-name>.com/fed" 
    ];

    // Add our other supported external tenants (Multitenant orgs)
    foreach (var tenantId in validTenants)
    {
        validIssuers.Add($"https://login.microsoftonline.com/{tenantId}/v2.0"); // For AzureAD/Entra (OIDC)
        validIssuers.Add($"https://sts.windows.net/{tenantId}");  // For legacy AD or WS-Fed/SAML identity providers
    }

    options.Authority = authorityUrl;
    options.Audience = audience;

    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuers = validIssuers.ToArray(),
        ValidateIssuer = true,
        ValidateIssuerSigningKey = true,
        ValidateAudience = true,
        ValidateLifetime = true
    };
}
artmasa commented 1 month ago

@devrony, really glad to hear!