B2C Custom Policy error with client certificates - azure-ad-b2c

I am using B2C custom policies to federate to the external Azure AD Identity Provider.
I was successful before when federating with client secrets, but trying to switch to certificates now, and getting an error "No url encoding for asymmetric keys". Here is what I did as a Proof Of Concept:
Generated a new self-signed certificate in Azure keyvault
Exported it into the .pfx and .cer files.
In B2C created a Policy key and uploaded the .pfx certificate there
In custom policy referenced that policy key
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_MyB2CPolicy" />
</CryptographicKeys>
In Azure AD app registration under Certificates and Secrets uploaded the .cer file (with public key)
Tried to connect using MSAL.js
Received a "server error" with correlation id, which in appinsights shows up as
{
"Key": "SendErrorTechnicalProfile",
"Value": "OpenIdConnectProtocolProvider"
},
{
"Key": "Exception",
"Value": {
"Kind": "Handled",
"HResult": "80131500",
"Message": "An invalid OAuth response was received: \"{0}\".",
"Data": {
"IsPolicySpecificError": false
},
"Exception": {
"Kind": "Handled",
"HResult": "80131515",
"Message": "No url encoding for asymmetric keys",
"Data": {}
}
}
}
What am I doing wrong? Do I need to convert the certificates to another format before uploading? Perhaps .pem? The problem is, there is no documentation I could find on this anywhere.

Azure AD B2C can use client certificate to redeem authorization codes at the /token endpoint of a federated IdP.
See the reference for the OpenId Connect Technical profile on all supported options.
Based on Azure AD doc for certificate credential here and Metadata options available, in the OIDC Technical profile, use the following options:
token_endpoint_auth_method: private_key_jwt
token_signing_algorithm: RS256
In the cryptographic keys section, reference your certifcate that you uploaded into B2C Policy Keys:
assertion_signing_key: B2C_1A_MyB2CPolicy
Remove the client_secret reference.

Related

Retrieve custom claim from federating IdP access token in Azure AD B2C custom policy

I am configuring an OIDC-based SSO flow in Azure AD B2C using custom policy to allow users to login to downstream applications with their federated identity provider's (IdP) credentials. Custom policy is used to allow some complex business logic to be run prior to providing the token to the downstream applications.
The flow is correctly redirecting users to the external IdP for login and ultimately back to my downstream applications with associated claims. However, there is a custom claim that is only available in the access token received by B2C from the external IdP (not the ID token), and I can't figure out how to retrieve this claim from the access token to be used in the B2C user journey and ultimately provided with all the other claims to the downstream applications.
I can see that B2C does receive both the ID and access tokens by reviewing Application Insights logs (sample output):
"TESTtechnicalprofile": {
"ContentType": "Jwt",
"Created": "2022-10-15T07:37:45.8678974Z",
"Key": "TESTtechnicalprofile",
"Persistent": true,
"Value": "eyJhb..."
},
"TESTtechnicalprofileaccess_token": {
"ContentType": "Unspecified",
"Created": "2022-10-15T07:37:45.8678974Z",
"Key": "TESTtechnicalprofileaccess_token",
"Persistent": false,
"Value": "eyJhb..."
},
And general format of the payloads of the tokens is as follows:
ID Token
{
"<name of custom claim I can retrieve>": "custom claim value",
"iss": ...,
...
}
Access Token
{
"<name of custom claim I cannot retrieve>": "custom claim value",
"iss": ...,
...
}
I can successfully retrieve claims from the ID token by mapping from the partner claim type:
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="<name of claim containing email in ID token>" />
However this same method doesn't work for claims in the access token. If I reference the name of the custom claim in the access token in PartnerClaimType then B2C omits the claim (presumably because it fails to retrieve it).
I've tried retrieving the access token itself as a claim using the method described here and that works (token in claim matches token seen in Application Insights logs), however I'm not sure if it's possible to decode this token in B2C policy and subsequently pull claims from it (or even if one would want to do that).
While I could let the downstream applications retrieve what they need from the access token, I have business logic in my user journey that needs this claim prior to providing the final token to the applications.
Following up here for anyone else trying to do the same, according to Microsoft Support it isn't possible to extract a claim from an access token in B2C policy. I ended up crafting a workaround involving calling an external REST API from B2C to retrieve the needed info for the user journey.

Azure AD B2C breaks OIDC spec

