AzureADB2C Client Credentials Grant with Client Assertion as Opposed to Static Secret - azure-ad-b2c

I have a requirement to provide API to our consumers. The intention is to secure the API using AzureAD B2C - Client Credential Grant flow.
I have created a custom policy on B2C tenant that provides the access token. Things work fine with the clientId and Secret authentication method.
I now want to secure the OAuth2 conversation further by allowing the client to use the signed client_assertion as opposed to static client secret using their protected key. I have uploaded the public portion of the key into the relevant app registration.
Unfortunately, consuming the /token endpoint with the signed client_assertion results in an error.
REQUEST
https://tenant.b2clogin.com/tenant.onmicrosoft.com/b2c_1a_demo_clientcredentialsflow/oauth2/v2.0/token
grant_type=client_credentials&scope=https%3A%2F%2Fapi%2F.default&client_id=d5339984-e6c7-457a-9ef9-21fb6e3e6c59&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGciOiJo
RESPONSE
HTTP/1.1 400 Bad Request
{"error":"invalid_request","error_description":"AADB2C99027: Policy 'B2C_1A_Demo_ClientCredentialsFlow' does not contain a AuthorizationTechnicalProfile with a corresponding ClientAssertionType.\r\nCorrelation ID: 5eb76fa5-c919-4877-a722-0d38408e18c6\r\nTimestamp: 2023-01-19 07:38:25Z\r\n"}
Can someone please tell me if B2C is intended to support client assertions? Metadata JSON on the policy returns only the following two authentication methods:
"token_endpoint_auth_methods_supported": [ "client_secret_post", "client_secret_basic" ]
Is it possible to include private_key_jwt as a supported authentication method using custom policy configuration? Is it possible to configure the AuthorizationTechnicalProfile for the policy with a corresponding ClientAssertionType?
I hope that I have explained the problem well enough.
I have tried various strategies, incluling the use of AAD token endpoint, login.microsoftonline.com with the B2C tenant Id. Using that endpoint, the custom policy on B2C is completely ignored, therefore generating a vanilla token with none of my curated claims.

TLDR: As of June 2022, Azure AD B2C does not support client assertions.
This issue on Github asks for documentation for error number AADB2C99027. In the course of the discussion, a member of the team states
Unfortunately, we decommissioned client_assertion flow because it didn't follow OIDC spec – So we shouldn't be documenting the error.
From that, I take that there are no plans to support client_assertion flow.

Related

azure-ad-b2c - How to prevent reuse of Oauth Authorization Code?

I'm using the OAuth 2.0 authorization code flow , but I noticed that I'm able to reuse the same authorization code and the server responds with a new id token.
And as per OAuth 2.0 specifications, The client MUST NOT reuse the same authorization code (https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2)
Am I missing any settings in my policy? The only documentation that I found about code authorization code flow in AzureB2C is the following: https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-tokens?pivots=b2c-custom-policy#authorization-code-lifetime
Edited:
I generated the auth-code by using below endpoint:
https://tenant.b2clogin.com/tenant/policy/oauth2/v2.0/authorize?client_id=clientid&response_type=code&redirect_uri=https://jwt.ms&scope=openid&response_mode=query&code_challenge=codechallenge&code_challenge_method=S256
I generated the access token by using parameters like below:
But still able to generate tokens using same code
Here's my new app:
If you are not using a nonce claim, this expected behavior due to a known limitation with B2C.
According to the RFC (6749) - The OAuth 2.0 Authorization Framework
The authentication code should be used one time.
With standard Azure AD, a fix was released in 2018:
Azure Active Directory breaking changes reference | Microsoft Docs
This is because B2C is stateless and does not follow the OTP RFC standard here. Standard Azure AD made engineering provisions to track and invalidate AuthZ codes back in 2018, but Azure AD B2C has not completed this work yet. B2C would have to become a stateful service and this is a significant investment. So while this is a known issue, this work is not completed at this time.
A workaround is to use the nonce claim.
See also: B2C nonce
I tried to reproduce the same in my environment and got the error like below when I tried to reuse the auth-code:
I created an Azure AD B2C application and added SPA redirect Uri like below:
I generated the auth-code by using below endpoint:
https://tenant.b2clogin.com/tenant.onmicrosoft.com/policy/oauth2/v2.0/authorize?
client_id=ClientID
&response_type=code
&redirect_uri=RedirectUri
&response_mode=query
&scope=scope
&state=state
&code_challenge=codechallenge
&code_challenge_method=S256
I generated the access token by using parameters like below:
https://tenant.b2clogin.com/tenant.onmicrosoft.com/policy/oauth2/v2.0/token
grant_type=authorization_code
&client_id=ClientID
&scope=scope
&redirect_uri=redirectUri
&code_verifier=codeverifier
When I tried to reuse the code, I got the error as below:
Note that: By default, the auth-code lifetime in Azure AD B2C is 10 minutes, and it cannot be changed.
By default, Azure AD B2C will not generate the token if the auth-code is already used.
Make sure to use https to transmit the auth-code.
While generating the auth-code make sure to receive the prompt while signing in by using prompt in the authorize endpoint.
If still the issue persists, try by registering a new application and check.
Reference:
Updates and breaking changes - Microsoft Entra

How to add a secret to "Resource Owner Password Credentials" B2C based app registraion on azure

