Azure APIM to azure function mutual authentication - azure

I've created a HTTPTriggered azure function and I've setup the API management to call the function. Certificate validation policy is set in APIM and when APIM will forward the call to azure function the API-key of the function will be passed.
is this possible to put a client certificate validation also from APIM to function-App instead of the just API-key?

You need to add a property to the function app resource definition as described here for client certificate authentication to be enabled. You'll then need to do your own cert validation in the function code.
{
"properties": {
"clientCertEnabled": true
}
}
Inspect this header for the base64 encoded client cert:
X-ARR-ClientCert
One caveat here, it may not work with Consumption plans, i didn't get around to test it, please leave a comment if it does work.
Alternatively, you could grab a Bearer token from Azure AD with client credentials flow (client_id and client_secret) and call the function that way (enable EasyAuth for that to work). The bigger caveat here is also a question, can APIM do that flow for you? I have no clue.
Update:
Found something here, looks like it's doable with an outbound policy and makes a raw POST request into Azure AD, which is fine, since that's what that flow is really about.

Related

How can I configure Identity Server to correctly validate JWT tokens after an Azure App Slot Switch?

I have a core hosted API which uses IdentityServer and issues JwtBearer tokens to my desktop based client. This is generally working and I can log in and use the application as expected.
However, when using Azure Deployment Slots I run into problems due to the Issuer validation.
When swapping slots, azure doesn't swap the running code, but rather just swaps the pointing urls so that a warmed up running app is ready to serve requests immediately. However, the Identity Server implemenation seems to keep a reference to the OLD slot URL and use this as part of the Issuer Validation.
This means that once the slots are swapped, not only are all the clients effectively logged out (which is bad enough), but then when the client logs in again the token that the Identity Server supplies isn't even valid becuase it's got the wrong URI for the issuer.
This results in the error:
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:
IdentityServerJwtBearer was not authenticated. Failure message:
IDX10205: Issuer validation failed. Issuer:
'https://my-app-name.azurewebsites.net'. Did not match:
validationParameters.ValidIssuer:
'https://my-app-name-deployment.azurewebsites.net' or
validationParameters.ValidIssuers: 'null'.
I have tried to disable Issuer Validation by doing this in the setup:
services.AddAuthentication(options =>
{
})
.AddCookie()
.AddJwtBearer(options =>
{
options.TokenValidationParameters.ValidateIssuer = false;
})
.AddIdentityServerJwt();
However this doesn't seem to make any difference. Am I setting this variable in the wrong place? Are there other settings that need to be also configured as well as this to bypass this check?
I have also tried setting the list of ValidIssuers to include both 'https://my-app-name.azurewebsites.net' and 'https://my-app-name-deployment.azurewebsites.net' in the hopes that either one would be accepted and allow tokens to be validated by either slot, but again this seems to make no difference.
Alternatively, Is there a way to pervent IdentityServer caching the Issuer Url - or a way to flush that cache without restarting the application? Currently once the slots are swapped the only way I can get the desktop application to access the API is to restart the API application and then log in again aftwards (just logging out and logging in still results in a token that the server cannot validate, even though the server just issued it).
I feel like I must be missing something glaringly obvious, but I can't see what it is...
To configure the IdentityServer JWT Bearer you can use a configure call:
services.Configure<JwtBearerOptions>(IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.TokenValidationParameters.ValidateIssuer = false;
});
This way it the ValidateIssuer flag is set on the IdentityServerJwt rather than the original code which set up a new JwtBearer which was entirely seperate from the IdentityServer one.
Once the configuration is being set on the correct service it is also possible to use the array of ValidIssuers to include the Asure Slot Urls that should be accepted.

Using Quarkus OpenId Connect and Azure b2c

