TenantAtlas/specs/063-entra-signin/plan.md
ahmido c5fbcaa692 063-entra-signin (#76)
Key changes

Adds Entra OIDC redirect + callback endpoints under /auth/entra/* (token exchange only there).
Upserts tenant users keyed by (entra_tenant_id = tid, entra_object_id = oid); regenerates session; never stores tokens.
Blocks disabled / soft-deleted users with a generic error and safe logging.
Membership-based post-login routing:
0 memberships → /admin/no-access
1 membership → tenant dashboard (via Filament URL helpers)
>1 memberships → /admin/choose-tenant
Adds Filament pages:
/admin/choose-tenant (tenant selection + redirect)
/admin/no-access (tenantless-safe)
Both use simple layout to avoid tenant-required UI.
Guards / tests

Adds DbOnlyPagesDoNotMakeHttpRequestsTest to enforce DB-only render/hydration for:
/admin/login, /admin/no-access, /admin/choose-tenant
with Http::preventStrayRequests()
Adds session separation smoke coverage to ensure tenant session doesn’t access system and vice versa.
Runs: vendor/bin/sail artisan test --compact tests/Feature/Auth

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #76
2026-01-27 16:38:53 +00:00

96 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Implementation Plan: 063 — Entra Sign-in (Tenant Panel) v1
**Branch**: `063-entra-signin` | **Date**: 2026-01-27 | **Spec**: [specs/063-entra-signin/spec.md](spec.md)
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/063-entra-signin/spec.md`
## Summary
This feature will implement Microsoft Entra ID OIDC-based sign-in for the Tenant Admin panel. The implementation will override the default Filament login page to present an Entra-only sign-in option. It will utilize Laravel Socialite to manage the OAuth2 flow, handling redirection to Microsoft Entra ID and processing the callback. Core logic involves upserting users based on `(entra_tenant_id, entra_object_id)`, blocking disabled users, regenerating sessions, and dynamically routing users post-login to their tenant dashboard, a dedicated tenant chooser page (for multiple memberships), or a "no access" page (for zero memberships). The implementation adheres to security best practices by not storing sensitive tokens and ensures all affected pages remain DB-only at render time.
Routing stability rule: redirects into a tenant MUST use Filament page URL helpers (e.g., `App\\Filament\\Pages\\TenantDashboard::getUrl(tenant: $tenant)`) rather than hardcoding `/admin/t/...`, so future route prefix / tenant slug changes dont break auth flows.
## Technical Context
**Language/Version**: PHP 8.4
**Primary Dependencies**: `laravel/framework:^12`, `livewire/livewire:^4`, `filament/filament:^5`, `laravel/socialite:^5.0`
**Storage**: PostgreSQL
**Testing**: Pest
**Target Platform**: Web
**Project Type**: Web application
**Performance Goals**: Callback returns within ~2s under normal conditions.
**Constraints**: Do not persist secrets/tokens. Sanitize all error output and logs. Outbound HTTP is permitted only inside /auth/entra/* endpoints.
**Scale/Scope**: Tenant Admin panel (`/admin`) sign-in only.
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- **Inventory-first**: Not directly applicable to auth.
- **Read/write separation**: N/A for login, but user upsert is a write. The spec requires it to be idempotent, which aligns.
- **Graph contract path**: The spec explicitly forbids Graph calls during render/poll/hydration, and limits OIDC calls to the `/auth/entra/*` routes. This is compliant.
- **Deterministic capabilities**: N/A.
- **Tenant isolation**: Compliant. The entire flow is built around tenant context (`tid`).
- **Run observability**: Compliant. The spec references `OPS-EX-AUTH-001`, the Auth Handshake Exception, which exempts this synchronous login flow from requiring an `OperationRun`. Logging requirements are specified.
- **Automation**: N/A.
- **Data minimization**: Compliant. Spec says "MUST NOT store Entra access/refresh tokens" and requires safe logging.
- **Badge semantics (BADGE-001)**: N/A.
**Result**: The plan is compliant with the constitution.
## Project Structure
### Documentation (this feature)
```text
specs/063-entra-signin/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── entra-auth-flow.md
└── tasks.md
```
### Source Code (repository root)
```text
# Web application
app/
├── Http/
│ ├── Controllers/
│ │ └── Auth/
│ │ └── EntraController.php # New: Handles OIDC redirect and callback logic
│ └── Middleware/
├── Filament/
│ ├── Pages/
│ │ ├── Auth/
│ │ │ └── Login.php # New: Custom Filament login page with Entra-only CTA
│ │ ├── NoAccess.php # New: Page for users with no tenant memberships
│ │ └── ChooseTenant.php # New: Page for users with multiple tenant memberships
│ └── Tenant/
│ └── Resources/
├── Models/
│ └── User.php # Modified: User model for Entra IDs and tenant relationships
├── Providers/
│ └── Filament/
│ └── AdminPanelProvider.php # Modified: Register custom login page
config/
├── services.php # Modified: Add Microsoft Socialite provider config
routes/
│ └── web.php # Modified: Register OIDC redirect and callback routes
tests/
└── Feature/
└── Auth/
├── AdminLoginIsEntraOnlyTest.php
├── EntraCallbackUpsertByTidOidTest.php
├── PostLoginRoutingByMembershipTest.php
├── OidcFailureRedirectsSafelyTest.php
├── SessionSeparationSmokeTest.php
└── DisabledUserLoginIsBlockedTest.php
```
**Structure Decision**: The project is a standard Laravel web application. The changes will be implemented within the existing structure, primarily affecting `app/`, `routes/`, `config/` and `tests/`.
## Complexity Tracking
No violations.