docs: F3+F4 — rename onboarding hook to onFirstSignIn(AuthenticatedUser), fix UT-06 package
F3: rename SparkboardOnboardingHook.afterFirstLogin(OnboardingContext)
→ onFirstSignIn(AuthenticatedUser) across Architecture, Integration-Guide,
Sprint-1-Plan-Part-2, Sprint-1-Testplan, Home.
plate-auth's spelling wins; aligns with plate-auth Architecture +
Sprint-0-Plan SPI signature.
F4: Sprint-1-Testplan UT-06 test class moved from
de.plate.sparkboard.auth.SparkboardOnboardingHookTest →
de.plate.sparkboard.onboarding.SparkboardOnboardingHookTest.
Matches the package declared in Sprint-1-Plan-Part-2 +
Integration-Guide §2.
Home.md hook link text also updated to onFirstSignIn().
Plan-Review.md intentionally left untouched (historical record).
+5
-5
@@ -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
|
||||
|
||||
+5
-5
@@ -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.
|
||||
|
||||
+6
-6
@@ -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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user