I have started to look into Azure web apps to implement the following scenarion in a single Azure tenant:
- Frontend web app that shall verify user's credentials, allow or reject the access and send user's requests to a backend web app
- Backend web app used as an API endpoint that will communicate with Azure SQL db and storage and serve user's requests from the frontend app.
One of the most important things to establish is security and token exchange. I've been struggling a bit finding the right way to connect frontend web app with the backend one, and in this post I share my findings.
For now I just drop some draft notes which will later be properly organizied as I progress...
Frontend web app set up
Provision a new web app in azure called site
and url site.azurewebsites.net
. Any unauthenticated requests to this url shall be redirected to AAD identity provider.
Configuration
Stack -> Node 22
Startup Command -> pm2 serve /home/site/wwwroot --no-daemon --spa
Authentication
Add Microsoft as identity provider.
Restrict access -> Require authentication
Unauthenticated requests -> Return HTTP 302 Found (Redirect to identity provider)
Redirect to -> Microsoft
Token store -> Enabled
Supported account types -> Current tenant - Single tenant
I use existing app and secret with API permissions set to Microsoft Graph (more details on it later). The same app will be used for the backend web app.
I have also set up a route that enables unauthenticated access using file 'authConfig.json' uploaded to /site/wwwroot
with setting excludedPaths
in it, modified with tool at resources.azure.com/
. The settings are located here:
resources.azure.com/subscriptions/<your-subscription-guid>/resourceGroups/your-rg-name/providers/Microsoft.Web/sites/your-site/config/authsettingsV2/list
There with the provided PUT request tooling you add to platform
a new key/value "configFilePath": "authConfig.json"
to use the file. More details on it later...
Backend web app set up
Same as the frontend but called api-site
and url api-site.azurewebsites.net
. In the authentication we choose Microsoft and the same app/secret as for the frontend.
For Unauthenticated requests we choose Return HTTP 401 Unauthorized.
Set also CORS to allow site.azurewebsites.net.
Final working snippet
Method 1
fetch('/.auth/refresh')
.then(() => {
fetch('/.auth/me')
.then(res => res.json())
.then(arr => {
const idToken = arr[0].id_token;
fetch('https://api-site.azurewebsites.net/.auth/login/aad', {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({"access_token": idToken})
})
.then(res => res.json())
.then(authRes => {
const authToken = authRes.authenticationToken;
// console.log(authToken);
fetch('https://api-site.azurewebsites.net/.auth/me', {
headers: {"X-ZUMO-AUTH": authToken}
})
.then(res => res.text())
.then(console.log)
})
})
})
Having logged in to site.azurewebsites.net
, open devtools and execute the above code in the console.
Explanation
First we refresh the token by calling site.azurewebsites.net/.auth/refresh
, then we obtain id_token
by calling site.azurewebsites.net/.auth/me
. This id_token
is then used to call https://api-site.azurewebsites.net/.auth/login/aad
to get back authenticationToken
which is passed as X-ZUMO-AUTH
header when making API-call to https://api-site.azurewebsites.net
.
In the above snippet I send a request to a protected route https://api-site.azurewebsites.net/.auth/me
that gives back details of the user logged in to api-site.azurewebsites.net, demonstrating that the flow works.
authenticationToken
has exp
(Expiration time) 30 days, so in theory it can be used without the need to refresh, but this needs to be checked. So all consequent call to the API-endpoint can be easily done as follows:
// authToken is saved before
fetch('https://api-site.azurewebsites.net/.auth/me', {
headers: {"X-ZUMO-AUTH": authToken}
})
.then(res => res.text())
.then(console.log)
Method 2 (alternative)
Backend app shall authorize frontend to accept the id_token
as mentioned here:
Note: id_token
shall be provided, not access_token
as mentioned on the linked resource.
To enable the the backend accept the frontend token we may use the above mentioned resources.azure.com
where we need to find authsettingsV2
in the backend app here resources.azure.com/subscriptions/SUBSCR_GUID/resourceGroups/RG_NAME/providers/Microsoft.Web/sites/APP_NAME/config/authsettingsV2/list
-> find defaultAuthorizationPolicy
-> allowedApplications
-> add the frontend enterprise application Object ID to the array.
As a result we can call the backend from the frontend with this code resulting in the same response as in Method 1:
fetch('/.auth/me') // calling from your frontend site
.then(res => res.json())
.then(arr => {
const idToken = arr[0].id_token;
// calling protected endpoint of the backend app
fetch('https://api-site.azurewebsites.net/.auth/me', {
headers: {
"Content-Type": "application/json",
'Authorization': `Bearer ${idToken}`
}
})
.then(res => res.text())
.then(console.log)
})
Top comments (0)