I have an app registration on Azure that is being used to authenticate requests using the ROPC flow, so that we can mimick a real user access token and test our APIs. This is working, but to make this more secure we would like to be able to say a secret is required in the request.
This is not default for ROPC as it wasn't intended to be used with secrets (note - we cannot use client credentials for this part of testing we are doing).
The app registration is used along with a B2C user flow setup along side it. I don't believe this to be too relevant for this issue, but I could be wrong.
Currently the working call is as follows -
On azure to allow the ROPC flow you need to set the application to allow public flows.
I believe this setting stops any form of client secret or certificate being required in the request.
The microsoft docs seem to suggest it is possible to add a secret to ROPC calls and make them required but i can't figure out a way of doing it while having the above "public" azure setting set to true. With that off you get an error saying that the resource owner flow is not allowed due to it being private.
This is part of the Microsoft docs - https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc which seems to suggest you might be able to make the app private, I just can't see how while allowing the flow.
This link you mentioned is about Azure AD, but not Azure AD B2C. So there are two methods to get the access tokens with different formats. In addition, it's more secure for you to use authorization code flow in Azure Active Directory B2C.
One is following AAD Docs with your AAD B2C directory, see the explanation. Note: scope doesn't support application-id, it's related to the permissions in API Permissions.
POST https://login.microsoftonline.com/{b2c-tenant-name}.onmicrosoft.com/oauth2/v2.0/token
client_id={b2c-application-id}
&scope=openid offline_access
&username={username}
&password={password}
&grant_type=password
&client_secret={client_secret, if your app is not a public client}
[Recommend] Another is testing ROPC flow in Azure AD B2C. client_secret is not in the parameters, so it is not required whether it is public or not.
https://{b2c-tenant-name}.b2clogin.com/{b2c-tenant-name}.onmicrosoft.com/{B2C_1_ROPC_Auth, name of ROPC flow}/oauth2/v2.0/token
username={username}
&password={password}
&grant_type=password
&scope=openid offline_access {b2c-application-id}
&client_id={b2c-application-id}
&response_type=token id_token

Is the Azure Active Directory Access Token valid for ReAuthentication with a Custom WCF Policy?

I am retrieving an Azure AD access token using MSAL.js at the client and passing it as Authorization header to web api hosted in WCF API Management. But I get an error along with all the proper response as well my username and email
Cannot validate access token. IDX10501: Signature validation failed.
Key tried: 'System.IdentityModel.Tokens.X509AsymmetricSecurityKey
The API is a WCF service configured with a custom policy binding for OAuth with the metadataEndpoint or even the v2 version https://login.microsoftonline.com/{tenantid}/.well-known/openid-configuration .
I guess the Azure AD Access token that I have is not meant for validating authentication again.
My guess I might be either missing permissions on the App registration but I couldn't find an API permission for exact authentication/authorization.
I believe with new Azure AD , we cannot authenticate the earlier manner by just passing a token and need to use MSAL standard libraries.
Could someone shed some more light on this.
I used an ID Token and then it worked. You get 2 tokens when authenticated so the ID token seems to work.

Validating Azure B2C Token in app that didn't authenticate user

I have a scenario whereby, a user would sign in/sign up to Azure AD B2C from a frontend application. There after they would make calls to an API application (with a JWT token) separate from this frontend. The requirement is that the API application validates a user's token and decides whether to execute the request. I can send the JWT token from frontend to API using the auth header. I can receive it in the API also. The challenge is to now validate that this token is not faked by a man-in-the-middle or just being abused by someone who got hold of it. I know each token comes with a timestamp and can be checked for expiration. However, this is not sufficient. I need to check it against Azure AD B2C somehow.
Update: the API is a NodeJs based Azure Functions app
You can validate the signatures of the token in your API app. A Token is signed by asymmetric keys.
Every user flow in AzureB2C has a associated metadata document which has all the details about the keys in the tag "jwks_uri"
"jwks_uri": "https://xxxxxx.b2clogin.com/xxxxxxxxx.onmicrosoft.com/discovery/v2.0/keys?p=b2c_1_pe",
you can fetch the key details from the link under jwks_uri tag and use it to validate the signature. Also remember these keys are rotated so you need to get the latest once every 24 hours.
Sample :-
https://github.com/azure-ad-b2c/samples/tree/master/policies/user_info
Doc https://learn.microsoft.com/en-us/azure/active-directory-b2c/tokens-overview
See this sample. The API is protected using the UseOAuthBearerAuthentication to verify the signature of the token. All Azure AD B2C tokens are signed JWTs. The sample code will find the metadata endpoint of your policy+tenant combination, and use the public key to ensure that the token signature is valid/unmodified and has been signed by the private keyholder (AAD B2C)

ADB2C identity experience framework : access token use just after an authentification

we are using lots of ADB2C built-in policies and wanna now switch on custom policies thanks to the Identity Experience Framework.
One of our use-cases is: make some calls (from the policy) on endpoints (that are protected by access token) just after an authentication (signup or signin). For example : just after the signup we would like to call an api for the privacy policy management. In order to have it working, we need to have the access token.
Is there a way, thanks to custom policies, to call an http endpoint with the access token just issued right after the authentication ?
When an access token or id token is generated by Identity Experience Framework (IEF), it signifies that all requirements of the user journey were met. That is, if the user journey required some privacy policy management and user needed to consent to it, only then would the access token or id token be generated.
The scenario that you are mentioning could be achieved by IEF calling the privacy policy management API using service-to-service trust and passing user's identity by other means, such as objectId in header or in the body.
Because IEF is directly calling the Rest API, it is unclear how IEF generating a token and sending that to the Rest API is more beneficial than IEF making a request over SSL and providing user's data.

Resources