M1: Identity & access — members, RBAC, JWT auth, invitations

Adds the access foundation everything else enforces against.

SharedKernel (shared access contracts, no Identity dependency for consumers):
- ScopeRef/ScopeType, RoleType, Capability, AccessPolicy (role x capability matrix),
  ICurrentUser, IPermissionService (scope-chain evaluation).

Identity module:
- Member, Membership, Invitation entities; internal IdentityDbContext (schema
  "identity") + InitialIdentity migration; design-time factory.
- JWT auth (HS256) issuing membership claims; PasswordHasher<Member>; CurrentUser
  (claims -> ICurrentUser) and PermissionService implementations.
- Public IMemberDirectory contract for other modules to resolve member display info.
- Endpoints: POST /bootstrap (first owner), /auth/login, GET /me, POST /invitations,
  POST /invitations/accept. Owner-only actions enforced via IPermissionService.
- Web host wires UseAuthentication/UseAuthorization and string-enum JSON.

Verified: build green; ArchitectureTests 8/8 (Identity references only SharedKernel);
IntegrationTests 11/11 incl. a new end-to-end flow — bootstrap -> login -> /me ->
invite -> accept -> login as invitee, and a Member is 403'd from inviting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-09 07:59:57 +03:30
parent 265861b89b
commit 61991bf6cd
29 changed files with 1333 additions and 14 deletions
+8
View File
@@ -1,3 +1,4 @@
using System.Text.Json.Serialization;
using OpenTelemetry.Trace;
using Serilog;
using TeamUp.Bootstrap;
@@ -12,6 +13,10 @@ builder.Host.UseSerilog((context, services, configuration) => configuration
builder.Services.AddOpenApi();
// Bind/serialize enums as strings across the API (e.g. ScopeType "Organization", RoleType "Member").
builder.Services.ConfigureHttpJsonOptions(options =>
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()));
builder.Services.AddTeamUpObservability(
builder.Configuration,
serviceName: "teamup-web",
@@ -42,6 +47,9 @@ app.UseSerilogRequestLogging();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.MapHealthChecks("/health");
app.MapTeamUpModules();