401 error when authenticating to an Azure API App

2019-04-08 07:49发布

问题:

I have an API app that has been working fine with a Gateway Host and now that the gateway host is being deprecated I'm trying to follow the Migration Guide. I've redeployed my service using the 2.8.1 SDK and can log into the service with a browser using AAD or a Microsoft account and use Swagger to test the service. However, I'm trying to get a client to access the service using a ClientId and Secret. The code is able to get the access token from AAD but I always get a 401 error whenever I try to access one of the service resources.

When I debug the service I see the following in the log:

Microsoft.Azure.AppService.Authentication Verbose: 0 : Received request: GET https://[myService].azurewebsites.net/api/[myResource]
Microsoft.Azure.AppService.Authentication Warning: 0 : JWT validation failed: IDX10214: Audience validation failed. Audiences: 'https://[myService].azurewebsites.net/'. Did not match:  validationParameters.ValidAudience: '[AAD ClientId]' or validationParameters.ValidAudiences: 'http://[myService].azurewebsites.net'.
Microsoft.Azure.AppService.Authentication Information: 0 : Sending response: 401.71 Unauthorized
The thread 0x3b00 has exited with code 0 (0x0).

What appears to be the issue is that the Audience presented with the request is https but the validParameters.ValidAudiences collection only contains http.

I can't see any way of configuring the Audience and it appears that the http based audience is being set when Visual Studio 2015 creates the App Service. Is there a way of manually editing the ValidAudience collection?

For reference my client code is:

    private static void Main(string[] args)
    {
        string app_id_url = "https://[myService].azurewebsites.net/";
        string authority = "https://login.windows.net/[myDirectory].onmicrosoft.com/";
        string clientId = "[AAD ClientId]";
        string clientSecret = "[AAD Client Secret]";
        string apiBaseUrl = "https://[myService].azurewebsites.net/";

        string aadToken = GetTokenForApplication(authority, clientId, clientSecret, app_id_url);

        var apiClient = new HttpClient { BaseAddress = new Uri(apiBaseUrl) };
        apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", aadToken);
        var apiResponse = apiClient.GetAsync(apiBaseUrl + @"api/[myResource]").Result;
        string apiResponseContent = apiResponse.Content.ReadAsStringAsync().Result;
        Console.WriteLine(apiResponseContent);
    }

    public static string GetTokenForApplication(string authority, string clientId, string clientSecret, string resourceUrl)
    {
        AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
        ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
        AuthenticationResult authenticationResult = authenticationContext.AcquireToken(resourceUrl, clientCred);
        string token = authenticationResult.AccessToken;
        return token;
    }

回答1:

Your problem have something to do with the valid audiences. You may have 2 choices:

Option 1. Try to acquire the token with the WebAPI client ID as the AcquireToken method 'resource' parameter, instead of its Uri.

Option 2. If the previous method didn't work, you should have to modify the authentication settings of the App Service API, using Azure Resources Explorer. Navigate to your web API, find the authSettings JSON document under the config node, and modify (after having changed to Read/Write mode) the array allowedAudiences to match your needs. In your case you may have to change http to https



回答2:

In my ASP.NET 4.5 Web app I found that I had to specify the Valid Audiences to avoid a runtime exception being thrown.

public partial class Startup
{
    private static string _aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
    private static string _tenant = ConfigurationManager.AppSettings["ida:Tenant"];
    private static string _realm = ConfigurationManager.AppSettings["ida:Wtrealm"];
    private static string _metadataAddress = string.Format("{0}/{1}/federationmetadata/2007-06/federationmetadata.xml", _aadInstance, _tenant);
    private static string _authority = String.Format(CultureInfo.InvariantCulture, _aadInstance, _tenant);

    public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());

        app.UseWsFederationAuthentication(
            new WsFederationAuthenticationOptions
            {
                Wtrealm = _realm,
                MetadataAddress = _metadataAddress,
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidAudiences = new string[] { "spn:" + _realm }
                }
            }
        );
    }
}