Azure Static Web Apps are an awesome service on Azure if you want a simple and low cost way to host an html based website with some fairly simple content. In my case I wanted to have a site which was also restricted so that only users within a certain entra group could access it.
I found it a little awkward to configure the authentication the way I wanted so making a few notes for myself on what makes it work.
My requirements were:
- Deploy a lightweight website with just html files
- Limit access to just users in my tenant
- Limit access to just users within a group
The steps to do this are below.
Caveats
- You need to use a standard sku for the static web app
Step 1
I need to modify the staticwebapp.config.json file to add the Entra authentication provider. Below is the config to add
"auth": {
"identityProviders": {
"azureActiveDirectory": {
"registration": {
"openIdIssuer": "https://login.microsoftonline.com/[Your tenant id]/v2.0",
"clientIdSettingName": "AAD_CLIENT_ID",
"clientSecretSettingName": "AAD_CLIENT_SECRET"
},
"login": {
"loginParameters": [
"domain_hint=[Your domain]"
]
}
}
}
},
Step 2
One of the bits that confused me was the AAD_CLIENT_ID is actually a reference to an environment variable so i needed to add this to my static web app environment variables. This is where they go

Step 3
Back in the staticwebapp.config.json file I now need routes to control access. In ths case I am allowing unauthenticated users to hit the login page but restricting the others as per the config below.
"routes": [
{
"route": "/login",
"rewrite": "/login/index.html"
},
{
"route": "/.auth/logout",
"redirect": "/login",
"statusCode": 302
},
{
"route": "/*",
"allowedRoles": ["authenticated"]
}
],
"responseOverrides": {
"401": {
"redirect": "/.auth/login/aad?post_login_redirect_uri=/",
"statusCode": 302
}
},
Step 4
I now add an index.html file in the log in folder in the web application and I just had a button which triggers the redirect to the /.auth/login/aad path which would trigger the authentication provider.
If you are following this walk through then the login page html is at the bottom of the page if you want it.
Step 5
Next I need to change the enterprise app associated with my Service Principal to limit assignment for which users can authenticate with it. At present the default will allow all users. I will limit it to assigned users.

Step 6
I then go and add the users to the users and groups section of the enterprise app

Step 7
Now I can go to the web app and browse to it. If I authenticate with a user who is in the group or a direct assignment to the enterprise app then I am good, if they arent assigned then they get blocked.
Notes
Login Page
Below is the login page if it helps.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sign In | Acme Portal</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #f4f6f9;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
color: #1a1a2e;
}
.card {
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0,0,0,0.10);
padding: 48px 40px 40px;
width: 100%;
max-width: 420px;
text-align: center;
}
.logo {
font-size: 2rem;
font-weight: 800;
color: #0078d4;
letter-spacing: -1px;
margin-bottom: 8px;
}
.logo span { color: #1a1a2e; }
.subtitle {
font-size: 0.95rem;
color: #555;
margin-bottom: 36px;
}
.badge {
display: inline-block;
background: #e6f2fb;
color: #0078d4;
font-size: 0.75rem;
font-weight: 600;
padding: 4px 10px;
border-radius: 20px;
margin-bottom: 28px;
letter-spacing: 0.5px;
text-transform: uppercase;
}
.btn-sso {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
width: 100%;
padding: 14px 20px;
background: #0078d4;
color: #fff;
font-size: 1rem;
font-weight: 600;
border: none;
border-radius: 8px;
cursor: pointer;
text-decoration: none;
transition: background 0.2s;
}
.btn-sso:hover { background: #005a9e; }
.btn-sso svg { flex-shrink: 0; }
.divider {
margin: 28px 0 20px;
border: none;
border-top: 1px solid #e8e8e8;
}
.footer-note {
font-size: 0.78rem;
color: #888;
line-height: 1.5;
}
.error-msg {
display: none;
background: #fef0f0;
border: 1px solid #f5c6cb;
color: #c0392b;
border-radius: 6px;
padding: 10px 14px;
font-size: 0.875rem;
margin-bottom: 20px;
text-align: left;
}
</style>
</head>
<body>
<div class="card">
<div class="logo">Acme</div>
<div class="subtitle">Private Portal</div>
<div class="badge">Internal Use Only</div>
<div class="error-msg" id="errorMsg">
Sign-in failed.
</div>
<a class="btn-sso" href="/.auth/login/aad?post_login_redirect_uri=/" id="loginBtn">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 23 23" fill="none">
<rect x="1" y="1" width="10" height="10" fill="#f35325"/>
<rect x="12" y="1" width="10" height="10" fill="#81bc06"/>
<rect x="1" y="12" width="10" height="10" fill="#05a6f0"/>
<rect x="12" y="12" width="10" height="10" fill="#ffba08"/>
</svg>
Sign in with Microsoft
</a>
<hr class="divider" />
<p class="footer-note">
This portal contains confidential information.<br />
Access is restricted to authorised users only
</p>
</div>
<script>
// Show error if redirected back with an error parameter
const params = new URLSearchParams(window.location.search);
if (params.get('reason') === 'unauthorized' || params.get('reason') === 'forbidden') {
document.getElementById('errorMsg').style.display = 'block';
}
// Preserve intended destination after login
const redirect = params.get('redirect');
if (redirect) {
document.getElementById('loginBtn').href =
`/.auth/login/aad?post_login_redirect_uri=${encodeURIComponent(redirect)}`;
}
</script>
</body>
</html>
