Call azure function secured by AAD using client id and client secret - authentication

I have an Azure Function secured by the Azure Active Directory.
I can successfully call the application from the browser provided that I'm logged.
Now, what I want to do is to call that function from an application that doesn't use the username and password in order to authenticate but rather a trusted application client id and client secret.
I tried to use for that purpose the same application (say ApplicationA) that has been created for securing the function application.
I went to the App Registrations, found the application created by configuring the Azure Active Directory authentication provider in the Function App Authentication/Authorization settings (ApplicationA) and I set a Client Secret for it.
Now I'm able to obtain a token using said application Client Id and the generated secret.
But when I'm trying to call the Function using that token I'm getting 401 error.
I tried the same operation with a newly created application in App Registration (say ApplicationB) but with the same result, I'm still getting 401.
I even tried to add a Reader Role for that application in Subscriptions but then again - 401 when calling Function with the token.
I know, there's something like API permissions in the application settings.
So I tried to add the permission for ApplicationB to ApplicationA user_impersonation but it also doesn't work so that's not the way.
So how do I grant the permission for a registered Application for which I'm able to acquire a token so I can use this token to call the Function?
EDIT:
I'm using the following code for token acquisition:
using Microsoft.Identity.Client;
string ClientId = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
string ClientSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
string TenantId = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
string Authority = $"https://login.microsoftonline.com/{TenantId}";
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithClientSecret(ClientSecret)
.WithAuthority(new Uri(Authority))
.Build();
AuthenticationResult result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
string token = result.AccessToken;

You need to register at least one application permission (appRole with allowed member type Application) via the manifest into the functions app registration.
Then you can assign the application permission to the client app.
You can see my article for more info on defining permissions: https://joonasw.net/view/defining-permissions-and-roles-in-aad
Example app permission in manifest:
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Allow the application to read all things as itself.",
"displayName": "Read all things",
"id": "32028ccd-3212-4f39-3212-beabd6787d81",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "Things.Read.All"
}
],
You need to define the id yourself, it just needs to be a GUID.
The value is what is sent in the tokens in the roles claim.
Description and display name are just for how the permissions shows up in the UI.

Related

Roles Claims are missing in Azure AD Access Token using App Authentication client library in .NET

I am trying to generate Azure AD access token (Application Only Token using Managed Identity Credentials) at backend that can be later used to invoke other APIs.
I created App registration in Azure AD and exposed as an API.
I set application ID URI api://CLIENT_ID.
I created App roles and manifest looks like below.
"accessTokenAcceptedVersion": 2,
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Search.Address",
"displayName": "SearchAddress",
"id": "82bc288b-b03b-4acb-a99b-f246d8ad1551",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "Search.Address"
}
],
I configured API permissions and granted admin consent for Application Permissions.
I created Azure Function App enabled System Managed Identity and tried below code to generate access token in HTTP Trigger Function.
Code:
using Azure.Identity;
using Azure.Core;
var credential = new ManagedIdentityCredential();
var accessTokenRequest = await credential.GetTokenAsync(new TokenRequestContext(scopes: new string[] { "api://bee96ec1-1acc-456c-847b-84f88870b1d9" + "/.default" }) { });
var accessToken = accessTokenRequest.Token;
I am successfully getting the Token but Roles are missing. Am i missing something in configuration?
Why I cannot see scopes/Roles in Token?
If I am generating access token using client credentials approach through Postman, I can see Roles. But I would like to avoid this approach as I no longer needs to store keys If I use Managed Identity approach.
It worked in my side so I can only try to share the differences I found and how I do in my side.
I noticed that the aud claim in my token is different from yours.
And we are using DefaultAzureCredential, so I followed this section and set environment variable for my app.

How to authenticate Python client app for access to restricted Google Cloud function?

