ADR-002: Authentication with Auth.js and Entra ID
Status
Accepted
Context
The application requires:
- Enterprise SSO: Integration with Microsoft Entra ID (Azure AD)
- Secure token handling: Access tokens must not be exposed to browsers
- Session management: Persistent sessions with automatic refresh
- Anonymous support: Some features available without authentication
Options considered:
- Custom OAuth implementation: Full control but high maintenance
- Auth0/Okta: Third-party dependency and cost
- Auth.js (NextAuth): Open source, Next.js native, well-maintained
Decision
We will use Auth.js (NextAuth v5) with Microsoft Entra ID provider:
- OAuth flow: Authorization Code with PKCE
- Session strategy: JWT stored in HTTP-only cookies
- Token storage: Encrypted in session, server-side only
- Anonymous fallback: Client Credentials Flow for unauthenticated requests
// src/auth.ts
export const { auth, handlers } = NextAuth({
providers: [EntraID({
clientId: process.env.ENTRA_CLIENT_ID,
clientSecret: process.env.ENTRA_CLIENT_SECRET,
tenantId: process.env.ENTRA_TENANT_ID,
})],
session: { strategy: 'jwt' },
callbacks: {
jwt: async ({ token, account }) => {
if (account?.access_token) {
token.accessToken = account.access_token;
}
return token;
},
},
});
Anonymous User Support
For pre-authentication features, we use Client Credentials Flow:
- App authenticates with Entra using its own credentials
- Receives an access token without user context
- Backend identifies these as "anonymous" requests
- Limited to read-only, public data operations
Consequences
Positive
- Security: Tokens never exposed to client JavaScript
- Enterprise ready: Native Entra ID support
- Automatic refresh: Auth.js handles token lifecycle
- Middleware integration: Easy route protection
Negative
- Auth.js versioning: Breaking changes between major versions
- Debugging OAuth: Complex flow to troubleshoot
- Entra configuration: Requires Azure portal setup
Neutral
- Session size: JWT includes encrypted tokens
- Callback URLs: Must be registered in Entra
Related ADRs
- ADR-004: API Client - Token injection in API calls