Title image

At work we use acmebot with a dashboard available through an azure web app. Access to the dashboard is configured to use authentication to Entra via an Enterprise application and Easy Auth. The app registration is configured to use a client secret and by the time I got back from summer vacation, the secret had expired… So now I want to eliminate the need for these client secrets!

Easy Auth

Easy Auth is the built-in authentication (signing in users) and authorization (providing access to secure data) capabilities in Azure App Service. You can use these mechanisms to require users to sign-in in order to access data, all by writing little or no code in your web app, RESTful API, mobile server, and functions.

Have a look at the feature architecture for details about how EasyAuth works.

In this post I will setup a website with authentication through Easy Auth using an app registration and a client secret. To eliminate the use of the client secret, federated credentials will be configured. In the post I use a static web app, but same would apply to any app service.

Entra App Registration

First create and prepare the app registration. Use placeholder values for login and logout callback urls, I’ll come back and update these urls with the correct values once the webapp is up.

created app registration
created app registration

For the initial setup I go ahead and create a client secret, the goal will be to eliminate the use for it.

client secret
client secret

Azure Web App

Next up is to create a web app. Since it is a demo, I have put the client secret as a cleartext instead of a keyvault reference.

targetScope = 'subscription'

param resourceGroupName string
param appName string
param appLocation string
param easyAuthAppRegClientId string
param uamiName string

module rg 'br/public:avm/res/resources/resource-group:0.4.1' = {
  params: {
    name: resourceGroupName
  }
}

module uami 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = {
  scope: resourceGroup(resourceGroupName)
  dependsOn: [rg]
  params: {
    name: uamiName
  }
}

// resource type 'Microsoft.Web/staticSites'. List of available regions for the resource type is 'westus2,centralus,eastus2,westeurope,eastasia'.
module staticwebapp 'br/public:avm/res/web/static-site:0.9.1' = {
  scope: resourceGroup(resourceGroupName)
  dependsOn: [rg]
  params: {
    name: appName
    location: appLocation
    sku: 'Standard'
    publicNetworkAccess: 'Enabled'
    appSettings: {
      AZURE_CLIENT_ID: easyAuthAppRegClientId
      MICROSOFT_PROVIDER_AUTHENTICATION_SECRET: 'avoid-clear-text-secret-4TR8Q...'
      // alternative keyvault reference '@Microsoft.KeyVault(VaultName=${keyVaultName};SecretName=clientSecret)'
    }
    managedIdentities: {
      userAssignedResourceIds: [uami.outputs.resourceId]
    }
  }
}

output staticwebappUamiObjectId string = uami.outputs.principalId

Site content

My demo site will be a static html file and staticwebapp.config.json for storing the app registration details for easy auth, and require authentication for all routes.

./src/app/index.html

<h1>Static Web App behind Easy Auth</h1>

./src/app/staticwebapp.config.json

{
  "routes": [
    {
      "route": "/*",
      "allowedRoles": ["authenticated"]
    }
  ],
  "responseOverrides": {
    "401": {
      "redirect": "/.auth/login/aad?post_login_redirect_uri=.referrer",
      "statusCode": 302
      }
  },
  "auth": {
    "identityProviders": {
      "azureActiveDirectory": {
        "registration": {
          "openIdIssuer": "https://login.microsoftonline.com/{TENANT-ID}/v2.0",
          "clientIdSettingName": "AZURE_CLIENT_ID",
          "clientSecretSettingName": "MICROSOFT_PROVIDER_AUTHENTICATION_SECRET"
        }
      }
    }
  }
}

Install the swa cli

winget install -e --id OpenJS.NodeJS
npm install -g @azure/static-web-apps-cli
swa --version

Publish site

./scripts/publish-to-stapp.ps1

#Requires -Modules @{ ModuleName="Az.Websites"; ModuleVersion="3.2.1" }

param (
    $Name = "stapp-easyauth-001",
    $ResourceGroupName = "rg-easyauth-001",
    $Environment = "preview-{0}" -f (Get-Date -Format "yyyyMMddHHmmss")
)

$env:SWA_CLI_DEPLOYMENT_TOKEN = ((Get-AzStaticWebAppSecret -Name $Name -ResourceGroupName $ResourceGroupName).Property | ConvertFrom-Json).apiKey

swa deploy ./src/app --app-name $Name --resource-group $ResourceGroupName  --env $Environment --deployment-token $env:SWA_CLI_DEPLOYMENT_TOKEN
swa deploy
swa deploy

Test site with client secret

With the app service up, head back to the app registration and update the redirect url and logout url. If everything is done correctly the site should redirect to a microsoft signin page if the visitor does not have any active session. Signing in with valid credentials will allow entrance to the site.

site redirects to login
site redirects to login
site up
site up

Eliminate client secret

Now what we are really here for, remove the need for a client secret…

The app registration will now be updated with federated credentials allowing the user assigned managed identity associated with the web app to impersonate the application.

add federated credentials
add federated credentials
details for federated credentials
details for federated credentials
federated credentials created
federated credentials created

Now the property-value for clientSecretSettingName in staticwebapp.config.json must be updated to OVERRIDE_USE_MI_FIC_ASSERTION_CLIENTID:

{
  ...
  "registration": {
    "openIdIssuer": "https://login.microsoftonline.com/{TENANT-ID}/v2.0",
    "clientIdSettingName": "AZURE_CLIENT_ID",
    "clientSecretSettingName": "OVERRIDE_USE_MI_FIC_ASSERTION_CLIENTID"
  }
  ...
}

And the app service needs to have its appSetting MICROSOFT_PROVIDER_AUTHENTICATION_SECRET removed in favour of the newer OVERRIDE_USE_MI_FIC_ASSERTION_CLIENTID.

...
  appSettings: {
    AZURE_CLIENT_ID: easyAuthAppRegClientId
    // MICROSOFT_PROVIDER_AUTHENTICATION_SECRET: 'avoid-clear-text-secret-4TR8Q...' // REMOVE ME
    OVERRIDE_USE_MI_FIC_ASSERTION_CLIENTID: uami.outputs.clientId
  }
...

With both the updated bicep code and site content deployed, the site should still function the same and I can finally remove the client secret from the app registration!

Logs

Having a look at the sign-in logs for enterprise application show the sign-in Client credential type change from Client secret to Federated identity credential.

federated credentials created
federated credentials created
federated credentials created
federated credentials created

Closing words

According to the app-service documentation this is in preview for azure app service, but managed identities as federated credentials for Entra apps is already in GA! More documentation.