Azure Active Directory Integration with Hasura JWT Claims Mapping
Prerequisites
- Azure account with subscription
- Azure functions core tools installed (VSCode with Azure Functions extension recommended)
- Heroku account
Set up Hasura Cloud
- Create an account at Hasura Cloud, and generate a new free-tier project
- After the project is created, it will bring you to your project settings page. Click "Launch Dashboard" at the top right to open the Hasura web console
- From the Hasura web console, you will be greeted with a getting-started menu. At the top of this menu, select "Connect your first Database". (If this menu is not present, or you have dismissed it, you may also click the "DATA" tab at the top of the page)
- Click "Connect Database"
- Switch tabs on the page you're navigated to, from the default "Connect existing Database" to "Create Heroku Database (Free)". Then press the "Create Database" button to automatically create and connect a Hobby-tier Heroku DB to your Hasura Cloud instance (requires a Heroku account).
- Add a "users" table to your new Hasura project with fields
id
,azure_id
, androle
.
Set up Azure Functions
- Create a directory for your new Azure functions project and initialize it. You'll be taken through a couple of setup questions: in this demo, we've selected "node" and "JavaScript"
$ mkdir <functions-directory-name>
$ cd <functions-directory-name>
$ func init .
- Create your first function! Again, you'll be asked a couple of questions: here we selected "HTTP trigger" and named our function "getHasuraInfo". Check to see that it all works locally, but then you can stop the server.
$ func new
$ npm install
$ func start
Set up Azure AD B2C
- Set up an Azure AD B2C tenant according to the docs
- Set up the B2C tenant for custom claims according to the docs
- This demo skips the Facebook social login
- This step is just setup of the custom claims flow; we will add the actual custom fields in the next step
Test that you can generate a valid JWT with this new flow
- Navigate back to Identity Experience Framework dashboard
- Scroll down to see the list of custom policies, select "B2C_1A_signup_signin"
- Select
https://jwt.ms
as the reply url - Run! Sign up and see that a new user is created and a valid JWT provided
Modify your Azure AD B2C setup to include your custom claims
- Follow the instructions from this tutorial to modify your custom policies with custom claims
- See the demo custom policy files in this repo
- Use the static-response version of your Azure function for now
- Set up environment variables locally in
local.settings.json
under "values". Add the environment variables for prod via the Azure Portal: go to your Functions App, navigate to 'Configuration' (under 'Settings'), and add your environment variables as new application settings. Don't forget to save! - Check again that everything still starts locally as a sanity check before re-deploying the function
- Test again with
"B2C_1A_signup_signin"
again to verify that you're getting a valid JWT with the expected claims and that a user is created in Hasura
Update your Hasura instance to use Azure tokens
- Add the
HASURA_GRAPHQL_JWT_SECRET
environment variable to your Cloud app from the settings page in the Cloud dashboard:
- Set the following as the value for
HASURA_GRAPHQL_JWT_SECRET
, to configure the JWK url and use the "Claims Mapping" feature (see more here) to map Azure AD's claims to the values Hasura needs for it's JWT tokens:
{
"jwk_url": "https://<mytenant>.b2clogin.com/<mytenant>.onmicrosoft.com/discovery/v2.0/keys?p=b2c_1a_signup_signin",
"claims_map": {
"x-hasura-allowed-roles": ["user", "admin"],
"x-hasura-default-role": { "path": "$.extension_hasura_role" },
"x-hasura-user-id": { "path": "$.extension_hasura_id" }
}
}
Using with Azure AD (not B2C)
- Export the existing users from your Azure AD tenant
- Bulk import the user data into a
users
table in Hasura with the Azure AD objectId (idToken claim:oid
) as the user id. Roles could be created from group id's or separately.
{
"jwk_url": "https://login.windows.net/common/discovery/keys",
"claims_map": {
"x-hasura-allowed-roles": { "path": "$.groups" },
"x-hasura-default-role": { "path": "$.groups[0]" },
"x-hasura-user-id": { "path": "$.oid" }
}
}
Related reading