Angular + .NET (Azure AD BFF): Handling 401 due to token expiry without losing unsaved form data (auto-save to DB only)

Summary

This incident centers on 401 Unauthorized responses caused by Azure AD cookie expiration in an Angular + .NET BFF architecture. Because the frontend stores no tokens locally and the session cookie silently expires, long‑running form sessions lead to data loss when auto‑save or submit calls fail. The user is redirected before the backend can persist unsaved data. This postmortem explains why this happens, how real systems mitigate it, and what senior engineers do to prevent data loss in regulated environments.

Root Cause

The root cause is a combination of silent cookie expiration and frontend behavior that treats 401 as a terminal failure.

Key contributing factors:

  • Azure AD session cookie expires while the user is still filling out the form.
  • Angular interceptor redirects immediately on 401, preventing retry or re-auth.
  • No local persistence layer (by design), so unsaved data exists only in memory.
  • Auto-save API calls fail because the backend cannot refresh the cookie without a round-trip to Azure AD.
  • Backend cannot save data because the user is no longer authenticated.
  • User loses all in-memory form state when redirected.

Why This Happens in Real Systems

This pattern is extremely common in enterprise BFF architectures:

  • Browser cookies expire silently; the user has no visual indication.
  • Azure AD does not refresh tokens proactively unless the user interacts with the auth endpoint.
  • Long-lived forms (30–60 minutes) exceed typical session lifetimes.
  • Security policies forbid localStorage, eliminating the usual fallback.
  • Frontend frameworks assume short-lived interactions, not long-running data entry.
  • 401 is treated as a hard failure, not a recoverable authentication event.

Real-World Impact

When this failure occurs, organizations typically see:

  • Loss of sensitive or regulated data
  • User frustration and repeated support tickets
  • Increased abandonment rates
  • Compliance risks if users re-enter sensitive data incorrectly
  • Backend logs filled with 401s, obscuring real issues
  • Perception of instability, even though the system is behaving as designed

Example or Code (if necessary and relevant)

Below is a minimal Angular interceptor pattern that does not redirect immediately but instead attempts a silent re-auth by calling a backend endpoint that triggers Azure AD re-challenge.

@Injectable()
export class AuthRetryInterceptor implements HttpInterceptor {
  private isRefreshing = false;

  intercept(req: HttpRequest, next: HttpHandler) {
    return next.handle(req).pipe(
      catchError(err => {
        if (err.status === 401 && !this.isRefreshing) {
          this.isRefreshing = true;
          return this.silentReauth().pipe(
            switchMap(() => next.handle(req)),
            finalize(() => this.isRefreshing = false)
          );
        }
        return throwError(() => err);
      })
    );
  }

  private silentReauth() {
    return this.http.get('/bff/refresh'); // backend triggers Azure AD challenge
  }
}

How Senior Engineers Fix It

Experienced engineers treat 401 as a recoverable authentication event, not a redirect trigger. They implement a multi-layered solution:

1. Backend: Provide a silent re-auth endpoint

  • A /bff/refresh endpoint that:
    • Forces Azure AD to re-issue the cookie if possible
    • Returns 200 if refreshed
    • Returns 401 only if the user must fully re-login

2. Frontend: Retry failed requests after silent re-auth

  • Interceptor attempts:
    • First: original request
    • On 401: call /bff/refresh
    • If refresh succeeds: retry original request
    • If refresh fails: show modal, not redirect

3. Frontend: Replace redirect with a modal

Instead of redirecting immediately:

  • Show a “Session expired — click to re-authenticate” modal
  • Pause auto-save
  • Keep form data in memory
  • After re-auth, resume auto-save

4. Backend: Auto-save endpoint accepts partial data

  • Save incremental progress
  • Avoid requiring full form submission
  • Ensure idempotency

5. Optional: Server-side draft storage

  • Store drafts keyed by user + form instance
  • Allow resuming after re-authentication

6. Optional: Sliding session renewal

  • Extend cookie lifetime on each backend call
  • Prevent expiration during active use

Why Juniors Miss It

Junior engineers often overlook this failure mode because:

  • They assume 401 means “user is logged out”, not “session silently expired.”
  • They rely on redirect-based authentication flows, which break long-lived forms.
  • They underestimate session lifetime constraints in enterprise identity systems.
  • They do not consider in-memory-only state as a risk.
  • They assume auto-save always works, without planning for auth failures.
  • They lack experience with BFF patterns and Azure AD cookie behavior.

Senior engineers recognize that authentication is stateful, sessions expire silently, and frontend state is fragile. They design systems that treat authentication failures as recoverable events, not catastrophic ones.

Leave a Comment