diff --git a/Sprint-0-Plan.md b/Sprint-0-Plan.md
index 9904de4..9e7c821 100644
--- a/Sprint-0-Plan.md
+++ b/Sprint-0-Plan.md
@@ -159,3 +159,239 @@ See § 8 for step-by-step.
---
*Plan continues in the next section — backend extraction step-by-step.*
+
+---
+
+## 4. Backend extraction — step-by-step
+
+### 4.1 W1 — Repo scaffolding
+
+**Goal:** Create the directory layout + skeleton `pom.xml` files + npm workspace structure.
+
+**Steps:**
+
+1. **W1-1** In the `plate-auth` repo root, create:
+ ```
+ plate-auth/
+ ├── pom.xml # parent POM, packaging=pom
+ ├── plate-auth-starter/
+ │ ├── pom.xml # the published artifact
+ │ └── src/{main,test}/{java,resources}/
+ ├── packages/
+ │ └── auth/
+ │ ├── package.json # @platesoft/auth
+ │ └── src/
+ ├── package.json # workspace root
+ ├── pnpm-workspace.yaml # if pnpm; npm/yarn equivalent works
+ ├── .gitea/workflows/ # Gitea Actions
+ ├── CHANGELOG.md
+ └── README.md
+ ```
+2. **W1-2** Parent `pom.xml`: `de.platesoft`, `plate-auth-parent`,
+ `${revision}` (CI-injected), `pom`,
+ `plate-auth-starter`. Inherit from `spring-boot-starter-parent:4.1.0`.
+3. **W1-3** `plate-auth-starter/pom.xml`: `plate-auth-starter`,
+ deps copied from InspectFlow `backend/pom.xml` minus app-specific (no ONNX, no openpdf, no pdfbox):
+ ```
+ web, data-jpa, security, validation, actuator, mail,
+ hibernate-envers, postgresql, flyway,
+ jjwt-api/impl/jackson,
+ mapstruct, lombok, logstash-logback,
+ h2 (test scope), testcontainers (test scope)
+ ```
+4. **W1-4** Root `package.json` + `pnpm-workspace.yaml` configured to pick up `packages/*`.
+5. **W1-5** `packages/auth/package.json`: `"name": "@platesoft/auth"`, `"version": "0.1.0"`,
+ peerDeps on `next@>=15`, `next-auth@^5.0.0-beta`, `react@>=19`.
+6. **W1-6** Add `.gitignore`, `.editorconfig`, basic `README.md` linking to the wiki.
+7. **W1-7** Commit + push to `main`. CI must pass on empty modules (just compiles).
+
+**Done when:** `mvn -B verify` and `pnpm -r build` both succeed on a fresh clone.
+
+### 4.2 W2-A — Backend: copy + rename classes
+
+**Goal:** Mechanical move from `de.platesoft.inspectflow.*` → `de.platesoft.auth.*` for every auth class.
+
+**Steps (per source class — repeat for the inventory in Sprint-0-Assessment § 1.1):**
+
+1. **W2-1** Copy class file from `inspectflow/backend/src/main/java/de/platesoft/inspectflow//.java`
+ to `plate-auth/plate-auth-starter/src/main/java/de/platesoft/auth//.java`.
+2. **W2-2** Update `package` declaration: `package de.platesoft.auth.;`.
+3. **W2-3** Update **internal imports** (i.e. imports of other classes we are also moving) to the new
+ package. Use search-replace, but verify each file compiles independently after.
+4. **W2-4** **Leave behind**: any reference to `Company`, `OnboardingService`, `TenantAutoMapService` —
+ replace with an SPI call (see W4 below). DO NOT move these classes.
+
+**Class-by-class checklist:**
+
+- [ ] `filter/JwtAuthenticationFilter.java` — direct move, no rewrites
+- [ ] `filter/OrgContextResolver.java` — direct move
+- [ ] `config/SecurityConfig.java` — direct move, **but**: replace InspectFlow-specific permit-list
+ (`/api/companies/*/public-info`) with config-driven list from `PlateAuthProperties.cors.additionalPermitPaths`
+- [ ] `service/JwtService.java` — direct move; rename `@Value("${jwt.secret}")` → `@Value("${plate.auth.jwt.secret}")`
+- [ ] `service/OAuthService.java` — move + **swap** `tenantAutoMapService.maybeAutoMap(...)` call for
+ `onboardingHook.onFirstSignIn(...)` (SPI)
+- [ ] `service/ExchangeService.java` — direct move
+- [ ] `service/MembershipService.java` — move + **swap** `companyRepository.findById(...)` for
+ `orgValidator.exists(orgType, orgId)` / `orgDisplayNameResolver.displayName(...)` calls
+- [ ] `service/InvitationService.java` — direct move (Sprint 14.3 already abstracted the mailer)
+- [ ] `service/AccessRequestService.java` — direct move
+- [ ] `service/LoginEventService.java` — direct move
+- [ ] `service/AuthService.java` — direct move (password login/register)
+- [ ] `controller/AuthController.java` — direct move
+- [ ] `controller/OAuthController.java` — direct move
+- [ ] `controller/InvitationController.java` — direct move
+- [ ] `controller/AccessRequestController.java` — direct move
+- [ ] `controller/AdminAuditController.java` — direct move
+- [ ] `entity/User.java` — direct move
+- [ ] `entity/UserIdentity.java` — direct move
+- [ ] `entity/Membership.java` — direct move
+- [ ] `entity/Invitation.java` — direct move
+- [ ] `entity/AccessRequest.java` — direct move
+- [ ] `entity/LoginEvent.java` — direct move
+- [ ] `entity/RevInfo.java` (Envers revinfo with actor) — direct move
+- [ ] `repository/*` — all auth-related repositories — direct move
+- [ ] `dto/request/*` and `dto/response/*` — direct move
+- [ ] `enums/*` — direct move (Role, OrgType, MembershipRole, MembershipStatus, InvitationStatus,
+ AccessRequestStatus, LoginProvider)
+
+**Done when:** `plate-auth-starter` compiles in isolation. No references to `de.platesoft.inspectflow.*`
+remain in moved classes.
+
+### 4.3 W2-B — Configuration namespace
+
+**Goal:** Consolidate all `@Value("${...}")` injections into a single
+`@ConfigurationProperties("plate.auth")` class.
+
+**Steps:**
+
+1. **W2-8** Create `de.platesoft.auth.PlateAuthProperties`:
+ ```java
+ @ConfigurationProperties(prefix = "plate.auth")
+ @Data
+ public class PlateAuthProperties {
+ private Jwt jwt = new Jwt();
+ private Exchange exchange = new Exchange();
+ private Registration registration = new Registration();
+ private Cors cors = new Cors();
+ private Providers providers = new Providers();
+
+ @Data public static class Jwt {
+ private String secret;
+ private Duration accessExpiration = Duration.ofMinutes(15);
+ private Duration refreshExpiration = Duration.ofDays(30);
+ private String issuer = "plate-auth";
+ }
+ @Data public static class Exchange {
+ private String secret;
+ private Duration maxAge = Duration.ofSeconds(60);
+ private Duration nonceTtl = Duration.ofMinutes(5);
+ }
+ @Data public static class Registration {
+ private boolean enabled = false;
+ }
+ @Data public static class Cors {
+ private List allowedOrigins = new ArrayList<>();
+ private List additionalPermitPaths = new ArrayList<>();
+ }
+ @Data public static class Providers {
+ private ProviderToggle google = new ProviderToggle(true);
+ private ProviderToggle microsoft = new ProviderToggle(false);
+ private ProviderToggle emailMagicLink = new ProviderToggle(false);
+ }
+ @Data @AllArgsConstructor @NoArgsConstructor public static class ProviderToggle {
+ private boolean enabled;
+ }
+ }
+ ```
+2. **W2-9** Inject `PlateAuthProperties` (constructor injection) into every service that previously read
+ `@Value("${jwt....}")` etc. Replace `@Value` annotations.
+3. **W2-10** Add `META-INF/spring-configuration-metadata.json` (or rely on `spring-boot-configuration-processor`
+ annotation processor — preferred — add it to the build).
+4. **W2-11** Bean-validate critical fields:
+ ```java
+ @NotBlank @Size(min=32) private String secret; // both jwt.secret and exchange.secret
+ ```
+
+**Done when:** No `@Value("${jwt....}")` or `@Value("${nextauth....}")` strings remain. All config flows
+through `PlateAuthProperties`. App fails fast at startup if `secret` is missing or <32 chars.
+
+### 4.4 W2-C — Auto-configuration
+
+**Goal:** Make the starter "just work" when added as a dependency.
+
+**Steps:**
+
+1. **W2-12** Create `de.platesoft.auth.PlateAuthAutoConfiguration`:
+ ```java
+ @AutoConfiguration
+ @EnableConfigurationProperties(PlateAuthProperties.class)
+ @ComponentScan(basePackages = "de.platesoft.auth")
+ @EntityScan(basePackages = "de.platesoft.auth.entity")
+ @EnableJpaRepositories(basePackages = "de.platesoft.auth.repository")
+ @ConditionalOnProperty(prefix = "plate.auth", name = "enabled", havingValue = "true", matchIfMissing = true)
+ public class PlateAuthAutoConfiguration {
+ // Default SPI beans, ConditionalOnMissingBean — see W4
+ }
+ ```
+2. **W2-13** Register the auto-config:
+ ```
+ src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+ ```
+ Contents: single line `de.platesoft.auth.PlateAuthAutoConfiguration`.
+3. **W2-14** Verify that adding the starter to a minimal Spring Boot 4 app, setting `plate.auth.jwt.secret`
+ + `plate.auth.exchange.secret` env vars, and `spring.datasource.url` to a Postgres, makes the app boot
+ and serve `/api/auth/config`.
+
+**Done when:** Smoke test app (in `plate-auth-tests/`) boots and responds to `/api/auth/config`.
+
+### 4.5 W4 — SPI design + default implementations
+
+**Goal:** Five SPI interfaces with sensible defaults so consumers can override selectively.
+
+**Steps:**
+
+1. **W4-1** Create the 5 SPI interfaces under `de.platesoft.auth.spi`:
+ ```java
+ public interface OrgValidator {
+ boolean exists(OrgType type, UUID orgId);
+ }
+ public interface OrgDisplayNameResolver {
+ String displayName(OrgType type, UUID orgId);
+ }
+ public interface InvitationMailer {
+ void sendInvitation(Invitation invitation, String acceptUrl);
+ }
+ public interface AccessRequestMailer {
+ void notifyAdmins(AccessRequest request);
+ void notifyRequester(AccessRequest request); // on decision
+ }
+ public interface OnboardingHook {
+ void onFirstSignIn(User user, LoginProvider provider);
+ default void onSubsequentSignIn(User user, LoginProvider provider) { /* no-op */ }
+ }
+ ```
+2. **W4-2** Default implementations (annotated `@ConditionalOnMissingBean`, registered in
+ `PlateAuthAutoConfiguration`):
+ - `DefaultOrgValidator` — **does NOT exist as a default**; auto-config requires the consumer to
+ provide one if T2 endpoints are used. If absent, T2 endpoints fail-fast on first invocation with a
+ clear error: "Provide a `de.platesoft.auth.spi.OrgValidator` bean to use plate-auth multi-tenancy."
+ - `DefaultOrgDisplayNameResolver` — returns `type + ":" + orgId.toString()`
+ - `LoggingInvitationMailer` — logs the accept URL at INFO level
+ - `LoggingAccessRequestMailer` — logs notifications at INFO level
+ - `NoOpOnboardingHook` — no-op
+3. **W4-3** Wire each service to its SPI dep via constructor injection:
+ - `MembershipService` ← `OrgValidator`, `OrgDisplayNameResolver`
+ - `InvitationService` ← `InvitationMailer`, `OrgDisplayNameResolver`
+ - `AccessRequestService` ← `AccessRequestMailer`, `OrgDisplayNameResolver`
+ - `OAuthService` ← `OnboardingHook`
+4. **W4-4** Document each SPI with Javadoc including:
+ - When it is called
+ - What happens if the consumer doesn't provide one (which default kicks in)
+ - Migration: if a previous version's signature changed, link to CHANGELOG
+
+**Done when:** A consumer can register a single `OrgValidator` bean and have T2 fully functional. Default
+mailers log instead of crashing.
+
+---
+
+*Plan continues — frontend extraction, Flyway, publishing.*