When to prefix the resource with "api://" when acquiring an AccessToken to gain access to a protected API - scope

There are two Application Registrations in Azure and both have the Application ID URI set to api://{GUID}.
However for the first application I need to provide the complete Application ID URI, including the prefix api://.
And for the second application, I only need to provide the {GUID} to get a working valid AccessToken.
Both applications are used on-behalf "access_as_user" scope.
What could be the difference ?

When you set the Application ID URI in the portal -> your App registration -> Expose an API, it will give you a value with format api://{GUID} by default, actually the {GUID} is the Application ID(i.e. Client ID) of the AD App.
To get the access token for an AAD protected API, providing the Application ID URI or Application ID for the resource will both work, in your case, make sure you have not changed the {GUID} manually i.e. make sure the {GUID} is the Application ID.

Related

Authenticating with oAuth token from service principal, produces a "the audience is invalid" error

I'm authenticating with a service principal that was setup by another developer. I'm trying to authenticate against an internally developed API. The ASP is working when the other developer uses the SDK to authenticate. But when I try with Postman, I'm getting a 401 with this error.
The issue is that I don't really know which resource to use. I believe the client ID, secret, and scope is correct. But I can't seem to find any info on the type of resource to use. Is there any way to determine this for an internally developed API? I've used a ton of publicly available ones. Just as testing, as I can't find any info. And leaving it blank does not work.
Assuming you have two applications created in azure ad app registration, one representing the client application and the other representing the api application, (or in app registration you must have selected the required client application) and then you must be using the client application to call the Web api application.
NOTE: When you call web API , make sure you are sending Access token (not Id token)
you track the token you get in jwt.io .
Azure AD audience must match the “aud” claim when.
The Audience must equal the AppId or client id set for the application.
Check iss value of token in jwt.io and see the version of login url. If it is v2 set manifest json in the app registration for the API to 2, as it may be by default be 2.
"accessTokenAcceptedVersion": 2
In the app registration if your exposed api is something like : api://xxxx-xxx-xxx ,then the client id in appsettings.json of your app must be the same.
Note:Client id may be configured differently according to application.
If above doesn't work try client ID : instead of api://
You may need to grant application permissions to your client applications (this is the role permissions you define yourself, you can find it in My APIs when you add permissions).Then you need to click the admin consent button to grant administrator consent for this permission.
Here I exposed scope >> api://xxx-xxx-xxx/access_as_user. Make sure to use the same scope configured in portal is included in application configuration. The scope should include the exposing resource's identifier (the Application ID URI) in the code too.
Here Ex: scopes: "api://xxxx-xxx-xxxx/access_as_user "
In postman Set Authorization header to refer a global variable 'Bearer {{bearerToken}}' and add the authorization data to Request Headers,
If you are using postman call back uri , uncheck the authorize using browser for callbackurl in postman configuration.(see enable-azure-ad-authentication| csharpcorner )

Azure Active Directory Token Has Incorrect Audience

I'm grabbing a token (using v2 enpoints) for a client app that has been exposed to an API app, all registed in Azure Active Directory. I can successfully get tokens that have the correct scopes, roles, etc., but I noticed the audience is not quite right. In the api app registration in the directory in Azure Portal, I go to the "Expose an API Blade" I have set the app id, and hovering over the info icon it says "The globally unique URI used to identify this web API. It is the prefix for scopes and in access tokens, it is the value of the audience claim. Also referred to as an identifier uri.". Mine Is something like "AppName-Prod-Api-Ad.azurewebsites.net". However, when getting an access token for this api, regardless of what scope I request, I get the client id of the api inside the token instead of the identifier uri. This isn't detrimental, but I would prefer that clients that consume my api not see my client id in their decoded token.
In v1, I could specify this identifier uri as the "resource" parameter while acquiring the token and the audience would be correct in the decoded token. This is not available in v2.
So, to summarize, I have the identifier uri set in the app registration for an API in Azure Active Directory, in Azure Portal, but the audience present in access tokens is instead the Client Id of the API, regardless of what scope I request. What am I doing wrong here?
I have noticed the same thing.
It could be that MS is trying to unify behaviour and return the same audience every time regardless of how you requested the token.
Either the doc or behaviour is wrong though.
Technically your API should still accept both audiences, client id and app ID URI.
In v1 especially you had to configure both since a client could acquire a token using either the API client id or URI leading to different audiences.
So I'd just accept both values as valid audiences.

