diff --git a/Architecture.md b/Architecture.md index 9a1ce8e..49b2386 100644 --- a/Architecture.md +++ b/Architecture.md @@ -123,7 +123,7 @@ This is the **one SPI bean** Sparkboard implements. It's how a newly-signed-in G package de.plate.sparkboard.onboarding; import de.platesoft.auth.spi.OnboardingHook; -import de.platesoft.auth.spi.OnboardingContext; +import de.platesoft.auth.model.AuthenticatedUser; import de.platesoft.auth.membership.MembershipService; import de.platesoft.auth.membership.Role; import org.springframework.stereotype.Component; @@ -147,9 +147,9 @@ public class SparkboardOnboardingHook implements OnboardingHook { } @Override - public void afterFirstLogin(OnboardingContext ctx) { - Role role = admins.isAdminEmail(ctx.email()) ? Role.ADMIN : Role.MEMBER; - memberships.upsert(ctx.userId(), ORG_TYPE, FAMILY_SPARK_ID, role); + public void onFirstSignIn(AuthenticatedUser user) { + Role role = admins.isAdminEmail(user.email()) ? Role.ADMIN : Role.MEMBER; + memberships.upsert(user.id(), ORG_TYPE, FAMILY_SPARK_ID, role); } } ``` @@ -304,7 +304,7 @@ sequenceDiagram BE->>BE: plate-auth checks allowlist BE->>DB: SELECT / INSERT auth_identities Note over BE,DB: First login? → fire OnboardingHook - BE->>BE: SparkboardOnboardingHook.afterFirstLogin + BE->>BE: SparkboardOnboardingHook.onFirstSignIn BE->>DB: INSERT memberships (user_id, 'SPARK_ORG', FAMILY_SPARK_ID, role) BE-->>FE: { userId, accessToken (15m JWT), refreshToken } FE->>FE: NextAuth session populated diff --git a/Integration-Guide.md b/Integration-Guide.md index 55267cb..709e85b 100644 --- a/Integration-Guide.md +++ b/Integration-Guide.md @@ -40,8 +40,8 @@ The other four: Sparkboard's [`SparkboardOnboardingHook`](Architecture.md#5-single-org-mode--the-onboardinghook-spi) is the **entire** Java auth integration. The full impl sketch: ```java -// backend/src/main/java/de/plate/sparkboard/auth/SparkboardOnboardingHook.java -package de.plate.sparkboard.auth; +// backend/src/main/java/de/plate/sparkboard/onboarding/SparkboardOnboardingHook.java +package de.plate.sparkboard.onboarding; import de.platesoft.auth.spi.OnboardingHook; import de.platesoft.auth.model.AuthenticatedUser; @@ -66,7 +66,7 @@ public class SparkboardOnboardingHook implements OnboardingHook { } @Override - public void onFirstLogin(AuthenticatedUser user) { + public void onFirstSignIn(AuthenticatedUser user) { var role = adminProps.admins().contains(user.email()) ? "ADMIN" : "MEMBER"; memberships.upsert(user.id(), SPARK_ORG_TYPE, FAMILY_SPARK_ID, role); } @@ -198,7 +198,7 @@ Sparkboard writes zero lines of Spring Security config. plate-auth auto-config w | `SecurityFilterChain apiChain` | Requires authentication for `/api/**`. JWT bearer via Resource Server. | | `JwtDecoder` | Decodes Sparkboard JWTs using `plate.auth.jwt.secret`. | | `@CurrentUser` argument resolver | Injects an `AuthenticatedUser` into controllers. | -| Onboarding hook orchestrator | Calls `SparkboardOnboardingHook.onFirstLogin(...)` on first plate-auth login per user. | +| Onboarding hook orchestrator | Calls `SparkboardOnboardingHook.onFirstSignIn(...)` on first plate-auth sign-in per user. | | Flyway migrations | Loads from `classpath:db/migration-auth/` into `flyway_schema_history_auth`. | Sparkboard's controllers therefore look like this — no auth ceremony at all: @@ -379,7 +379,7 @@ Both tables sit in the same Postgres database. Both are managed by Flyway. They 5. NextAuth verifies the Google token, looks up the email `patrick@plate-software.de`. 6. The signIn callback (configured by `createAuthConfig`) hashes the email with `PLATE_AUTH_EXCHANGE_SECRET` and POSTs an envelope to `POST /api/auth/exchange` on the backend. 7. Backend's plate-auth verifies the HMAC, checks the allowlist (`patrick@plate-software.de` is in it), looks up or inserts an `auth_identities` row. -8. **If this is the first login**, plate-auth's onboarding orchestrator calls `SparkboardOnboardingHook.onFirstLogin(user)`. +8. **If this is the first sign-in**, plate-auth's onboarding orchestrator calls `SparkboardOnboardingHook.onFirstSignIn(user)`. 9. Hook checks `sparkboard.admins[]` → `patrick@plate-software.de` is there → calls `MembershipService.upsert(userId, "SPARK_ORG", FAMILY_SPARK_ID, "ADMIN")`. 10. plate-auth issues a JWT, signs it with `plate.auth.jwt.secret`, returns it in the exchange response. 11. NextAuth stores it in the session cookie. diff --git a/Sprint-1-Plan-Part-2.md b/Sprint-1-Plan-Part-2.md index be8efeb..099209e 100644 --- a/Sprint-1-Plan-Part-2.md +++ b/Sprint-1-Plan-Part-2.md @@ -223,7 +223,7 @@ Activated in `SparkboardApplication` with `@ConfigurationPropertiesScan` or `@En package de.plate.sparkboard.onboarding; import de.platesoft.auth.spi.OnboardingHook; -import de.platesoft.auth.spi.OnboardingContext; +import de.platesoft.auth.model.AuthenticatedUser; import de.platesoft.auth.membership.MembershipService; import de.platesoft.auth.membership.Role; import org.springframework.stereotype.Component; @@ -232,10 +232,10 @@ import java.util.UUID; /** * The one and only Sparkboard customisation of plate-auth's SPI. - * Runs after a successful first login, before NextAuth's signIn + * Runs after a successful first sign-in, before NextAuth's signIn * callback returns to the user. * - * Idempotent: runs every login (plate-auth promises first-login-only + * Idempotent: runs every login (plate-auth promises first-sign-in-only * but we do not depend on that promise being bug-free). */ @Component @@ -255,9 +255,9 @@ public class SparkboardOnboardingHook implements OnboardingHook { } @Override - public void afterFirstLogin(OnboardingContext ctx) { - Role role = admins.isAdminEmail(ctx.email()) ? Role.ADMIN : Role.MEMBER; - memberships.upsert(ctx.userId(), ORG_TYPE, FAMILY_SPARK_ID, role); + public void onFirstSignIn(AuthenticatedUser user) { + Role role = admins.isAdminEmail(user.email()) ? Role.ADMIN : Role.MEMBER; + memberships.upsert(user.id(), ORG_TYPE, FAMILY_SPARK_ID, role); } } ``` diff --git a/Sprint-1-Testplan.md b/Sprint-1-Testplan.md index 12f1c78..f306d8c 100644 --- a/Sprint-1-Testplan.md +++ b/Sprint-1-Testplan.md @@ -98,9 +98,9 @@ Tests **not** in scope: plate-auth's own internals (those are covered by the pla ### UT-06 — SparkboardOnboardingHook ADMIN path -- **Class:** `de.plate.sparkboard.auth.SparkboardOnboardingHookTest` +- **Class:** `de.plate.sparkboard.onboarding.SparkboardOnboardingHookTest` - **Given:** `sparkboard.admins[] = ["patrick@plate-software.de"]`, login event for `patrick@plate-software.de`. -- **When:** `hook.onFirstLogin(authenticatedUser)` +- **When:** `hook.onFirstSignIn(authenticatedUser)` - **Then:** `MembershipService.upsert(userId, "SPARK_ORG", FAMILY_SPARK_ID, "ADMIN")` called once. ### UT-07 — SparkboardOnboardingHook MEMBER path @@ -110,7 +110,7 @@ Tests **not** in scope: plate-auth's own internals (those are covered by the pla ### UT-08 — Hook idempotency -- **When:** `hook.onFirstLogin(user)` called twice with same user. +- **When:** `hook.onFirstSignIn(user)` called twice with same user. - **Then:** `MembershipService.upsert` called twice with identical arguments; no exception. (`upsert` semantics, not duplicate insert.) ### UT-09 — SparkboardAdminProperties binding