Skip to content

Authentication for Windows-hosted apps: IIS, AD, JWT and OIDC

Verified 2 min read

Choose Windows/AD auth, Basic, JWT or OIDC deliberately, avoid double-hop surprises, and test 401 vs 403.

Tested onStandard / Datacenter · Build 26100.33650 · x64
E1
Build
26100.33650
Edition
Standard / Datacenter
Architecture
x64
Support
Documentation (E1)
Evidence
E1Official documentation
Verified
2026-10-29
Page type
Concept
Need Choice
Internal Windows users, domain Windows auth (Negotiate/Kerberos)
Legacy app, no SSO Basic only over TLS, never public
SPA/API OIDC authorization code + PKCE, JWT access tokens
Server-to-server Client credentials/mTLS, not a shared password
Public consumer login OIDC/IdP with MFA; do not build your own password store

Authentication answers “who”; authorization answers “what”. A 401 means not authenticated; 403 means authenticated but not allowed.

Enable only the providers you need:

Terminal window
Enable-WindowsOptionalFeature -Online -FeatureName IIS-WindowsAuthentication -All

In IIS: disable Anonymous, enable Windows Authentication; choose Negotiate/Kerberos (NTLM fallback only if needed). For Kerberos:

  • SPN registered for the service account/host;
  • DNS name matches the SPN;
  • delegation configured only if you need double-hop.

Double-hop (IIS → SQL/file share as the user) fails without constrained delegation. Prefer a service identity for the backend instead of impersonating the user everywhere.

Basic sends Base64 credentials; without TLS it is plaintext. Use it only for a legacy device on a private network, over HTTPS, with a scoped account. Never expose it to the internet.

  • Validate signature, issuer, audience, expiry and clock skew.
  • Do not trust alg: none or a token just because it decodes.
  • Keep access tokens short; use refresh tokens carefully.
  • Store browser tokens in HttpOnly/Secure/SameSite cookies where possible; avoid localStorage for long-lived tokens.
  • Put authorization checks server-side; hiding a button is not authorization.
  • Authenticate at the edge/IdP, then pass a signed identity (header/JWT) to the app.
  • Strip client-supplied identity headers at the proxy; only the proxy may set them.
  • Do not let the app trust X-User from the internet.
Terminal window
curl.exe -I https://app.example/
curl.exe -I -u user:pass https://app.example/private

Check the challenge, cookie flags (Secure, HttpOnly, SameSite), token expiry, and that a low-privilege user gets 403, not data.

E1. The lab used a local admin for WAC; it did not configure AD/Kerberos/OIDC for a web app. The decision table is the design to validate on Server 2025.

Sources