I'm building a backend-service that provides an API using Quarkus and I need to validate the incoming requests.
By default, quarkus uses keycload, but I want to validate with azure b2c.
At the moment I have the following configs:
quarkus.oidc.auth-server-url=https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/oauth2/v2.0/authorize?p={policy}
quarkus.oidc.application.application-type=service
quarkus.http.auth.permission.authenticated.paths=/hello/*
quarkus.http.auth.permission.authenticated.policy=authenticated
quarkus.log.category."io.quarkus.oidc".level= DEBUG
And as an example:
#Path("/hello")
public class GreetingResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
#RolesAllowed("test")
public String hello() {
return "Hello RESTEasy";
}
But Quarkus keeps throwing the same error:
OIDC server is not available at the 'https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/oauth2/v2.0/authorize?p={policy}'
Am I doing something wrong?
Thanks in advance!
Policy name for Azure AD B2C, format is like
quarkus.oidc.auth-server-url=https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/<policy-name>/oauth2/v2.0/authorize
(or)
quarkus.oidc.auth-server-url=https://<tenant-name>.b2clogin.com/<tenant-id> /<policy-name>/oauth2/v2.0/authorize
The code in your Azure AD B2C-enabled applications and APIs may refer
to login.microsoftonline.com in several places. For example, your code
might have references to user flows and token endpoints. Update the
following to instead reference your-tenant-name.b2clogin.com:
Authorization endpoint
Token endpoint
Token issuer
Please do check this MS docs for more information on the same.
2.
OIDC service application needs to know OpenId Connect provider’s token,
By default they are discovered by adding a /.well-known/openid-configuration path to the configured quarkus.oidc.auth-server-url.
Ex: https://{tenant-name}.b2clogin.com/{tenant-id}/.well-known/openid-configuration?p={policy-name}
By default, the iss claim value is compared to the issuer property which may have been discovered in the well-known provider configuration. But if quarkus.oidc.token.issuer property is set then the iss claim value is compared to it instead.
References:
quarkus/issues
security-openid-connect

What is the best way to call an authenticated HTTP Cloud Function from Node JS app deployed in GCP?

We have an authenticated HTTP cloud function (CF). The endpoint for this CF is public but because it is authenticated, it requires a valid identity token (id_token) to be added to the Authorization header.
We have another Node JS application that is deployed in the same Google Cloud. What we want is to call the CF from the Node application, for which we will be needing a valid id token.
The GCP documentation for authentication is too generic and does not have anything for such kind of scenario.
So what is the best way to achieve this?
Note
Like every google Kubernetes deployment, the node application has a service account attached to it which already has cloud function invoker access.
Follow Up
Before posting the question here I had already followed the same approach as #guillaume mentioned in his answer.
In my current code, I am hitting the metadata server from the Node JS application to get an id_token, and then I am sending the id_token in a header Authorization: 'Bearer [id_token]' to the CF HTTP request.
However, I am getting a 403 forbidden when I do that. I am not sure why??
I can verify the id_token fetched from the metadata server with the following endpoint.
https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=[id_token]
It's a valid one.
And it has the following fields.
Decoding the id_token in https://jwt.io/ shows the same field in the payload.
{
"issued_to": "XXX",
"audience": "[CLOUD_FUNTION_URL]",
"user_id": "XXX",
"expires_in": 3570,
"issuer": "https://accounts.google.com",
"issued_at": 1610010647
}
There is no service account email field!
You have what you need in the documentation but I agree, it's not clear. It's named function-to-function authentication.
In fact, because the metadata server is deployed on each computes element on Google Cloud, you can reuse this solution everywhere (or almost everywhere! You can't generate an id_token on Cloud Build, I wrote an article and a workaround on this)
This article provides also a great workaround for local testing (because you don't have metadata server on your computer!)

List Queues/Topics of Azure Service Bus using Rest API with SharedAccessKey

I am trying to list the Queues/Topics in an Azure Service Bus using the REST API.
When I try to connect I just get back a blank feed saying "This is the list of publicly-listed services currently available".
I am using the RootManageSharedAccessKey in the portal (for dev only, I can create a more restricted key later) so it should have all the access rights that I need, I just can't seem to get it to return anything. This documentation seems to suggest that this will work, but there's no actual working examples, just theoretical responses.
I have tried doing a GET request with the signature in the URL like this:
https://myservicebusnamespace.servicebus.windows.net/$Resources/Queues;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=MYSHAREDACCESSKEY
I have also tried doing it like this:
https://myservicebusnamespace.servicebus.windows.net/$Resources
and then setting the Authorization header to
WRAP access_token="MYSHAREDACCESSKEY="
Both times I just get this back
<feed xmlns="http://www.w3.org/2005/Atom">
<title type="text">Publicly Listed Services</title>
<subtitle type="text">This is the list of publicly-listed services currently available.</subtitle>
<id>uuid:6a5d438d-1793-451b-be41-XXXXXXXXXXXX;id=XXXXXX</id>
<updated>2020-06-28T13:03:04Z</updated>
<generator>Service Bus 1.1</generator>
</feed>
If I change the url slightly to be:
https://myservicebusnamespace.servicebus.windows.net/$Resources/Queues/
I get a slightly different response back of:
<Error>
<Code>401</Code>
<Detail>claim is empty. TrackingId:c40a2bd2-490d-4b5b-adde-33bc89aa84ff_G36, SystemTracker:myservicebusnamespace.servicebus.windows.net:$Resources/Queues, Timestamp:2020-06-28T13:27:40</Detail>
</Error>
Which seems to suggest that I am not authorised, or I am missing something. If I add an acutual queue name to the end of that url, it goes back to the original response.
I believe there is another way to get this information by using subscription ids and pem keys... using the management urls (https://management.core.windows.net/{subscription ID}/services/ServiceBus/Namespaces/{Namespace}/Topics/)
but this should all be possible using the format above, I just can't figure out the exact format required.
EDIT/UPDATE: If I don't include my auth claim, the result is exactly the same, suggesting that it's not seeing my auth claim or it's invalid. However if I include it, and just make it the token, without the WRAP bit at the start, I get an exception saying
<Error>
<Code>401</Code>
<Detail>MalformedToken: Invalid authorization header: The request is missing WRAP authorization credentials. TrackingId:7be2d7f0-c165-4658-8bf1-ea104c43defc_G28, SystemTracker:NoSystemTracker, Timestamp:2020-06-28T13:33:09</Detail>
</Error>
So it's like it's reading it then ignoring it?
If you want to list queues or topics we can use Azure service bus service rest api or Azure Resource Manager Rest API. For more details, please refer to the following steps
Azure service bus service rest api
Generate SAS token. For more details, please refer to the document
For example, I use python to create sas token
import hmac
import time
import hashlib
import base64
import urllib
sb_name='bowmantest'
// your entity path such as $Resources/topics (list topics) $Resources/queues(list queues)
topic='$Resources/topics'
url=urllib.parse.quote_plus("https://{}.servicebus.windows.net/{}".format(sb_name,topic))
sas_value='' // your share access key
sas_name='RootManageSharedAccessKey' // your share access rule name
expiry = str(int(time.time() + 10000))
to_sign =(url + '\n' + expiry).encode('utf-8')
sas = sas_value.encode('utf-8')
signed_hmac_sha256 = hmac.HMAC(sas, to_sign, hashlib.sha256)
signature = urllib.parse.quote(base64.b64encode(signed_hmac_sha256.digest()))
auth_format = 'SharedAccessSignature sig={0}&se={1}&skn={2}&sr={3}'
auth=auth_format.format(signature,expiry,sas_name,url)
print(auth)
Call the rest API
1). list Queues
GET https://<namespace name>.servicebus.windows.net/$Resources/queues
Authorization <sas token>
2). List topics
GET https://<namespace name>.servicebus.windows.net/$Resources/topics
Authorization <sas token>
Azure Resource Manager Rest API
create a service principal and assign Azure RABC role to the sp(I use Azure CLI)
az login
#it will create a service principal and assign contributor role to the sp
az ad sp create-for-rbac -n "jonsp2"
Get Azure AD 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=<app id>
&scope=https://management.azure.com/.default
&client_secret=<app password>
&grant_type=client_credentials
call the rest API
List Queues
GET https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ServiceBus/namespaces/{namespaceName}/queues?api-version=2017-04-01
Authorization Bearer <AD token>

Authenticated REST API call to Azure Service Bus using Managed Identity

Azure Service Bus supports managed identity access, however the only method I've found to for example send a message to a queue is using this approach that requires code and the Service Bus SDK:
var tokenProvider = TokenProvider.CreateManagedServiceIdentityTokenProvider();
QueueClient sendClient = new QueueClient($"sb://{Config.Namespace}.servicebus.windows.net/", Config.Queue, tokenProvider);
await sendClient.SendAsync(new Message(Encoding.UTF8.GetBytes(messageInfo.MessageToSend)));
await sendClient.CloseAsync();
Sources:
https://github.com/Azure-Samples/app-service-msi-servicebus-dotnet
https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-managed-service-identity
I'm looking for a way to do the same thing with a REST API call from within an Azure API Management policy. I've granted APIM, role based access to Service Bus and I'm able to get a token back, but I get this error back from Service Bus when attempting the REST API call with the managed identity token passed in the Authorization header:
MalformedToken: The credentials contained in the WRAP header are not well-formed.
It looks like Service Bus might only support WRAP or SAS tokens at this point with their REST API: https://learn.microsoft.com/en-us/rest/api/servicebus/send-message-batch
But then again how is this working behind the scenes?
TokenProvider.CreateManagedServiceIdentityTokenProvider()
Seems like it should be possible with the REST API.
It seems in the SDK they don't specify it as Authorization: Bearer tokenabcdef..... but as Authorization: tokenabcdef. Which is a bit unusual.

Resources