I've configured a custom policy with AAD B2C IEF per this link and am now trying to integrate it into API Gateway as a JWT authorizer per this link.
However, attempting to configure the authorizer throws an error
error updating API Gateway v2 authorizer
BadRequestException
Caught exception when connecting to https://tenant-domain.b2clogin.com/tenant-id-here/v2.0/.well-known/openid-configuration for issuer https://tenant-domain.b2clogin.com/tenant-id-here/v2.0/.
Please try again later.
Error:
Invalid issuer:
https://tenant-domain.b2clogin.com/tenant-id-here/v2.0/.
Issuer must have a valid discovery endpoint ended with '/.well-known/openid-configuration
The actual discovery endpoint is https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/v2.0/.well-known/openid-configuration, however, that returns a doc as below, which has a different issuer than the discovery URL.
{
"issuer": "https://tenant-domain.b2clogin.com/tenant-id-here/v2.0/",
"authorization_endpoint": "https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/oauth2/v2.0/authorize",
"token_endpoint": "https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/oauth2/v2.0/token",
"end_session_endpoint": "https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/oauth2/v2.0/logout",
"jwks_uri": "https://tenant-domain.b2clogin.com/tenant-domain.onmicrosoft.com/b2c_1a_signup_signin/discovery/v2.0/keys",
"response_modes_supported": [
"query",
"fragment",
"form_post"
],
"response_types_supported": [
"code",
"code id_token",
"code token",
"code id_token token",
"id_token",
"id_token token",
"token",
"token id_token"
],
"scopes_supported": [
"openid"
],
"subject_types_supported": [
"pairwise"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"token_endpoint_auth_methods_supported": [
"client_secret_post",
"client_secret_basic"
],
"claims_supported": [
"name",
"given_name",
"family_name",
"email",
"sub",
"tid",
"iss",
"iat",
"exp",
"aud",
"acr",
"nonce",
"auth_time"
]
}
Looking at this issue and the spec, it looks like AAD is not spec compliant.
Is there any way to get this to work or do I have to move to a spec-compliant OIDC provider?
Please try to configure issuer URL including tfp for token compatibility.
For more details see: Token compatibility
which says:
Note : iss claim i.e; issuer identifies tenant of azure ad b2c that
issued the token. Usually the value is some thing like this
:https://<domain>/{B2C tenant GUID}/v2.0/
But If your application or library needs Azure AD B2C to be
compliant with the OpenID Connect Discovery 1.0 spec, use this
https://<domain>/tfp/{B2C tenant GUID}/{Policy ID}/v2.0/ as it
includes IDs for both the Azure AD B2C tenant and the user flow that
was used in the token request.
For example:
“issuer” : “https://your-tenant-name.b2clogin.com/tfp/c5b2xxxxxxxxx0-8axxxxxx3d3b/B2C_1A_signin/v2.0/”
or
https://{tenantID}.b2clogin.com/tfp/{tenantID}/{policy-name}/v2.0/
References:
Configure the Azure Active Directory B2C provider manually - Power
Apps | Microsoft Docs
AzureAD Authentication with AWS API Gateway v2 JWT Authorizers |
rayterrill.com
In addition to the answer by #kavyasaraboju-MT, if you're using custom policies, you must set the IssuanceClaimPattern to AuthorityWithTfp in the JwtIssuer Tehcnical Profile per these docs.
e.g. using the LocalAccounts pack in active-directory-b2c-custom-policy-starterpack, add the element <Item Key="IssuanceClaimPattern">AuthorityWithTfp</Item> to the <Metadata> element

Azure AD, When I use custom domain in Azure WebApp to access, I can't get the correct token

I built a WebApp on Microsoft Azure, this WebApp needs to access a WebApi to get data, I used Azure AD authentication, everything works fine, but when I bind a custom domain to this WebApp, I go to use This custom domain to access the Token obtained by the WebApp is not correct.
Correct and incorrect token comparison:
Correct:
"aud": "api://[client id]",
"scp": "user_impersonation",
incorrect:
"nonce": "2o-O2KpKPQekxB_vLD9myPz2zq4n333dUphtn7FhJk",
"aud": "00000003-0000-0000-c000-000000000000",
"scp": "email openid profile",

Generate the token that can be validated against RSA signing key in azure

I am using an Azure B2C custom policy where I want to send an invitation link to the user, with JWT token containing necessary details.
I need to create the JWT token in JAVA. Using the jjwt library I was successful in creating a token using HS256, but later found out that I will require the token to be signed using RSA256.
I have the TokenSigningKeyContainer policy key in Azure AD B2C, with below fields
{
"metadata": {
"tenantID": "<tenant>.onmicrosoft.com",
"storageKeyId": "B2C_1A_TokenSigningKeyContainer",
"updatedUtc": "10/16/2019 2:12:11 PM"
},
"keys": [
{
"kid": "<key_id>",
"use": "sig",
"key_ops": [
"sign"
],
"kty": "RSA",
"e": "<>",
"n": "<n_value>"
}
]
}
This seems to be the public key to verify the generated token, sent in the invitation link.
What I need to know is how I can get the necessary details for the private key, to sign the JWT token?
AzureAD B2C doesn't reveal the private key once it is uploaded. By using apis, you can get access to only metadata, by using wellknown metadata discovery points, you can get public keys.
You need to store the cert with encrypted data store (such as Azure Key Vault) and use it while signing the token. According to the sample, you don't need to upload it to the AADB2C either.

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.

Resources