Where to find Audience and Client id fields to use in Microsoft Flow's Azure REST API call?

I am trying to call a CDN Purge's Azure REST API url from Microsoft Flow.
I found HTTP - HTTP as the best action to use as Azure with HTTP sounds to be specialized on accessing resources not on (just) calling.
I created a Web app / API application in Azure Active Directory for this purpose.
The API calls need to be authenticated so I chose Active Directory OAuth.
I filled in the Tenant id into Tenant, chose Secret as Credential type and put a application's password (Keys) into the Secret field.
However, I am lost what to put into Audience and Client ID field.
I tried to search docs but did not find any relevant results. I presume one of those should be the App Id.
What to put into the Audience and Client ID fields and how to find the values?
Is there anything else required to do to make this working (like setting a permission to allow purging the CDN, updating manifests, assigning roles)?
PS: I am getting BadRequest. Http request failed as there is an error getting AD OAuth token: 'AADSTS50105: Application '<appId>' is not assigned to a role for the application '<appIdUri>'. in my most recent attempt.
The audience is the value of App ID URI that you registered (homepage below image)
The Client Id is nothing but the Application ID

POSTMAN: "You do not have permission to view this directory or page" with Bearer Token

I've got a website hosted on HostGator, let's say it's domain is https://example.com.
I also have an application hosted on Azure, with Active Directory Authentication enabled on the entire site (including the API component), let's say it's domain is https://example.azurewebsites.net
THE GOAL - To have a PHP file be executed (as a CRON job) on https://example.com and have the file first Authenticate itself with Azure's Active Directory, and then to pull data from https://example.azurewebsites.net/api/getValues via an HTTP GET call.
THE PROBLEM - Obviously, just calling the API without a bearer token will cause a 401, but I'm still getting a 401 even though I'm passing in what appears to be a valid Bearer Token.
Here's what I did:
Using https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-code -
I visited https://login.microsoftonline.com/{{tenant_id}}/oauth2/authorize?response_type=code&client_id={{client_id}} that was given to me through Azure AD Management Portal.
This returned:
https://example.com/?code={{really_long_string_of_code}}
I took this really_long_string_of_code and put it through postman as a body parameter called code, seen below:
As you can see, it returned the token seen above ^.
I then took this token and put it through another Postman call:
But the problem is, I still get that exact error message:
You do not have permission to view this directory or page.
I feel like I've tried everything. I've even went into the portal.azure and set the "allowed token audiences":
Anyone know any settings I can change to allow this sort of call to happen?
The evilSnobu have already explained this issue which caused by incorrect audience. I want to explain it more general to help understand this issue.
There are two concepts client and resource server in the OAuth 2.0 Authorization Framework(refer rfc6749). When the client calls the resource server, the resource server will verify the token passed in the request. For example, it will verify the signature, issuer, client id, audience etc.
client:
An application making protected resource requests on behalf of the
resource owner and with its authorization. The term "client" does
not imply any particular implementation characteristics (e.g.,
whether the application executes on a server, a desktop, or other
devices).
resource server:
The server hosting the protected resources, capable of accepting
and responding to protected resource requests using access tokens.
In your scenario, you were acquire the access_token for the Azure AD Graph(https://graph.windows.net). However, the audience you config at the portal doesn't match the aud claim in the access_token. To fix the issue, we can use the app registered at Azure AD as both client and resource. If that, we need to acquire the access token using the Application ID instead of App ID URI. And config this value as the ALLOWED TOKEN AUDIENCES on the Azure portal.
Or we can just to register two apps in Azure AD to represent the client app and resource app separately. And using the client app to acquire the token for the resource app. If this, the value of resource should the App ID URI of resource app and we also need to config it as the ALLOWED TOKEN AUDIENCES on the Azure portal.
Remove the trailing slash from Allowed Token Audiences, e.g.:
https://example.com
http://example.com
..or was is the other way around.. hmm..
401 Unauthorized when everything looks right is usually a trailing slash in the audience. Sometimes you need one, sometimes you don't. It should match whatever you or your middleware define as valid audience in the app code. You can also use the app GUID (Application ID) as audience.
Also, you seem to have graph.windows.net as resource, is that intentional?
You should really open that token and check the contents. The audience must match your API's URL.
This to me doesn't feel like the right way to authenticate machine2machine calls. You should probably just use TLS mutual authentication or simply send a hard coded secret over HTTPS in a header (yes, just like a Bearer token, but without the trust chain). On the API side, store it in App Settings and pick it up in your code from the associated environment variable. Use the same App Settings mechanism in the calling app code.
You could rotate this secret using a Function App that changes the App Setting every X days (PowerShell Function Apps have the Resource Management cmdlets available so you could use an SPN to login (Add-AzureRmAccount) and then call Set-AzureRmWebApp -AppSettings [...]).
Alternatively, you can store the secret in Azure Key Vault, and while a great service it borders on overengineering for your use case.

Azure AD server authentication, No permission to view directory

I have created a Xamarin.Forms project and a Table API project. Both have been created through the QuickStart menu in Azure Portal.
I have configured an Azure AD in my portal and I can successfully retrieve a token from the AD through my XForms app. But when I try to login to the Table API using the LoginAsync method from the MobileServiceClient, I receive a "You do not have permission to view til directory or page."
I have been looking through the following guides but with no luck.
How to configure your App Service application to use Azure Active Directory login
Add authentication to your Xamarin.Forms app
How to: Work with authentication
I have also look at the following question but didn't find a solution.
Cordova AAD server flow authentication hangs on Android and iOS
I Am thinking that I might be missing some specific authentication on the Table API project?
Here is my code and setups:
PCL PROJECT IN XFORMS
var ar = await authContext.AcquireTokenAsync(Constants.GraphResourceUri, Constants.ClientId, userCredintials);
JObject payload = new JObject();
payload["access_token"] = ar.AccessToken;
var client = new MobileServiceClient(Constants.ApplicationUrl);
var user = await client.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
The Constants.ClientID is the ClientId of the Native Client app and not the webserver. If I switch it around I get a 404.
EXAMPLE OF CONTROLLER FROM TABLE API PROJECT
[Authorize]
public class StatisticController : ApiController
TABLE API StartUp.cs CONFIGURATION
public void Configuration(IAppBuilder app)
{
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions());
ConfigureMobileApp(app);
}
MOBILE SERVICE AUTH SETUP
SINGLE SING-ON AD SETTINGS
NATIVE CLIENT APP SETTINGS
EDIT
I can duplicate the error through PostMan with the following setup:
I tried to include a "X-ZUMO-AUTH" header with the value of the access_token but with the same result. Still no permission. I also tried to exclude every header in the POST request but with no changes. Does this mean that POST requests from my mobile app or Postmand is not allowed?
If I manually browse to mysite.azurewebsites.net/.auth/login/aad in a browser, then I can log in with the users from my AD. So it seems that the AD is communicating correctly with the service and vise versa.
FIXED IT
Great thanks to mattchenderson! As he suggests I should change the constant GraphResourceUri to the client id of my service instead of the normal graph api. Along with adding a single instance of the client I can now successfully log in to my service.
POST requests are allowed, and I use Postman all the time for testing.
The most common cause of an issue like this is an audience validation issue. The audience is a property of the AAD token which says for what resource is this token valid. Given the code above, I would expect the audience to be equal to Constants.GraphResourceUri. My guess is that this is actually the graph API, and not your application, and that would cause a validation failure, although I would expect it to happen when you call LoginAsync(). I would suggest trying the web application client ID instead (that's the Client ID from the "MOBILE SERVICE AUTH SETUP" screenshot).
For an easier time debugging, you can take your AAD token to something like http://jwt.io, and that will help you see the token properties. "aud" is expected to the same as the client ID of the application as registered in the portal, and you want the issuer fields to match up as well. Make sure the token is not expired either.
When using the token to access protected APIs, there are two ways to provide it:
Use the token returned as part of LoginAsync() in the X-ZUMO-AUTH header of your request. This will automatically be done by the SDK for table operations using the same MobileServiceClient.
Use the AAD token directly according to the bearer token spec. That is, include it in the Authorization header, with the value "Bearer ", replacing token with your value.
Also, I see in your code that you are creating a new MobileServiceClient for the login operation. This is something we generally discourage, as the login information is attached to the MobileServiceClient object. If you lose a reference to that, you lose the login information (and a few other settings), and they won't be applied to your table operations. The recommendation is to use a single instance that is referenced elsewhere - for example, in Xamarin, a static variable within your shared code.

Resources