We are using Hasura to provide our GraphQL API to consumers. Currently we use Auth0 to authenticate users, but we would like to migrate to Azure AD B2C.
A requirement of JWT security with Hasura is using the "https://hasura.io/jwt/claims" namespace to provide custom claims (such as X-Hasura-Org-Id, X-Hasura-App-Id, etc).
I have been able to get AAD B2C to:
- Gather the required values for these custom claims using a REST API;
- Transform the individual
string
/stringCollection
values into a JSON object using aClaimsTransformation
; and - Return the transformed claims in the JWT.
However, I cannot figure out how to get the JSON object to appear in the final JWT without the contents being escaped - i.e. being output as a string rather than an object.
Is AAD B2C capable of outputting nested objects in a JWT?
What we're hoping to achieve
This is what Hasura wants the JWT namespace to look like (note the https://hasura.io/jwt/claims
object)
{
"exp": 1588405829,
"nbf": 1588402229,
"ver": "1.0",
"iss": "https://<redacted>.b2clogin.com/<redacted>/v2.0/",
"sub": "<redacted>",
"aud": "<redacted>",
"acr": "b2c_1a_aaa_signupsignin",
"nonce": "defaultNonce",
"iat": 1588402229,
"auth_time": 1588402229,
"given_name": "Test",
"family_name": "User",
"name": "Test User",
"email": "[email protected]",
"idp": "facebook.com",
"https://hasura.io/jwt/claims": {
"x-hasura-allowed-roles":["role1","role2","role3"],
"x-hasura-default-role":"role1",
"x-hasura-org-id":"test-org",
"x-hasura-user-id":"test-user-id",
"x-hasura-app-id":"<redacted>"
}
}
What we're getting at the moment
Here's an example of the JWT from AAD B2C:
{
"exp": 1588405829,
"nbf": 1588402229,
"ver": "1.0",
"iss": "https://<redacted>.b2clogin.com/<redacted>/v2.0/",
"sub": "<redacted>",
"aud": "<redacted>",
"acr": "b2c_1a_aaa_signupsignin",
"nonce": "defaultNonce",
"iat": 1588402229,
"auth_time": 1588402229,
"given_name": "Test",
"family_name": "User",
"name": "Test User",
"email": "[email protected]",
"idp": "facebook.com",
"https://hasura.io/jwt/claims": "{\"x-hasura-allowed-roles\":[\"role1\",\"role2\",\"role3\"],\"x-hasura-default-role\":\"role1\",\"x-hasura-org-id\":\"test-org\",\"x-hasura-user-id\":\"test-user-id\",\"x-hasura-app-id\":\"<redacted>\"}"
}
There doesn't appear to be an option to store a claim as an object, only a string.
How we got there
An example of the ClaimsTransformation
:
<ClaimsTransformation Id="hasuraClaimsToJson" TransformationMethod="GenerateJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="x-hasura-allowed-roles" TransformationClaimType="x-hasura-allowed-roles" />
<InputClaim ClaimTypeReferenceId="x-hasura-default-role" TransformationClaimType="x-hasura-default-role" />
<InputClaim ClaimTypeReferenceId="x-hasura-org-id" TransformationClaimType="x-hasura-org-id" />
<InputClaim ClaimTypeReferenceId="x-hasura-user-id" TransformationClaimType="x-hasura-user-id" />
</InputClaims>
<InputParameters>
<InputParameter Id="x-hasura-app-id" DataType="string" Value="internal-redacted-uuid" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="hasuraClaims" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
Example RelyingParty
config:
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="hasuraClaims" PartnerClaimType="https://hasura.io/jwt/claims" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
ClaimsTransformation
andRelyingParty
configs above. We followed Microsoft's Integrate REST API claims exchanges in your Azure AD B2C custom policy document. I'll do a blog post with examples and share here. – Dave