Azure Static Web Apps: login_hint now works
Source: Dev.to
This post shows implementation of the login_hint which does not work with Azure Static Web Apps (SWA) by default.
References
(no items listed)
Auth Flow
-
Go to
https://swa.azurestaticapps.net/.auth/login/aad?login_hint=user@mail.com -
The user is redirected to
https://swa.azurestaticapps.net/.auth/login/aad?post_login_redirect_uri=/.auth/complete&staticWebAppsAuthNonce=aTakZLY%2fCmXnnD%2foxHxXW%2fWDcXGAy27B84se3dzrpE7UcwEFqKGy2VNnXRqvPInletF6R26ZDfdMSD0kKda41Y8%2b3BXO%2bHUoG3VEbaJpSkhdQ%2fRFWgFb1nKNWZ80dtzW -
Then to
https://login.microsoftonline.com/c74da02d-281d-4a45-a4af-cc520eafa6e3/oauth2/v2.0/authorize? response_type=code+id_token& redirect_uri=https%3A%2F%2Fswa.azurestaticapps.net%2F.auth%2Flogin%2Faad%2Fcallback& client_id=6c3476f8-54c2-4322-8401-f7774963a1e1& scope=openid+profile+email& response_mode=form_post& resource=https%3A%2F%2Fgraph.microsoft.com& nonce=9108b12ec87a4effa26bd5287d792605_20251223165427& state=redir%3D%252F.auth%252Fcomplete
Problem
login_hint is lost and never passed to the login.microsoftonline.com endpoint.
Fix
I implemented a middleware with Azure Functions v4 on Node using HTTP/2 requests.
Why HTTP/2?
The auth flow uses pseudo‑headers (:authority, :path) that belong to the HTTP/2 protocol.
I will provide the full project setup later.
Steps
-
User request – The user goes to
https://swa.azurestaticapps.net/api/whoami?login_hint=user@mail.com -
Function trigger – The request is received by the function
app.http("whoami"... )and invokes ahandlerfunction. -
Processing – The handler extracts the original SWA URL and
login_hintfrom the request, follows redirects, registers cookies, and finally builds a full URL for thelogin.microsoftonline.comendpoint that includes the proper nonce cookie. -
Redirect – The function returns a redirect response to the SWA page, which then completes the authentication flow.
-
Error handling – If something goes wrong, the function redirects back to
/.auth/login/aad, logs details withcontext.log, and the logs can be inspected in the Azure portal.
Function Code
import { app } from "@azure/functions";
import { connect } from "node:http2";
let count = 5;
/**
* Recursively follows redirects using HTTP/2 and injects login_hint when needed.
*
* @param {string} url – The URL to request.
* @param {PromiseWithResolvers} pWithResolvers – Optional promise resolvers (for recursion).
* @param {Object} headersObj – HTTP/2 pseudo‑headers for the request.
* @param {string} loginHint – The login_hint value (may be empty).
* @returns {Promise}
*/
async function getData(url, pWithResolvers, headersObj = {}, loginHint) {
const p = pWithResolvers ?? Promise.withResolvers();
const clientSession = connect(url);
clientSession.on("error", (err) => console.error(err));
const req = clientSession.request({
...headersObj,
});
req.on("response", (headers) => {
req.on("error", (e) => {
context.log(`problem with request:`, {
msg: e.message,
stack: e.stack,
e,
});
clientSession.close();
p.reject(e.message);
});
const status = headers