I have an Azure Function setup with a Web Trigger endpoint that I want to use as my backend for a React app. Without authentication setup, it works fine. When I setup App Service Authentication using AD, it works fine when I access directly via the browser (after authentication), but when I try to access from JS providing the Bearer token I get a 401.
const response = await axios.get(`${window.apiUrl}api/jobs`, {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token.accessToken,
},
});
The client app is running on Azure and is registered as an Azure AD app. I am able to authenticate, query AD, and use MS Graph API successfully.
I am using the built-in Azure App Services AD authentication. I have the Client ID set as the same client ID as the previously mentioned Azure AD app, as well as the same Issuer Url.
Attempt to get session token:
const accessToken = await authProvider.getAccessToken();
const idToken = await authProvider.getIdToken();
const login = await axios.post(
'https://<appname>.azurewebsites.net/.auth/login/aad',
{ access_token: accessToken.accessToken },
{
headers: {
'Content-Type': 'application/json',
},
},
);
More Info
My aud claim is 00000003-0000-0000-c000-000000000000. In Azure Portal, my Azure Function is configured to use the same Azure AD App as my SPA. I am using MSAL.js for authentication in my SPA. I am requesting the User.Read and Directory.Read.All scopes.
Microsoft has published a how-to article entitled Advanced usage of authentication and authorization in Azure App Service. In the section on validating tokens from providers, it says:
In a client-directed sign-in, the application signs in the user to the
provider manually and then submits the authentication token to App
Service for validation (see Authentication flow). This validation
itself doesn't actually grant you access to the desired app resources,
but a successful validation will give you a session token that you can
use to access app resources.
So you need to get the session token to access app resources.
Request:
POST https://<appname>.azurewebsites.net/.auth/login/aad HTTP/1.1
Content-Type: application/json
{"id_token":"<token>","access_token":"<token>"}
Response:
{
"authenticationToken": "...",
"user": {
"userId": "sid:..."
}
}
Once you have this session token(authenticationToken), you can access protected app resources by adding the X-ZUMO-AUTH header to your HTTP requests
GET https://<appname>.azurewebsites.net/api/products/1
X-ZUMO-AUTH: <authenticationToken_value>
You cannot request a token for Microsoft Graph and use it to call your own API. The Audience "00000003-0000-0000-c000-000000000000" means "intended for Microsoft Graph".
In MSAL, when you request the token, you need to adjust the scopes. Delete User.Read, delete Directory.Read.All and add the "Application ID URI" with a /.default at the end of it. You can find the Application ID URI in the "Expose an API" blade of your application registration on portal.azure.com. Example: https://SaeedApp/.default
If you need to do both, you can only request an access token for one resource at a time. However, you can request as many scopes as you need for one resource (User.Read and Directory.Read.All are both scopes for the same resource).
So you'll need to make two sets of requests:
1) to get an access token with all the scopes you need for Microsoft Graph
2) to get an access token with all of the scopes you need for your API
The reason behind why: If I could take an access token that's intended for your API and call Microsoft Graph with it, then that would open up "replay" attacks where one Resource API is hacked and the hacker that controls one resource can now reply access tokens it receives from clients against all the other Resource APIs.
Related
I have an Azure AD B2C tenant setup with an Angular app on the front-end using Authorization Code Flow with PKCE and a back-end api. Everything is working fine. I now have a need to allow the user to access certain pages on the front-end anonymously. I would prefer to still protect the apis these pages will call using the same access token.
I have followed the article here to set up Client Credentials flow. I am able to get an access token successfully using Postman and use it to call my back-end apis fine. However, when I try to do the same from the Angular app, I get the following error:
{"error":"invalid_request","error_description":"AADB2C99067: Public Client XXXXX-XXXXXX is not supported for Client Credentials Grant Flow\r\nCorrelation ID: 2b3346ef-1828-4900-b890-06cdb8e0bb52\r\nTimestamp: 2022-07-28 04:12:21Z\r\n"}
Below is the code snippet I am using in Angular to retrieve the access token.
const urlencoded = new URLSearchParams();
urlencoded.set('grant_type', 'client_credentials');
urlencoded.set('client_id', '<clientid>');
urlencoded.set('client_secret', '<clientsecret>');
urlencoded.set('scope', '<scope>');
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }),
};
const url = 'https://<b2ctenant>.b2clogin.com/<b2ctenant>.onmicrosoft.com/<customPolicy>/oauth2/v2.0/token';
return this.httpClient.post(url, urlencoded, httpOptions);
Any ideas what could be missing?
Thanks!
Though azureadb2c supports client_credential flow.One may not use them with SPA apps.
This scenario is not supported by MSAL.js. Client credential flow/ grant type will not work in SPAs(Angular) because browsers cannot securely keep client secrets.
As they may end up in the browser, visible to everyone and to attackers that load them.
Note:As the application's own credentials itself are being used, they must be kept safe - never publish that credential in your source
code
If you are using it for web app , please make sure to select the platform as web or change the reply url type to be web.
"replyUrlsWithType": [
{
"url": "https......",
"type": "Web"
},
]
Please refer :
Configure authentication in a sample Angular SPA by using Azure
Active Directory B2C | Microsoft Docs
OAuth 2.0 client credentials flow on the Microsoft identity platform- Microsoft Entra | Microsoft Docs
In basic terms, I am trying to get an access token to get Azure Cost Centre Data through the Usage Details API. The problem is that I can't seem to configure my service principal with azure properly. I have:
Created the registered app in Azure Active Directory
added https://www.thunderclient.io/oauth/callback in the redirect URL
generated a client secret
Included the following information in my Generate New Token in Thunder Client:
Request URL:
GET: https://management.azure.com/subscriptions/{subscription-id}/resourceGroupName/{resourceGroupName}/providers/Microsoft.CostManagement/dimensions?api-version=2019-11-01
Grant Type: Authorization Code
Auth Url: https://login.mmicrosoftonline.com/common/oauthorize
Token Url: https://login.microsoft.com/{tenant-id}/oauth2/v2.0/authorize from app registration
callback Url: https://thunderclient.io/oauth/callback
client ID: {{client_id}} from app registration
client secret: {{client_secret}} from app registration
scope: user_impersonation
{
Status 401 Unauthorized
"error": {
"code": "AuthenticationFailed",
"message": Authentication failed."
}
}
Header:
Bearer authorization_uri "https://login.windows.net/{tenant_id}, error= "invalid_token", error description="Could not find identity for access token"
Answering my own question. And big thanks to Guarav Mantri in the comments below.
The Scope should be set to https://management.azure.com/.default
The Grant Type is client credentials and not authorization code
The service principal needs to be added to the resource in azure that is part of the request (i.e. if looking for subscription data, then add the service principal as a reader role to the subscription).
Here's the configuration for the Azure AD B2C, create two applications: web and api. added two scopes read and write to the api scope. configure web application to web application. tested with the built-in user flows e.g. sign up sign in. run the flow for the web app, get the access token, scopes are in the token.
now create a custom policy to use multitenants to authenticate the users with Azure AD. created a custom signup/in policy. run the policy, got the access token by specifying the api scopes in the access token, however the return token does not contain the scope claims. my question is how to configure the custom policy to have the api scopes in the access token?
When you run the custom policy, it will only return an ID token rather than access token.
So your scope claims won't be included in the ID token.
You should refer to Request an access token in Azure Active Directory B2C.
After you have Added a web API application to your Azure Active Directory B2C tenant, use authorization code flow to get the access token.
GET https://<tenant-name>.b2clogin.com/tfp/<tenant-name>.onmicrosoft.com/<policy-name>/oauth2/v2.0/authorize?
client_id=<application-ID>
&nonce=anyRandomValue
&redirect_uri=https://jwt.ms
&scope=https://<tenant-name>.onmicrosoft.com/api/read
&response_type=code
The response with the authorization code should be similar to this example:
https://jwt.ms/?code=eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMC...
After successfully receiving the authorization code, you can use it to request an access token:
POST <tenant-name>.onmicrosoft.com/oauth2/v2.0/token?p=<policy-name> HTTP/1.1
Host: <tenant-name>.b2clogin.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=<application-ID>
&scope=https://<tenant-name>.onmicrosoft.com/api/read
&code=eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMC...
&redirect_uri=https://jwt.ms
&client_secret=2hMG2-_:y12n10vwH...
The response:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrN...",
"token_type": "Bearer",
"not_before": 1549647431,
"expires_in": 3600,
"expires_on": 1549651031,
"resource": "f2a76e08-93f2-4350-833c-965c02483b11",
"profile_info": "eyJ2ZXIiOiIxLjAiLCJ0aWQiOiJjNjRhNGY3ZC0zMDkxLTRjNzMtYTcyMi1hM2YwNjk0Z..."
}
See details here.
I have a background JS app that wont have any user input, so i was looking at using the app authentication without user input. I have set up an app in the Azure AD portal and also apps.dev.microsoft.com.
I am using the following endpoint:
login.microsoftonline.com/{Tenant}/oauth2/token
With the following body:
client_id: application_id
client_secret: generated key
grant_type: client_credentials
scope: "https://graph.microsoft.com/.default"
This generates an access_token however when i try and use it using the graph API, I get the following error
Access token validation failure
When investigating the token compared to a normal OAuth token with user input, i noticed its passing in roles rather then scp and the audience is my application rather then graph.microsoft.com.
What am i doing wrong?
The app registered in Azure AD portal works with Azure AD V1.0 endpoint ,app registered in apps.dev.microsoft.com that works with the v2.0 endpoint , Please firstly refer to this document for what's different about the v2.0 endpoint .
To use Azure AD v2.0 to access secure resources without user interaction(client credential flow) , you should send a POST request to the /token v2.0 endpoint:
POST /common/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=535fb089-9ff3-47b6-9bfb-4f1264799865&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret=qWgdYAmab0YSkuL1qKv5bPX&grant_type=client_credentials
You could refer to this document for how to use the OAuth 2.0 client credentials grant with Azure AD V2.0 endpoint .
If you want to use azure ad v1.0 (app registered in azure portal) with client credential flow, you could refer to this document . In azure ad v1.0 , you should indicate the resource parameter which the client app is requesting authorization for , in your scenario , the resource should be https://graph.microsoft.com/ if you want to acquire token to call microsoft graph api .
I have created a ASP.Net Web API (.Net Framework) app with "Work or School Accounts" as authentication type. This automatically registers this API app in my Azure subscription and I can see it under "App Registrations". I can see that Home Page Url is pointing to localhost address. I can see that API is launching locally on localhost address. I then launch Fiddler to get access token from Azure AD. My POST request to endpoint https://login.microsoftonline.com/<mytenant>.onmicrosoft.com/oauth2/token. has following 4 parameters
grant_type=client_credentials
&client_id=<appid from Azure AD Portal>
&client_secret=<secret from Azure AD Portal>
&resource=<appid from Azure AD Portal>
I get a token back. When I decode this token, I see aud and appid as expected(matching appid in Azure AD). I use this token as bearer token to invoke API call by adding Authorization: Bearer <mytoken> header in GET request to https://localhost:44374/api/values. However, this GET call to my API is returning me {"Message":"Authorization has been denied for this request."} error message.
What am I missing?
You should use App ID URI as the resource value when acquiring token , you could find the App ID URI in Properties of api app in azure portal ,like https://xxxxx.onmicrosoft.com/WebApplicationName . Web api will check whether the aud claim in access token matches the one you set in web.config :
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
ida:Audience value in web.config is the allowed audience .