feat(api): Implement authentication - #1385
Christopher MANEU (cmaneu) wants to merge 11 commits into
Conversation
Test Results (Node.js 22)test: Run #36
🎉 All tests passed! |
Test Results (Node.js 22)test: Run #39
🎉 All tests passed! |
|
Successfully tested locally |
There was a problem hiding this comment.
🟡 Changes recommended
Token validation, Cosmos uniqueness, JWKS failure handling, and local Portal integration contain blocking correctness and reliability issues.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds identity-only Microsoft Entra authentication to the API.
Changes:
- Adds JWT verification, authentication middleware, JIT user provisioning, and
/users/me. - Adds the users collection migration and authentication configuration.
- Adds tests, OpenAPI updates, documentation, and the
josedependency.
File summaries
| File | Description |
|---|---|
packages/shared/src/index.ts |
Exports authentication APIs. |
packages/shared/src/auth/user-schema.ts |
Defines persisted users. |
packages/shared/src/auth/user-schema.test.ts |
Tests user validation. |
packages/shared/src/auth/types.ts |
Defines authentication interfaces and errors. |
packages/shared/src/auth/index.ts |
Exports auth modules. |
packages/shared/src/auth/entra.ts |
Implements Entra token verification. |
packages/shared/src/auth/entra.test.ts |
Tests token verification. |
packages/shared/src/auth/config.ts |
Loads authentication configuration. |
packages/shared/src/auth/config.test.ts |
Tests configuration loading. |
packages/shared/src/auth/claims-enricher.ts |
Maps claims to profiles. |
packages/shared/src/auth/claims-enricher.test.ts |
Tests claim enrichment. |
packages/shared/package.json |
Adds jose. |
packages/db-migrations/src/required-migrations.ts |
Registers migration 029. |
packages/db-migrations/src/migrations/029-create-users-collection.ts |
Creates user indexes. |
packages/db-migrations/src/check-migrations.test.ts |
Updates migration expectations. |
NOTICE |
Adds the jose license. |
ENV_VARIABLES.md |
Documents API authentication settings. |
docs/architecture/auth-rbac.md |
Updates client configuration names. |
apps/api/src/test-helpers.ts |
Adds mock users storage. |
apps/api/src/routes/users.ts |
Adds the current-user endpoint. |
apps/api/src/routes/users.test.ts |
Tests the current-user endpoint. |
apps/api/src/route-context.ts |
Adds authentication dependencies. |
apps/api/src/index.ts |
Initializes and registers authentication. |
apps/api/src/auth/user-store.ts |
Implements JIT user persistence. |
apps/api/src/auth/user-store.test.ts |
Tests user persistence. |
apps/api/src/auth/types.ts |
Defines request principals. |
apps/api/src/auth/middleware.ts |
Authenticates bearer tokens globally. |
apps/api/src/auth/middleware.test.ts |
Tests authentication middleware. |
apps/api/src/__snapshots__/openapi-snapshot.test.ts.snap |
Records the new endpoint. |
.env.local.example |
Documents local emulator configuration. |
.env.example |
Adds general authentication examples. |
Review details
- Files reviewed: 31/33 changed files
- Comments generated: 11
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Wassim Chegham (manekinekko)
left a comment
There was a problem hiding this comment.
Follow-up review of the updated authentication implementation. Five findings and two non-blocking notes are attached inline.
There was a problem hiding this comment.
Thanks for adding this! I’m aligned with Wassim Chegham (@manekinekko)’s comments on the TypeScript mismatch and TLS configuration. One additional deployment detail for migration 029:
Our Cosmos accounts already use continuous backup—the shared dev infrastructure configures Continuous7Days, and the operations guide records Continuous30Days for integration and production.
With that configuration, Cosmos requires unique indexes to be supplied when creating the collection through its CreateCollection extension command. The current implicit collection creation through createIndex() therefore needs a Cosmos-compatible path alongside the native MongoDB implementation.
Earlier migrations work around related restrictions by falling back to non-unique indexes and application-level duplicate handling. Here, keeping the database-enforced identity constraint would help ensure concurrent logins always resolve to the same user.
The existing shared dev infrastructure guide provides a convenient way to exercise this on real CosmosDB, with an isolated database per worktree. That would let us confirm both that migration 029 completes and that the resulting index rejects duplicate identity triples.
Cedric Vidal (cedricvidal)
left a comment
There was a problem hiding this comment.
Some of the issues are need to be fixed
Wassim Chegham (manekinekko)
left a comment
There was a problem hiding this comment.
Focused follow-up on per-request user persistence: reliability, profile freshness, and login timestamp semantics.
|
The environment-specific auth configuration is being split into repository-owned work:
For integration, The API in this PR already reads runtime process environment variables. The remaining Portal work in this PR is to read its client ID, authority, and scopes from runtime |
- Reject empty and malformed Authorization headers while preserving the existing anonymous rollout. - Correct authentication deployment and local development documentation. - Replace local TLS verification bypasses with trusted development CA certificates and restore API queue declarations needed for dev builds. - Pass Portal authentication settings through Docker build arguments, Compose, and CI while preserving Vite development configuration. - Bootstrap admins using verified identity and tenant allowlists without requiring email verification; preserve the email-storage policy. - Declare HTTP bearer security only for GET /api/v1/users/me and update the generated OpenAPI specification and snapshot. - Add regression coverage for the corrected behavior. Refs: #1385 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
b6a87b1 to
0637b03
Compare
Test Results (Node.js 22)test: Run #67
🎉 All tests passed! |
…ex handling for users collection
Test Results (Node.js 22)test: Run #68
🎉 All tests passed! |
|
Christopher MANEU (@cmaneu), I agree with Josh's suggestion. Could you have the Portal read its client ID, authority, and scopes from runtime That would let us build the image once and promote it from integration to production, with each deployment supplying its own auth settings. Thanks! |
Cedric Vidal (cedricvidal)
left a comment
There was a problem hiding this comment.
Thanks for the rework. The overall direction looks sound: MSAL with PKCE, token verification before looking up application access, and a Redis cache with fixed expiration and MongoDB fallback. I don't think the Redis approach needs a redesign.
A few design points I'd like us to address or explicitly agree on:
-
Use POST for explicit enrollment. Could we keep
GET /users/meread-only and move enrollment to a POST endpoint? Thelogin=truepath intentionally creates/updates users and can apply bootstrap-admin promotion.Cache-Control: no-storeis helpful, but it doesn't change the safe-method semantics of GET. POST would avoid relying on every client to remember not to prefetch or automatically replay this request. -
Complete the Entra signing-key issuer check. The token issuer is already checked, but the generic JWKS resolver doesn't enforce the selected key's
issuerrestriction. Could we add that check for tenant-independent metadata, following Microsoft's validation guidance? This is separate from validating the token'sissclaim; I'm not claiming a demonstrated bypass. -
Confirm the access-revocation window. The five-minute cache lifetime and possible stale role/disabled status are clearly documented. Could we confirm that this delay is acceptable for the intended deployment? Future role/disable mutation endpoints should invalidate the matching cache entry, as the design already calls out.
I understand that full RBAC and ownership enforcement are outside this PR. Keeping that distinction explicit is important: the Portal login gate is not yet a deny-by-default API authorization boundary.
Test Results (Node.js 22)test: Run #69
🎉 All tests passed! |
Cedric Vidal (@cedricvidal) I do agree with the objective (build image once an promote it everywhere), and it's quite essential with #1407. However, I'm in favor of having this change as a separate PR to have time to discuss with Wassim Chegham (@manekinekko) about all the other values, and validate how we release scope publicly. I have created #1408 to track this. |
Here is it, adding the authentication to the API (Authorization is out of scope of this PR).
Attention points
No token / bad token discrepancy
There is a discrepancy in the answers:
Authorization: Bearer, you have an "error" JSONHTTP 401error.This was not intentional. Moreover, it seems we're not aligning on something like RFC 9457.