I've created a Google Cloud function and I would like to access it from a Python application I'm developing. I am able to access the function when there is no authentication required, but can't access the functions when I enable authentication.
Here is the service account key I'm using with stripped out info. The only role it is configured for is invoking cloud functions.
{
"type": "service_account",
"project_id": "XYZ",
"private_key_id": "XYZ",
"private_key": "XYZ",
"client_email": "XYZ",
"client_id": "XYZ",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "XYZ"
}
It seems that authenticated requests require a token that's included in the requests Authorization header, but I don't understand where to acquire this token.
I've tried using the approach outlined here with environment variables and the default auth method, but this doesn't work. I assume this is because the service account key is different from an OAuth token. (I have created a new service account the Cloud Functions invoker permission and am using that service account key). I receive the following error:
google.auth.exceptions.RefreshError: ('invalid_scope: Invalid OAuth scope or ID token audience provided.', '{"error":"invalid_scope","error_description":"Invalid OAuth scope or ID token audience provided."}')
How do I generate this token to authenticate the request from my Python script? Or is the approach with the service account recommended, but something else is going wrong?
I was able to get this working, though at time of writing there is a bug in the documentation that threw me off.
Access protected Cloud Functions is possible by using the IDTokenCredentials class within the google-auth library:
credentials = service_account.IDTokenCredentials.from_service_account_file(
SERVICE_ACCOUNT_JSON_FILE,
target_audience="https://function/url/here",
)
authed_session = AuthorizedSession(credentials)
response = authed_session.post(url)
I have my service account configured with the "Cloud Functions Invoker" role.
Your problem is almost certainly in the roles you gave that service account. Service accounts are finicky and the roles / permissions do not always act the way you think they will in my experience. Start by making a Service Account that has full permissions (project owner). Use that service account in your script then begin limiting the permissions from there. It sounds like you will need at a minimum cloud function "admin". If that works try another level down. Cloud function "developer" etc.
If you are using, for instance, App Engine or even other Cloud Functions to connect to your Cloud Function, you can use this: Function-to-function, the steps, basically are:
Grant the Cloud Functions Invoker.
In the calling function, you'll need to:
Create a Google-signed OAuth ID token with the audience (aud) set to the URL of the receiving function
Include the ID token in an Authorization: Bearer ID_TOKEN header in the request to the function.
import requests
//# TODO<developer>: set these values
REGION = 'us-central1'
PROJECT_ID = 'my-project'
RECEIVING_FUNCTION = 'my-function'
//# Constants for setting up metadata server request
//# See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
function_url = f'https://{REGION}-{PROJECT_ID}.cloudfunctions.net/{RECEIVING_FUNCTION}'
metadata_server_url = \
'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience='
token_full_url = metadata_server_url + function_url
token_headers = {'Metadata-Flavor': 'Google'}
def calling_function(request):
//# Fetch the token
token_response = requests.get(token_full_url, headers=token_headers)
jwt = token_response.text
//# Provide the token in the request to the receiving function
function_headers = {'Authorization': f'bearer {jwt}'}
function_response = requests.get(function_url, headers=function_headers)
return function_response.text
I have tested this solution and works as expected.
If you're invoking a function from a compute instance that doesn't have access to compute metadata (e.g. your own server), you'll have to manually generate the proper token:
Self-sign a service account JWT with the target_audience claim set to the URL of the receiving function.
Exchange the self-signed JWT for a Google-signed ID token, which should have the aud claim set to the above URL.
Include the ID token in an Authorization: Bearer ID_TOKEN header in the request to the function.
The Cloud IAP docs have sample code to demonstrate this functionality. The part you could be interested in should be this Authenticating from a service account

Token based authentication for both Web App and Web API using Azure AD B2C

