'Azure AD API request 401 Unauthorized
I have a standard Web API running on an Azure website with Azure AD authentication enabled, when browsing to the API in a browser I am able to login via the browser and gain access to the API.
The WPF desktop application however is receiving an Unauthorized response when submitting the request:
var authContext = new AuthenticationContext(authority, new FileCache());
var accessToken = await authContext.AcquireTokenAsync(apiResourceid, clientId, redirectUri,
new PlatformParameters(PromptBehavior.Auto));
// accessToken is valid
var apiUrl = "https://example.azurewebsites.net/api/list";
var request = new HttpRequestMessage(HttpMethod.Get, apiUrl);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.AccessToken);
var response = await httpClient.SendAsync(request);
The authentication is successfull and I can see the User info when debugging.
I do not have access to the Azure account but am confident the Service AD application is configured correctly to allow access to the Client AD application as when testing on an alternate account (not configured correctly) the AuthenticationContext.AcquireTokenAsync
method was failing.
I did notice that the - (Time is of course UTC)AuthenticationResult.ExpiresOn
is always in the past but see no way of extending it, should this be a future date?
Request:
GET https://example.azure
websites.net/api/categorisation HTTP/1.1
Authorization: Bearer eyJ0eXAiO...
Host: example.azurewebsites.net
Response:
HTTP/1.1 401 Unauthorized
Content-Length: 58
Content-Type: text/html
Server: Microsoft-IIS/8.0
WWW-Authenticate: Bearer realm="example.azurewebsites.net"
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=e35f2977dba55e6708887e762940f75c2a0fcb0a9df4e1cbe0d3f10a614c59b8;Path=/;Domain=example.azurewebsites.net
Date: Fri, 08 Jul 2016 07:51:13 GMT
You do not have permission to view this directory or page.
Update:
I have recreated the environment in an Azure account I have access to and still receive an Unauthorised response (works fine in a browser).
Solution 1:[1]
The issue appears to be with the "Authentication / Authorization" option in Azure Websites, when enabled the Web Api will not accept requests using the Authentication header. Disabling the option and using the Owin library alongside Azure AD has provided the solution required.
Solution 2:[2]
I know this is a few months old, but I wanted to throw out there what was causing this issue when I got it, and what I found out I could to do resolve it.
I had a site that I made that used SignalR. As I was developing I didn't secure the site, but when I went to secure the site with AzureAD I got the aforementioned error. The issue was that I had two startup classes, one in the application root, and one in App_Start. One was in the [applicationname].App_Start namespace, while one was in the App_Start namespace, and one was marked as the OWIN startup assembly.
My resolution was to remove the one in the App_Start folder, which was in the [appname].App_Start namespace, and add the proper SignalR and OWIN startup attributes to the one in the application root.
This solved my issue.
Hope this helps anyone else that runs into this!
Solution 3:[3]
I was also getting unauthorized errors and when obtaining a bearer token everything seemed to be working just fine.
My problem was in my resource id. It did not match my Azure-AD application's "App ID URI". I had an extra slash on the end when calling the AcquireTokenAsync method and I had entered it in Azure-AD without a slash.
// private string resourceId = "https://mywebsite.azurewebsites.net/"; // bad
private string resourceId = "https://mywebsite.azurewebsites.net"; // good
result = await authContext.AcquireTokenAsync(resourceId,
clientId, redirectUri, new PlatformParameters(PromptBehavior.Never));
So, make sure that your resource id matches your Azure-AD application's "App ID URI" exactly.
Notes:
- Every app service that is associated with Azure-AD has a corresponding Azure-AD application declaration of type Web app/API. This resource id is the "App ID URI" in the app service's Azure-AD application declaration.
- My resource id just happens to be my web site URL, but it could have been anything. The point is to match your "APP ID URI" of the Azure-AD application your trying to access.
Solution 4:[4]
Just my 5 cents. A bit late but if this helps somebody, awesome.
The issue for me was: Not sending Scopes properly along with the request.
Background: localhost node server using the official graph-api quickstart repo. REST API-server - app service running on azure, with Auth. (Express mode) enabled.
I made one critical mistake, and took way too long to figure it out. I was attempting to call the API with wrong access token.
It was possible to call the GRAPH API with just "openid", "profile" etc. scopes.
But to access the web API, I had to make the scopes include the API url Scopes = "https://yourwebsiteurl.com/allowed_scope" and not only "allowed_scope". Even the Application ID URI using the scope as "Application ID URI"/allowed_scope would not work, unless "Application ID URI" is the API url.
Azure throws a clear error if you attempt to call the API with a scope that does not exists or such, but in this case if you don't include the real API url within the scope - the error is just 401, nothing else.
The "Application ID URI" configuration in Azure actually does not seem to make difference, seems like it can be pretty much anything. - while the API call scope must include the whole API url.
Solution 5:[5]
Spent hours trying to figure out what the issue was and normally when that happens it is something simple. Postman provides RestSharp C# code and if you are using HttpClient it is simply a formatting issue. Use:
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer tokenValueLongStringHere);
Using httpClient.DefaultRequestHeaders.Authorization did not work in my case.
Solution 6:[6]
For me this was simply a case of using the wrong "secret" i.e. I accidentally used the SecretID instead of the value of the secret.
That was allowing me to get a code without an error message, but the code was not actually valid even though it looked like a proper code, and all I got back was the infamous 401 without a clue as to why it was happening.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Anth12 |
Solution 2 | Bryan |
Solution 3 | |
Solution 4 | Atte H |
Solution 5 | Dmitri K |
Solution 6 | user2728841 |