I'm having difficulty getting a basic OBO example up and running. Any insight is greatly appreciated as I haven't been able to find a complete example that uses my scenario:
A client app (WebApp1) that calls api1, which in turn calls api2 via OBO using the Microsoft.Identity.Web wrapper, running on 3 different localhost ports. In the portal, api1 exposes FunctionA and api2 exposes FunctionB, and WebApp1 has permission+granted consent to api1 and api1 has permission+granted consent to api2. Webapp1 & API1 as clients have secrets defined.
It fails with 401 unauthorized - invalid audience:
AuthenticationHeaderValue.Parameter "error=""invalid_token"", error_description=""The audience 'api2' is invalid"""
My setup:
*appsettings.json - webapp1
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "mydomain.onmicrosoft.com",
"TenantId": "123456",
"ClientId": "webapp1",
"ClientSecret": "webapp1 secret"
"CallbackPath": "/signin-oidc"
},
"DownstreamApi": {
"BaseUrl": "api://api1",
"Scopes": "FunctionA"
}
*appsettings.json - api1
"AzureAd": {
Instance, domain, tenantid = same...
"ClientId": "api1",
"Scopes": "FunctionA",
"ClientSecret": "api1 secret"
},
"api2": {
"BaseUrl": "api2 client id (without API:// prefix",
"Scopes": "FunctionB"
},
}
*api2
{
"AzureAd": {
"Instance, domain, tenantid = same
"ClientId": "api2",
"Scopes": "FunctionB"
},
webapp1 program.cs snippet:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi() //initialScopes
.AddInMemoryTokenCaches();
api1 program.cs snippet:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("api2", builder.Configuration.GetSection("api2"))
.AddInMemoryTokenCaches();
api1 controller (option 1 results in unauthenticated call with null scopes, option 2 results in 401 unauthorized - invalid audience):
[HttpGet(Name = "GetWeatherForecast")]
public async Task<IActionResult> Get()
{
// option 1 -
_scopes =
configuration["api2:Scopes"].Split(' ').Select(x => $"{_instanceURL}/{x}");
var testresponse = await _downstreamApi.CallApiForUserAsync(
"api2",
options =>
{
options.BaseUrl = "https://localhost:7075/";
options.RelativePath = "weatherforecast";
options.Scopes = _scopes;
options.HttpMethod = HttpMethod.Get;
}
); **// 401 - invalid audience**
// option 2
var accessToken1 = await _tokenAcquisition.GetAccessTokenForUserAsync(_scopes);
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken1);
httpClient.BaseAddress = new Uri("https://localhost:7075/");
var request = new HttpRequestMessage(HttpMethod.Get,
"weatherforecast");
var response = await httpClient.SendAsync(request); **// 401 - invalid audience**
if (response.StatusCode != HttpStatusCode.OK)...