Rename Java packages from net.siegeln.cameleer3 to net.siegeln.cameleer, update all references in workflows, Docker configs, docs, and bootstrap. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
90 lines
3.8 KiB
Markdown
90 lines
3.8 KiB
Markdown
# Custom Logto Sign-In UI — IMPLEMENTED
|
|
|
|
## Problem
|
|
|
|
Logto's default sign-in page uses Logto branding. While we configured colors and logos via `PATCH /api/sign-in-exp`, control over layout, typography, and components is limited. The sign-in experience is visually inconsistent with the cameleer-server login page.
|
|
|
|
## Goal
|
|
|
|
Replace Logto's sign-in UI with a custom React SPA that visually matches the cameleer-server login page, using `@cameleer/design-system` components for consistency across all deployment models.
|
|
|
|
## Scope
|
|
|
|
**MVP (implemented)**: Username/password sign-in only.
|
|
**Later**: Sign-up, forgot password, social login, MFA.
|
|
|
|
## Architecture
|
|
|
|
### Source
|
|
|
|
Separate Vite+React app at `ui/sign-in/`. Own `package.json`, shares `@cameleer/design-system` v0.1.31.
|
|
|
|
### Build
|
|
|
|
Custom Logto Docker image (`cameleer-logto`). `ui/sign-in/Dockerfile` has a multi-stage build: node stage builds the SPA, then copies dist over Logto's built-in experience directory at `/etc/logto/packages/experience/dist/`. CI builds and pushes the image.
|
|
|
|
**Note**: `CUSTOM_UI_PATH` env var does NOT work for Logto OSS — it's a Logto Cloud-only feature. The correct approach for self-hosted is replacing the experience dist directory.
|
|
|
|
### Deploy
|
|
|
|
`docker-compose.yml` pulls the pre-built `cameleer-logto` image. No init containers, no shared volumes, no local builds needed.
|
|
|
|
### Auth Flow
|
|
|
|
```
|
|
User visits /platform/ → LoginPage auto-redirects to Logto OIDC
|
|
→ Logto sets interaction cookie, serves custom sign-in UI
|
|
→ User enters credentials → Experience API 4-step flow
|
|
→ Logto redirects back to /platform/callback with auth code
|
|
→ SaaS app exchanges code for token, user lands on dashboard
|
|
```
|
|
|
|
## Experience API
|
|
|
|
The custom UI communicates with Logto via the Experience API (stateful, cookie-based):
|
|
|
|
```
|
|
1. PUT /api/experience → 204 (init SignIn)
|
|
2. POST /api/experience/verification/password → 200 { verificationId }
|
|
3. POST /api/experience/identification → 204 (confirm identity)
|
|
4. POST /api/experience/submit → 200 { redirectTo }
|
|
```
|
|
|
|
The interaction cookie is set by `/oidc/auth` before the user lands on the sign-in page. All API calls use `credentials: "same-origin"`.
|
|
|
|
## Visual Design
|
|
|
|
Matches cameleer-server LoginPage exactly:
|
|
- Centered `Card` (400px max-width, 32px padding)
|
|
- Logo: favicon.svg + "cameleer" text (24px bold)
|
|
- Random witty subtitle (13px muted)
|
|
- `FormField` + `Input` for username and password
|
|
- Amber `Button` (primary variant, full-width)
|
|
- `Alert` for errors
|
|
- Background: `var(--bg-base)` (light beige)
|
|
- Dark mode support via design-system tokens
|
|
- Favicon bundled in `ui/sign-in/public/favicon.svg` (served by Logto, not SaaS)
|
|
|
|
## Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `ui/sign-in/Dockerfile` | Multi-stage: node build + FROM logto:latest + COPY dist |
|
|
| `ui/sign-in/package.json` | React 19 + @cameleer/design-system + Vite 6 |
|
|
| `ui/sign-in/src/SignInPage.tsx` | Login form with Experience API integration |
|
|
| `ui/sign-in/src/SignInPage.module.css` | CSS Modules (matches server LoginPage) |
|
|
| `ui/sign-in/src/experience-api.ts` | Typed Experience API client (4-step flow) |
|
|
| `ui/sign-in/src/main.tsx` | React mount + design-system CSS import |
|
|
| `ui/sign-in/public/favicon.svg` | Cameleer camel logo (bundled in dist) |
|
|
| `ui/src/auth/LoginPage.tsx` | Auto-redirects to Logto OIDC (no button) |
|
|
| `.gitea/workflows/ci.yml` | Builds and pushes `cameleer-logto` image |
|
|
| `docker-compose.yml` | Uses `cameleer-logto` image (no build directive) |
|
|
|
|
## Future Extensions
|
|
|
|
- Add React Router for multiple pages (sign-up, forgot password)
|
|
- Fetch `GET /api/.well-known/sign-in-exp` for dynamic sign-in method detection
|
|
- Social login buttons via `POST /api/experience/verification/social`
|
|
- MFA via `POST /api/experience/verification/totp`
|
|
- Consent page via `GET /api/interaction/consent`
|