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
2.6 KiB
2.6 KiB
Research: 063 — Entra Sign-in
1. Required OAuth Scopes for Entra ID Sign-in
Decision: Use the standard OpenID Connect scopes: openid, email, profile.
Rationale:
openid: Required for OIDC compliance, returns thesub(subject) claim which can be used for the user identifier.email: Requests theemailclaim.profile: Requests claims likename,family_name,given_name. Thetidandoidclaims are standard in Microsoft's implementation and do not require special scopes.
Alternatives considered:
- Requesting more specific scopes (e.g.,
User.Readfrom Microsoft Graph). This is not necessary for basic sign-in and would require the Graph API, which is out of scope for the login flow itself.
2. User Provisioning Strategy
Decision: Just-In-Time (JIT) provisioning using updateOrCreate.
Rationale:
- The
spec.mdrequires upserting the user based on(entra_tenant_id, entra_object_id). - Laravel's
updateOrCreateis the idiomatic way to handle this. It will find a user with the matchingentra_tenant_idandentra_object_idor create a new one if none exists. - The
Usermodel will be updated with information from the Entra ID claims (name, email).
Alternatives considered:
- Pre-provisioning users. This would require an admin to manually create users before they can sign in, which is not a good user experience.
3. Interaction with Tenant RBAC
Decision: The sign-in flow will only read tenant memberships after the user is authenticated.
Rationale:
- The
spec.mdis clear: "063 MUST NOT refactor tenant RBAC data model or enforcement. It may only read memberships to decide where to redirect after login." - After the user is upserted and logged in, a service class will be used to check their tenant memberships (e.g.,
Auth::user()->tenants()->count()). - The result of this check (0, 1, or N) will determine the redirect path as per the spec.
Alternatives considered:
- Modifying RBAC during login. This is explicitly out of scope.
4. User Experience for Unmapped/New Users
Decision: The user experience is defined by the post-login routing based on tenant memberships.
Rationale:
- If a new user signs in via Entra, they will be created in the
userstable. - At this point, they will have 0 tenant memberships.
- According to the spec (
FR-003), they will be redirected to/admin/no-access. - This page will instruct them to "Ask an admin to add you." This is the desired flow.
Alternatives considered:
- Displaying an error on the login page. This is less user-friendly than guiding them to a page that explains the next steps.