TenantAtlas/specs/063-entra-signin/plan.md
Ahmed Darrazi 4acb86d49d feat(063-entra-signin): Add technical plan for Entra Sign-in feature
This commit adds the initial technical plan for the 063-entra-signin feature. The plan outlines the high-level architecture, key components, database changes, test plan, and deployment considerations based on the clarified feature specification.

The plan addresses:
- Authentication flow via Laravel Socialite and Entra ID.
- User provisioning and upsert logic.
- Post-login routing based on tenant memberships, including a dedicated chooser page for multiple memberships.
- Handling of disabled user logins.
- Database schema details for Entra ID fields.
- Comprehensive test coverage using Pest (unit, feature, browser tests).
2026-01-26 23:54:33 +01:00

6.7 KiB
Raw Blame History

Technical Plan: 063 — Entra Sign-in (Tenant Panel) v1

Feature Branch: 063-entra-signin Created: 2026-01-26 Status: Draft (v1) Spec: specs/063-entra-signin/spec.md


1. Overview & Goal Alignment

  • Goal: Implement a secure, Entra ID-only sign-in flow for the Tenant Admin panel (/admin), safely onboarding users and routing them based on tenant memberships.
  • Key Constraints: No /system panel modification, no Graph calls during render/hydration, DB-only at render time for login/no-access pages, synchronous login flow.
  • Clarifications Incorporated:
    • Multi-tenant users redirect to a dedicated chooser page.
    • Disabled users are blocked from login, redirected to /admin/login with generic error.
    • entra_tenant_id and entra_object_id columns are VARCHAR(36) (or UUID for Postgres).

2. Architecture & High-Level Design

  • Authentication Flow:
    1. User navigates to /admin/login.
    2. Presented with "Sign in with Microsoft" button (no local login fields).
    3. Clicking button initiates OIDC flow via Laravel Socialite (Entra ID provider).
    4. Redirects to /auth/entra/redirect.
    5. Entra ID authenticates user and redirects back to /auth/entra/callback with OIDC claims.
    6. Callback handler:
      • Validates claims (tid, oid present).
      • Performs upsert on users table based on (entra_tenant_id, entra_object_id).
      • Checks user status (active/disabled). If disabled, blocks login.
      • Regenerates session.
      • Determines post-login route based on tenant memberships.
  • User Provisioning (Upsert):
    • Use User::updateOrCreate with ['entra_tenant_id' => $tid, 'entra_object_id' => $oid] as unique key.
    • Populate name, email from claims.
    • entra_tenant_id, entra_object_id columns as VARCHAR(36) (or UUID).
  • Post-Login Routing:
    • 0 Memberships: Redirect to /admin/no-access (Filament page).
    • 1 Membership: Redirect to that tenants dashboard.
    • N Memberships: Redirect to /admin/choose-tenant (dedicated Filament chooser page).
  • Session Separation: Leverage Laravel's guard system for clear separation between platform (system) and tenant (admin) panels.

3. Key Components & Implementation Details

  • Laravel Socialite:
    • Configure Entra ID provider in config/services.php.
    • Create SocialiteController (or similar) to handle /auth/entra/redirect and /auth/entra/callback.
  • User Model (app/Models/User.php):
    • Add entra_tenant_id and entra_object_id as fillable properties.
    • Implement logic for checking tenants() relationship.
  • Migrations:
    • Add entra_tenant_id (string('entra_tenant_id', 36)->nullable() or uuid('entra_tenant_id')) and entra_object_id (string('entra_object_id', 36)->nullable() or uuid('entra_object_id')) columns to users table.
    • Add unique index unique(['entra_tenant_id', 'entra_object_id']).
    • Note: Initial migration can add as nullable, then a follow-up migration can make non-nullable if all existing users are migrated or it's a new system. Given it's a new system for admin sign-in, it should probably be non-nullable from the start.
  • Filament Panel Customization:
    • Override default Filament login page for /admin panel to remove email/password fields and add "Sign in with Microsoft" button.
    • Create NoAccessPage (/admin/no-access) and TenantChooserPage (/admin/choose-tenant) as Filament pages.
  • Error Handling & Logging:
    • Implement robust OIDC failure handling as per FR-004.
    • Utilize Laravel's logging facilities for privacy-safe audit logs (FR-005).
    • Define stable reason_code examples (e.g., oidc_missing_claims, user_disabled).
  • Service Layer (Optional but Recommended):
    • Consider a EntraLoginService to encapsulate OIDC callback logic, user upsert, and routing decisions. This keeps controllers lean and business logic testable.

4. Database Schema Changes

  • users table:
    • entra_tenant_id string('entra_tenant_id', 36)->nullable() (or uuid('entra_tenant_id'))
    • entra_object_id string('entra_object_id', 36)->nullable() (or uuid('entra_object_id'))
    • Add unique(['entra_tenant_id', 'entra_object_id']) index.
    • Note: Initial migration can add as nullable, then a follow-up migration can make non-nullable if all existing users are migrated or it's a new system. Given it's a new system for admin sign-in, it should probably be non-nullable from the start.

5. Test Plan (Building on Spec Acceptance Tests)

  • Unit Tests (Pest):
    • Socialite callback handler logic (claim validation, upsert logic, disabled user check).
    • User model methods related to Entra ID and tenant memberships.
    • Routing service/resolver.
  • Feature Tests (Pest):
    • AdminLoginIsEntraOnlyTest (GET /admin/login renders correctly, no password inputs).
    • EntraCallbackUpsertByTidOidTest (callback upserts, session regenerated).
    • PostLoginRoutingByMembershipTest (0, 1, N memberships routing).
    • OidcFailureRedirectsSafelyTest (missing claims, generic error, logs).
    • SessionSeparationSmokeTest (guard separation works).
    • DisabledUserLoginIsBlockedTest (disabled user login attempt blocked and logged).
  • Browser Tests (Pest v4):
    • End-to-end flow for successful Entra login.
    • Verify the /admin/choose-tenant page renders correctly and allows selection.

6. Deployment Considerations

  • Environment Variables:
    • ENTRA_CLIENT_ID, ENTRA_CLIENT_SECRET, ENTRA_REDIRECT_URI for Socialite.
    • These must be managed securely (Dokploy environment variables).
  • Migrations: Ensure database migrations are run (sail artisan migrate).
  • Filament Assets: php artisan filament:assets must be run on deployment.
  • Dokploy: Ensure Dokploy config includes any new routes/pages.

7. Open Questions / Potential Risks

  • Entra ID Setup: Assumed Entra ID application registration and configuration are handled externally.
  • last_tenant_id: Decision on whether to implement last_tenant_id for multi-membership users is deferred. It's an optimization.
  • User Provisioning: What if required claims like email or name are missing from Entra ID? Current spec implies they should be present, but fallback behavior is not explicitly defined for name (email is nullable).
  • Authorization: This plan focuses on authentication. Tenant-specific authorization (what actions a user can perform within a tenant) is out of scope for this feature (covered by 062-tenant-rbac-v1).