Securing API with client credentials via Azure AD - azure

I have an ASP.NET Core API hosted in Azure which is to be accessed by several trusted clients. I wish to offer an /auth endpoint which accepts a client_id and client_secret. Response will be an OAuth access token with expiry.
The many examples/tutorials I have found mostly relate to username/password login and full OAuth flow (B2C) which isn't what I'm looking for as the trusted clients have the secret.
I've been looking at Azure API Management which links through to Azure AD for OAuth but I'm thinking this is just complicating things right now.
In the past I have generated and validated JWT bearer tokens using the ASP.NET middleware, but I am sure I should be generating and validating tokens via Azure AD - or am I wrong here?
[expecting to get some down votes for not being explicitly code related, but really need a little bit of advice to get me past this]

I wish to offer an /auth endpoint which accepts a client_id and
client_secret. Response will be an OAuth access token with expiry.
You can use client credential flow. Then you can use this endpoint to get access token.
POST /{tenant}/oauth2/v2.0/token HTTP/1.1 //Line breaks for clarity
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
Before doing this, you need to expose application permissions of your application api. The contents of appRoles are as below.
"appRoles": [
{
"allowedMemberTypes": [ "Application" ],
"description": "Accesses the TodoListService-Cert as an application.",
"displayName": "access_as_application",
"id": "ccf784a6-fd0c-45f2-9c08-2f9d162a0628",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "access_as_application"
}
],

Related

Bypass Azure AD SAML

I got SAML setup on my AD and I have the private and certificate PEMs along with the certificate and metadata and I want to know if it's possible to bypass the login from API? I'm developing a Node.JS API which I want to make requests to the server behind the SAML login but I need to bypass it with what I got.
Maybe a way to generate a token from Azure's API (to use as bearer authorization) would work like how you do it with OAuth?
I'm not asking for any other solution (like whitelisting etc.), I just need it to be token / API based
To bypass azure ad , you may chose for the On behalf flow.
For single-page apps (SPAs), here we pass an access token to a middle-tier confidential client to perform OBO flows instead.
This will only work, if the respective permissions (scope grants) are
already granted when you try to get an access token using the
on-behalf of flow.
Here API A authenticates to the Microsoft identity platform token
issuance endpoint and requests a token to access API B.
https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token
with grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
client_id=xxx
&client_secret=saxx1s
&scope=https://graph.microsoft.com/user.read+offline_access
&requested_token_use=on_behalf_of
Here we need to expose api and here I am giving user.read
And exposed api
The response has access token , refresh token
{
"token_type": "Bearer",
"scope": "https://graph.microsoft.com/user.read",
"expires_in": 3269,
"ext_expires_in": 0,
"access_token": "xx",
"refresh_token": "xxxx"
}
Please check Microsoft identity platform and OAuth2.0 On-Behalf-Of flow - Microsoft Entra | Microsoft Learn
Reference : how-to-use-azure-ad-access-token-to-bypass-microsoft-online-login

Scope is not being added to Access Token returned from Azure Ad

Overview
We have an Azure AD secured API living in Azure as a web app. We need to be able to:
Trigger this API via user interaction from a client application (this part works).
Trigger this API programmatically from a scheduled job that will simply get a token & hit this API (this part does not work due to authentication issues).
Problem
The issue is that when we request a token from Azure AD, scope is not being set in our token claims resulting in the API rejecting the token.
This is the request we are making:
This request returns an access token with the following claims:
{
"aud": "<our api client id>",
"iss": "https://login.microsoftonline.com/<tenantId>/v2.0",
"iat": 1644421512,
"nbf": 1644421512,
"exp": 1644425412,
"aio": "<value>",
"azp": "<scheduled job client id>",
"azpacr": "1",
"oid": "<guid>",
"rh": "<value>",
"sub": "<guid>",
"tid": "<guid>",
"uti": "<value>",
"ver": "2.0"
}
As you can see scp (scope) is not included in the token claims even though we include it in the request.
If we use this token to make a request to our API we get the following error:
System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token.
Any help on how we can get an access token from Azure AD with the proper scope/permissions to call our API, would be greatly appreciated.
Note
The Azure AD App Registration for our scheduled job that will request a token and then hit our API, does have the Delegated API Permission access_as_user which you can see I am including in the token request's scope.
The above is expected as using client credentials you can't get the delegated permissions i.e. the access_as_user permission and also the scope in client credentials should be used as api://<APP_ID>/.default . So , If you want delegated permissions then you will have to use implicit grant flow instead of client credentials.
For testing , I created two app registrations , One on which API is exposed (Postman) and other which is to be used for authentication (Powershelltest) and then , I have tested the same in 2 different scenarios like one for client credentials and another for implicit grant :
Main APP whose API has been exposed :
App used for authentication:
Scenario 1 Using Client Credential flow :
Scenario 2 using Implicit Grant flow :