Scenario:
Both Web application and Web API need to be authenticated and protected from the server side.
Requirement:
Web application is serving the contents for the browser and browser should be calling Web API directly (i.e. Browser to API).
Question:
Is it possible to authenticate both Web APP and the API using tokens?
Any sample code or clear direction would be highly appreciated.
Normally web applications are authenticated using cookies and APIs are authenticated using tokens.There are some sample projects available here but they are either browser to API (SPA token based) or Server side Web App calling API from server to server.
UPDATE 1
App is saving the TokenValidationParameters and used bootstrapContext.Token within the app controller to grab for server to server communication.
As per #dstrockis, I'm trying to grab the id_token from the Web App soon after the end of validation (not within the app contrller).
I'm using SecurityTokenValidated invoker in OpenIdConnectAuthenticationOptions.Notifications within the Startup class. SecurityTokenValidated receives a parameter of type SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> but I'm not sure where to find the id_token within it. Method is below.
private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
{
return new OpenIdConnectAuthenticationOptions
{
// For each policy, give OWIN the policy-specific metadata address, and
// set the authentication type to the id of the policy
MetadataAddress = String.Format(aadInstance, tenant, policy),
AuthenticationType = policy,
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = clientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
//NEW METHOD INVOKE ************************************
//******************************************************
SecurityTokenValidated = OnSecurityTokenValidated
//******************************************************
},
Scope = "openid",
ResponseType = "id_token",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
SaveSigninToken = true
},
};
}
//NEW METHOD ************************************
private Task OnSecurityTokenValidated(
SecurityTokenValidatedNotification<OpenIdConnectMessage,
OpenIdConnectAuthenticationOptions> arg)
{
//QUESTION ********************************************************
//How to find the just saved id_token using incoming parameter, arg
//*****************************************************************
return Task.FromResult(0);
}
UPDATE 2
Instead of SecurityTokenValidated, I tried AuthorizationCodeReceived and it's not getting called at all. As discussed here, my redirect url does have an ending slash as well.
Any Ideas?
Our ASP.NET OpenID Connect middleware which supports AAD B2C is built to rely on cookie authentication from a browser. It doesn't accept tokens in a header or anything like that for securing web pages. So I'd say if you want to serve HTML from your web app in the classic way, you need to use cookies to authenticate requests to the web app.
You can definitely get & store tokens within the browser and use those to access your web API, even if you use cookies to authenticate to the web app. There's two patterns I'd recommend:
Perform the initial login using the OpenID Connect Middleware, initiating the flow from the server side as described in the samples. Once the flow completes, the middleware will validate the resulting id_token and drop cookies in the browser for future requests. You can instruct the middleware to save the id_token for later use by using the line of code written here. You can then somehow pass that id_token down to your browser, cache it, and use it to make requests to the API.
The other pattern is the inverse. Start by initiating the login from javascript, using the single page app pattern from the B2C documentation. Cache the resulting id_tokens in the browser, and use them to make API calls. But when the login completes, you can send a request to your web app with the id_token in the body, triggering the OpenID Connect middleware to process the request and issue a session cookie. If you want to know the format of that request, I'd recommend inspecting a regular server side OpenID Connect flow.
Found the answer to my own question and adding here for the future reference.
After a successful validation, id_token can be accessed by invoking the SecurityTokenValidated notification. Code sample is below.
private Task OnSecurityTokenValidated(
SecurityTokenValidatedNotification<OpenIdConnectMessage,
OpenIdConnectAuthenticationOptions> arg)
{
//Id Token can be retrieved as below.
//**************************************
var token = arg.ProtocolMessage.IdToken;
return Task.FromResult(0);
}
However, saving this directly into a browser cookie may not be secure.

How to refresh the access token of an Application with the WSO2 API Manager 1.10.0?

