As many others, I run tests on my apps before deploying them to production, one kind of tests are the e2e tests written in Cypress.
And for these to run, I need to be able to login with a test user. I’ll get back to how exactly I do that with Cypress in another blog post, but I do it with UI and saving a session as per Cypress own documentation.
But this created a new problem, now any stage environments that Azure Static Web App spins up needs to be a redirect url for my Azure AD B2C that handles the users.
Setting upp Azure B2C
Well, Google to the rescue. Thanks to this GitHub issue, https://github.com/Azure/static-web-apps/issues/992 I got pointed in the right direction, but the answer provided there wasn’t complete. Back, to Google. Thankfully Microsofts documentation come with solutions quickly.
I followed this guide, https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-get-started?tabs=app-reg-ga to create an application that Github Actions could use to authenticate against Azure, giving it the right premissions (those are found in the documentation here, https://learn.microsoft.com/en-us/graph/api/application-update?view=graph-rest-1.0&tabs=http)
Setting up B2C
Following the guide found at https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-get-started?tabs=app-reg-ga in all the steps, using the B2C tenant you want to work with. Remember to save the client secret in the last step of the guide to use in the next step here.
To check if it works, you can use https://developer.microsoft.com/en-us/graph/graph-explorer/ to test. And if you need to switch from your default tenant of your Microsoft account, add a query variable like so, https://developer.microsoft.com/en-us/graph/graph-explorer?tenant=YOUR_TENANT.onmicrosoft.com
Setting up GitHub Secrets
With B2C set up to be able to accept a login from an app, time to save the data needed for GitHub in GitHub secrets and variables. I use environments to be able to shift depending on where I’m doing it, leaving some data global to the repository.
In secrets for my Stage environment, adding AZURE_B2C_CREDENTIALS containing a Json object, as follows:
{
clientId:"B2C_CLIENT_ID",
clientSecret:"B2C_CLIENT_SECRET",
subscriptionId:"B2C_SUBSCRIPTION_ID",
tenantId: "B2C_TENANT_ID"
}
Where you replace the values B2C_CLIENT_ID and the rest with your values from the B2C client you use along with the client secret created in the previous step.
Next we need the application Id for the client in B2C, this is not the client id found in the UI, you need to use https://graph.microsoft.com/v1.0/applications and find the application you want to update with the new url in the list, and get the application id. Save this Id as a secret in GitHub as B2C_APP_ID for the environment used (Stage in the code further down)
Application URL
In my workflow, this is a step after publication of the app, meaning I have the generated url saved and is given it as an input from the previous step. However, you can construct it as:
$appUrl = "https://${{ vars.STATIC_WEB_APP_URL }}-${{ github.event.pull_request.number }}.${{ vars.STATIC_SITE_REGION }}.1.azurestaticapps.net"
Where you then need to save the variables STATIC_WEB_APP_URL and STATIC_SITE_REGION for your static web app.
STATIC_WEB_APP_URL is the the xx part of xx.1.azurestaticapps.net of my Static Web App’s generated url.
STATIC_SITE_REGION is in lowercase, the region chosen for the static web app when it was created.
Updating GitHub Workflow
Okay, ready to write the workflow using what we have preparted. Since I mostly use reusable workflows, I added a new one that I called setup-b2c.yml in my workflows folder.
And wrote it as following:
name: Add app url to B2C app for testing
on:
workflow_call:
inputs:
appUrl:
type: string
required: true
environment:
type: string
required: true
secrets:
AZURE_B2C_CREDENTIALS:
required: true
jobs:
update-b2c:
name: Update B2C Redirect URIs
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_B2C_CREDENTIALS }}
allow-no-subscriptions: true
enable-AzPSSession: true
- name: Update B2C Redirect URIs
uses: azure/powershell@v1
with:
inlineScript: |
$appId= "${{ secrets.B2C_APP_ID }}"
$appUrl = "${{ inputs.appUrl}}"
$apiUrl = "https://graph.microsoft.com/v1.0/applications/" + $appId
$apiUrlSpaFilterd = $apiUrl + "?$select=spa"
$appUris = az rest --method GET --uri $apiUrlSpaFilterd | ConvertFrom-Json
$newUris = @{'spa' = $appUris.spa}
if ( -not ($newUris.spa.redirectUris -contains $appUrl)) {
$newUris.spa.redirectUris += $appUrl
}
$newUrisJson = $newUris | ConvertTo-Json -Compress
$newUrisJson = $newUrisJson -replace '([\\]*)"', '$1$1\"'
az rest --method PATCH --uri $apiUrl --headers 'Content-Type=application/json' --body $newUrisJson
azPSVersion: "latest"
The magic happens in a PowerShell script for Azure, first loading the spas urls already registered, and if it doesn’t, creates a new string where the app’s url is added on and then it is updated at the B2C client with a http Patch call.
And with that it is done, obviously I had to wire up my main file to use the workflow, but that was simply adding a job like this:
setup-b2c:
uses: ./.github/workflows/setup-b2c.yml
needs: deploy
secrets: inherit
with:
appUrl: ${{ needs.deploy.outputs.appUrl }}
environment: Stage
Inspiration
- https://github.com/Azure/static-web-apps/issues/992
- https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-get-started?tabs=app-reg-ga
- https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-operations
UPDATE
A few months back I ran into a problem with with logging in using the client secret, giving me the error “The subscription of ‘xxx’ doesn’t exist in cloud ‘AzureCloud’.
After way more time than I want to admit, I solved it by setting up the Authentication App in B2C to use Federated Credentials towards Github and ran it with that instead successfully. Meaing I had to update the yaml in the setup-b2c.yml to
name: Add app url to B2C app for testing
on:
workflow_call:
inputs:
appUrl:
type: string
required: true
environment:
type: string
required: true
secrets:
AZURE_B2C_CREDENTIALS:
required: true
jobs:
update-b2c:
name: Update B2C Redirect URIs
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Login to Azure
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_B2C_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_B2C_TENANT_ID }}
allow-no-subscriptions: true
enable-AzPSSession: true
- name: Update B2C Redirect URIs
... As before
Where, you guessed it AZURE_B2C_CLIENT_ID and AZURE_B2C_TENANT_ID are new reposisitory secrets representing the authentication client id and tenant from Azure.