I can't get email or profile scopes for an Azure B2C application, nor can I call the OIDC UserInfo endpoint

I'm finding Azure B2C really confusing. Currently I am using oauth2_proxy behind an nginx ingress controller in a test (single node) AKS kubernetes environment. I have made a slight change to oauth2_proxy to redirect upon error to chain Workflows together (like password recovery link etc.) and everything is working so far. I get an authorisation cookie from oath2_proxy and the ingress controller lets me through.
My next step is to work out what the username is, and maybe gather some more informaton. I think I can get some of the information in the id_token, but I am failing to see "email" and "preferred_username". Apparently these are only available if my token has the "email" and "profile" scopes, but I cannot work out how to get those scopes on my B2C App.
Currently I have a test application. In it's API permissions area I have a single "read" permission so that I have a resource to request to get an access_token. I also have "email", "offline_access", "openid", "profile" and "User.Read" permissions from "Microsoft Graph" - all permissions are granted by the admin on the API permissions screen.
The process I have been following up to now for testing is to run a signin workflow, selecting my API in the resources dropdown. I then copy the "Code" into a token request in postman, and then try both the "https://graph.microsoft.com/v1.0/me" and the "https://graph.microsoft.com/oidc/userinfo" endpoints with both the "id_token" and the "access_token". Both of these respond with "InvalidAuthenticationToken", message "Access token validation failure".
I had since noticed that my resources dropdown still only contains "read" and "openid" access rights, so I have been copying the "run userflow" url and inserting the scopes for "email", "offline_access", "openid" and "profile". I have also ensured that my call to the "token" endpoint contains these scopes. The response from the token endpoint only returns scopes "read offline_access openid", so "profile" and "email" are missing.
I don't know how to configure my app registration in B2C so that the "email" and "profile" scopes become available. Since "openid" appears in the resource drop down, I would expect "email" and "profile to turn up in there, but they don't. My token calls definitely show that the scopes that I need are not returned.
My workflow url looks as follows:
https://********.b2clogin.com/********.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1_signup_signin&client_id=********&nonce=defaultNonce&redirect_uri=https%3A%2F%2Fjwt.ms&scope=openid%20user.read%20email%20profile%20offline_access%20https%3A%2F%2F********.onmicrosoft.com%2Ftestapp%2Fread&response_type=code&prompt=login
My token call looks as follows (using the code value from the workflow):
https://********.b2clogin.com/********/b2c_1_signup_signin/oauth2/v2.0/token?grant_type=authorization_code&client_id=********&response_type=code&redirect_uri=https://jwt.ms&response_mode=query&scope=https://********.onmicrosoft.com/apptest/read openid offline_access email profile&code=******CODE FROM WORKFLOW CALLBACK******
Token response is like:
{
"access_token": "******BIG LONG BASE64******",
"id_token": "******BIG LONG BASE64******",
"token_type": "Bearer",
"not_before": 1591607270,
"expires_in": 3600,
"expires_on": 1591610870,
"resource": "********",
"id_token_expires_in": 3600,
"profile_info": "******BASE64******",
"scope": "https://********.onmicrosoft.com/testapp/read offline_access openid",
"refresh_token": "******BASE64******",
"refresh_token_expires_in": 1209600
}
OIDC endpoint call like this:
https://graph.microsoft.com/oidc/userinfo
HAS HEADERS
Content-Type: application/json
Authorization: Bearer ********BASE64 from access_token********
Any help would be appreciated. i.e. how do I return additional scopes + how do I call the OIDC UserInfo endpoint.
Thanks.
I struggled with a similar issue a while back, where I wanted my API to use the access token to look up user info. Azure AD does not work in a standards compliant way:
Calling User Info requires a separate graph token
You have to use a token exchange request to get the graph token
Here are some resources of mine which I think are related and will hopefully give you enough info to resolve your problem. I think right now you are getting the error from Step 14 of the blog post.
Token Exchange + User Info Lookup C# Code
Blog Post on Azure AD Settings related to Graph / User Info

How to Access Sharepoint Online API with v1 Azure AD Application and Client Credentials

