Multi-tenant WebAPI – simple admin consent


The VS.NET 2015 wizard for adding authentication to ASP.NET WebAPI projects does not support using the multi-tenant option. Here are some notes on how to implement this option yourself and create OAuth2 access tokens to such resources using separate tenants. This is not meant as an attempt to document features – rather as a record of my own experience in this area.

Step 1: Configure app as multi-tenant

In Azure AD terms, a multi-tenant application is one which is registered in one Azure AD tenant (resource tenant) but can be accessed using tokens issued by other Azure AD tenants (client tenants). To use this option with ASP.NET WebAPI, configure the application with single tenant option first (supported by the wizard), then use the Azure AD portal to change the application’s tenancy flag to multi-tenant.

This is sufficient for any client application from any tenant to acquire an access token to this resource, using the client credentials flow only, i.e. using the application credentials:

var tokenResp = await ctx.AcquireTokenAsync(“resource app id, e.g. 75b31c6d-d907-…”, new ClientCredential(ClientId, ClientKey));

 

A client cannot acquire a token using other OAuth2 flows. Nor does the token acquired using the client credentials flow include any role permissions – the client’s tenant does not in fact know anything about the resource application, which would define the roles.

Step 2: Expose the resource in the client tenant

To add the resource application to the client’s tenant so as to be able to select roles that the client can use in the resource you need to implement the admin consent flow: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-devhowto-multi-tenant-overview#understanding-user-and-admin-consent

At the minimum, you need to ask the admin of the client tenant to navigate a browser to a url constructed as follows:

https://login.windows.net/common/oauth2/authorize?response_type=code&client_id=<resource guid or id url>&resource=https%3A%2F%2Fgraph.windows.net&redirect_uri=<reply url registered in ADD for this resource app>&prompt=admin_consent

Azure AD will then prompt the admin user to authenticate and grant consent for the resource app to use in this tenant. The reply url may not have any logic behind it or even be accessible. It must be valid in the sense that it must have been registered as a valid reply url for this application. If it does not have any implementation behind it, the resource app will not be notified about the consent.

Once the admin consent has been granted the resource application will appear among the list of registered application in the client tenant and you will be able to use the Add application task in the classic portal to the resource to a client and, more importantly configure which application permissions exposed by the resource should be granted to the client (roles in the access token).

Step 3: Record client tenant as valid issuer

The reply url used in the previous step will receive a response from Azure AD as to whether the client tenant consented to the resource using it. If it is not handled, the resource will not know that a new tenant may be used to issue tokens and so fail their validation. In that case, some other way of recording new tenants will need to be implemented. Alternatively, the resource may handle the response and record the new tenant. An ASP.NET WebAPI controller which initiates a user (admin) consent flow as described in step 2 and records a new tenant when consent is granted is shown below:

   public class ConsentController : ApiController

{

public async Task<IHttpActionResult> Get(

string code = “”,

string session_state = “”,

bool admin_consent = false)

{

var clientId = ConfigurationManager.AppSettings[“ida:ClientId”];

// Initially admin_consent is false, AAD should respond with true when admin has consented

if (!admin_consent)

{

var req = String.Format(

“https://login.windows.net/common/oauth2/authorize?response_type=code&client_id={0}&resource={1}&redirect_uri={2}&prompt={3}”,

Uri.EscapeDataString(clientId),

Uri.EscapeDataString(“https://graph.windows.net”),

Uri.EscapeDataString(Request.RequestUri.GetLeftPart(UriPartial.Authority).ToString() + “/api/consent”),

Uri.EscapeDataString(“admin_consent”));

return Redirect(req);

} else if (!String.IsNullOrEmpty(code))

{

var clientSecret = ConfigurationManager.AppSettings[“ida:ClientSecret”];

var ctx = new AuthenticationContext(“https://login.windows.net/common”, null);

var res = await ctx.AcquireTokenByAuthorizationCodeAsync(

code,

new Uri(Request.RequestUri.GetLeftPart(UriPartial.Authority).ToString() + “/api/consent”),

new ClientCredential(clientId, clientSecret));

var tenantId = res.TenantId;

//TODO: store the tenant if somewhere so that Startup.Auth.cs can validate subsequent tokens

 

return Ok(“<b>tenant registered</b>”);

}

return null;

}

 

To summarize, while multi-tenant Web Apps (UI apps) have a well-documented way of providing for new tenant registration, which includes new tenant’s admin consent, Web API do not. Since they typically do not expose any UI there is no obvious place from which to drive the user interaction needed to initiate Azure AD consent flow. However, even without exposing any UI, using the approach described above a multi-tenant Web Api provider can provide the endpoints, without any UI to initiate the flow and, optionally receive the new tenant’s object id.

Comments (0)

Skip to main content