I've (finally) managed to get a valid access token for an application using the API with the WSO2 API Manager 1.10.0. However, it is unclear to me how to refresh this token.
I got the working access token by calling https://myapi.mydomain.com/api/am/store/v0.9/applications/generate-keys?applicationId=2b205ee5-5891-4913-96e0-b4952d7d2a4c with the following payload
var payload = {
'grant_type': 'password',
'username': username,
'password': password,
"validityTime": validityTime,
"keyType": "PRODUCTION",
"accessAllowDomains": [ domain ]
}
The result looks like this:
Generating application keys for application with id '2b205ee5-5891-4913-96e0-b4952d7d2a4c'
{ consumerKey: '5k7UDuFTV0UE7mESHerEIm2Nj3ga',
consumerSecret: 'm1hS_SNfHF25l9lP9YjYpf977VUa',
keyState: 'APPROVED',
keyType: 'PRODUCTION',
supportedGrantTypes:
[ 'urn:ietf:params:oauth:grant-type:saml2-bearer',
'iwa:ntlm',
'refresh_token',
'client_credentials',
'password' ],
token:
{ tokenScopes: [ 'am_application_scope', 'default' ],
validityTime: 2678400,
accessToken: 'be8661550ee51b7682902e58a58108f6' } }
So it does seem like I have the grant to refresh my access token. Howver the documentation only mentions how to do this when you have a refresh token, which is missing from the reply. What am I doing wrong?
The API you used to generate tokens is not the API for your job. The API 'applications/generate-keys' is there to generate application related keys (Client secret and client id) . The token generated for
this has a client_credential grant type. thats why you do not get a refresh token. That token is generated using the client id and secret.
I see you have sent the grant type as 'password' in the payload but those parameters are not valid to start with. that api does not accept grant type name. (api is there to generate the application keys. so grant type is not needed)
Actual sample payload (see https://docs.wso2.com/display/AM1100/apidocs/store/#!/operations#ApplicationindividualApi#applicationsGenerateKeysPost)
{
"validityTime": "3600",
"keyType": "PRODUCTION",
"accessAllowDomains": ["ALL"
]
}
to generate the tokens please use the token api (https://docs.wso2.com/display/AM1100/Token+API). Those apis are implemented according to the OAuth2 specs. You will get a refresh token when you use 'password'
grant type using these token apis
You can refresh the token in API Store (developer portal), if you click on the Subscriptions tab, and there either:
Manually click the Re-generate button refresh right now, or
Click the cURL button to generate the command that you can use to refresh programmatically:
Notes:
You can also read more information about Token API in documentation,
For the screenshot, I used hosted version of API Manager - WSO2 API Cloud.

Get Access token with Azure AD multi-tenant openID authentication

I followed the sample code here to create a MVC web app with Azure AD multi-tenant OpenID authentication. I use the following code to get user sign in.
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
Now I need to send a web api call which is protected by my Azure AD as well. Currently, before I send the request I use ADAL library to ask users to login again and get the access token like this.
AuthenticationContext ac = new AuthenticationContext(authority);
AuthenticationResult ar = ac.AcquireToken(resourceID, clientID, redirectURI, PromptBehavior.Always);
string accessToken = ar.AccessToken;
However, since the authentication used in the MVC(if the user is from my AD) is the same as the one used to protect the web api. I'm wondering if there is a way to get the access token when user login with this openID authentication so that I can skip the second login with ADAL?
UPDATE:
Following vibronet's answer, I am trying to use the following code to get the token:
string authority = "https://login.windows.net/ucdavisprojecthotmail.onmicrosoft.com";
ClientCredential clientcred = new ClientCredential(clientId, appKey);
AuthenticationContext authContext = new AuthenticationContext(authority);
AuthenticationResult result = authContext.AcquireTokenSilent(resourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
Here, this code is used in an MVC web app and the clienId and appKey is the clientID and key of the web API I want to call. The resoureID is the APP ID URI of the web API obtained in Azure portal.
However, I got this error: Failed to acquire token silently. Call method AcquireToken. Anything I was missing?
Absolutely. Check out https://github.com/AzureADSamples/WebApp-WebAPI-MultiTenant-OpenIdConnect-DotNet, it's like the sample you've been working with but with in addition the access token acquisition and use you are asking about. Also note, AcquireTokenSilent can only work if you have a token in the cache - to be used directly or refreshed. FInally: when you ask for a token, you must specify both the ID fo the resource you want a token for, and the clientID of the application doing the request. In your code, you appear to have used the clientID of the target app. Please refer to the sample I linked above, it shows the exact pattern to be used in this scenario.