I am unable to make an API call to Sharepoint Online using Postman. I have successfully made API calls to the Graph API so I am familiar with how I think this should work.
I have followed these instructions for setup:
for creating a certificate and registering a v1 azure app:
https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread
for using the client credentials flow: https://learn.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow
for creating a client assertion: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials
The first article says that it won't accept access tokens generated using a client secret, but I have generated tokens with a secret and a certificate and have found no difference.
When calling anything, such as:
https://<tenant>.sharepoint.com/_api/web
I get the error:
{"error_description":"Exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' was thrown."}
here's a sample of the access token I generate with the v1 /token endpoint:
{
"aud": "https://microsoft.sharepoint-df.com/",
"iss": "https://sts.windows.net/462c0b***********c3708/",
"iat": 1569243291,
"nbf": 1569243291,
"exp": 1569247191,
"aio": "42FgYDiXt***********==",
"app_displayname": "T***********n",
"appid": "00c***********2b",
"appidacr": "2",
"idp": "https://sts.windows.net/46***********708/",
"oid": "2f8a5***********684",
"roles": [
"User.ReadWrite.All",
"TermStore.Read.All",
"Sites.FullControl.All"
],
"sid": "5ab8d57***********0bc",
"sub": "2f8a5***********684",
"tid": "462c0***********708",
"uti": "aHt8d***********9AA",
"ver": "1.0"
}
The error message seems to imply that my resource parameter in the /token endpoint call was set incorrectly. I believe this is omitted in the Microsoft documentation, since the documentation is so divided. The correct token endpoint call for a V1 app to call SharePoint Online looks like the following:
Web Service
POST https://login.microsoftonline.com/<TARGET-TENANT-ID OR NAME>/oauth2/token
Parameters
client_id= <Application ID from Azure Portal>
grant_type=client_credentials
resource= https://<TARGET-TENANT-NAME>.sharepoint.com
client_assertion_type= urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion= <See Link Above to create assertion>
The links above omit how to calculate the x5t value for the certificate JWT. You can use this:
echo $(openssl x509 -in certificate.pem -fingerprint -noout) | sed 's/SHA1 Fingerprint=//g' | sed 's/://g' | xxd -r -ps | base64
which I got from here: How to obtain value of "x5t" using Certificate credentials for application authentication
If you try to use client secret instead of client assertion, you'll get a token back, but the SharePoint Online REST API will return:
Unsupported app only token.
As you see in the first article, access tokens generated using a client secret is not-supported App only token authentication for SharePoint.
If you decode the access token, you will find that for "appidacr", if client ID and client secret are used, the value is "1". If a client certificate was used for authentication, the value is "2". See details here.
Client certificate is more more secure than client secret. It provides dual verification and protection.
You can refer to this 3rd-party article to get Azure AD app-only access token using certificate and use this access token to access your SharePoint resources.
Of course, the article you mentioned in the comment is also helpful. It uses ACS to finish authentication.

Azure access token generation from Postman

I wanted to generate Azure token from Postman for API authorization in my project. I am able to generate token using below API request but getting the below error message "Authorization denied for this request" while using the generated token in another API request.
Endpoint#
https://login.microsoftonline.com/:tenant_id/oauth2/token
Params#
tenant_id:As per id generation by azure.
Body# (Form-data)
grant_type:client_credentials
client_id:As per id generation by azure.
client_secret:As per id generation by azure.
resource:Required URL
Response#
"token_type": "Bearer",
"expires_in": "foo",
"ext_expires_in": "foo",
"expires_on": "foo",
"not_before": "foo",
"resource": "foo",
"access_token":foo
Since the above returned token is not accepted, I had passed username and password as well in body of the request but ended up with same results. Also azure did not consider my credentials even they are wrong.
Could you please assist what else I need to send in the response to get valid token id?
The Valid format for client_credentials authentication flow is like below:
Azure Portal Credentials For App Id and Tenant Id:
Application Secret from Portal:
Token Endpoint Or URL:
https://login.microsoftonline.com/YourTenantName.onmicrosoft.com/oauth2/token
Request Param:
grant_type:client_credentials
client_id:b603c7be_Your_App_ID_e6921e61f925
client_secret:Vxf1Sl_Your_App_Secret_2XDSeZ8wL/Yp8ns4sc=
resource:https://graph.microsoft.com
PostMan Sample:
Token On Response:
Expose Your Own API:
When You want to authorize your own API you have add it here. So that your token will contain this permission and this API can be accessed. Refer this docs
For more clarity you could refer official docs
You should try adding "X-ZUMO-AUTH" header to your request when using the generated token.
GET https://<appname>.azurewebsites.net/api/products/1
X-ZUMO-AUTH: <authenticationToken_value>
https://learn.microsoft.com/en-us/azure/app-service/app-service-authentication-how-to

Resources