Implements passwordless authentication via passkeys using @simplewebauthn:
Backend (mana-core-auth):
- New passkeys table in auth schema (credentialId, publicKey, counter, etc.)
- PasskeyService with registration/authentication flows and challenge storage
- 7 new API endpoints (register, authenticate, list, delete, rename)
- createSessionAndTokens helper for non-password auth flows
- Security event types for passkey operations
Client (shared-auth):
- signInWithPasskey() and registerPasskey() with dynamic @simplewebauthn/browser imports
- isPasskeyAvailable() browser capability check
- Passkey management methods (list, delete, rename)
UI (shared-auth-ui):
- Passkey button on LoginPage with key icon, shown when browser supports WebAuthn
- Divider between passkey and email/password form
App integration:
- All 19 web app auth stores have isPasskeyAvailable() and signInWithPasskey()
- All 19 web app login pages pass passkeyAvailable and onSignInWithPasskey props
- rpID=mana.how in production enables cross-app passkey usage (SSO-compatible)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace createRemoteJWKSet (HTTP to localhost) with local DB-backed
JWKS cache. Keys are read from auth.jwks table and cached in memory
with 5-minute TTL. Eliminates HTTP roundtrip per token validation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The auth service now depends on @manacore/shared-llm but it was missing
from the Docker build context, causing build failures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mac Mini had 25k+ TIME_WAIT sockets exhausting the 16k ephemeral port range,
blocking all outgoing TCP connections. Root cause: ~50 health checks at 30s
intervals + n8n automation creating excessive short-lived connections.
- Remove n8n service and volume (no longer needed)
- Increase health check intervals: 30s → 120s (app services), 10s → 30s (infra)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mana-media uses NestJS 11 while shared-nestjs-metrics targets NestJS 10,
causing DynamicModule type incompatibility. Use prom-client directly with
a simple MetricsController to expose /metrics endpoint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dockerfile, docker-compose service (port 5100), Caddy and cloudflared
routing for the WhoPixels game. PORT is now configurable via env var.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Switch from bcrypt (native C++ addon) to bcryptjs (pure JavaScript)
- Remove python3/make/g++ build tools from Dockerfile builder stage
- bcryptjs is 100% hash-compatible with bcrypt
- Smaller builder image and faster Docker builds
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SameSite=Lax only sends cookies on top-level navigations (link clicks),
not on programmatic fetch() requests. SSO relies on fetch() with
credentials:'include' from app subdomains to auth.mana.how, so
SameSite=None is required when COOKIE_DOMAIN is set.
Falls back to Lax for local development (no COOKIE_DOMAIN).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use --chown on COPY instead of chown -R (eliminates duplicate layer)
- Remove corepack from production stage (not needed at runtime)
- Prune devDependencies and clean up test/docs/sourcemaps from node_modules
- Tested: container starts and passes health check
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add pnpm prune --prod to remove devDependencies from node_modules
- Use --chown on COPY instead of chown -R (eliminates 1.6GB duplicate layer)
- Remove corepack from production stage (not needed at runtime)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. SecurityEventsService: Centralized audit logging for all auth events
(login, register, logout, password changes, API key operations, SSO
token exchange, etc.). Fire-and-forget pattern ensures auth flows
are never blocked by logging failures.
2. AccountLockoutService: Locks accounts after 5 failed login attempts
within 15 minutes. 30-minute lockout duration. Fails open on DB
errors. Clears attempts on successful login. Email-not-verified
does not count as a failed attempt.
3. API Key validation endpoint secured with rate limiting (10 req/min
per IP via ThrottlerGuard) and audit logging. Key prefixes logged
for forensics, never full keys.
New schema: auth.login_attempts table for tracking failed logins.
174 tests passing across all auth and security modules.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adding a new app to cross-app SSO requires updating trustedOrigins,
CORS_ORIGINS, and running SSO contract tests. Documented in both
root CLAUDE.md and mana-core-auth CLAUDE.md to prevent future regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Several apps (mukke, photos, planta, questions, todo, traces, context,
docs, manadeck, zitare) were missing from Better Auth's trustedOrigins,
causing SSO session cookie exchange to fail for those apps. Also synced
CORS_ORIGINS in docker-compose.macmini.yml.
Added 47 SSO contract tests to prevent regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assign version numbers based on app maturity: Calendar/Contacts/Todo (1.0.0),
Chat/Picture (0.3.0), 11 beta apps (0.2.0), Context/Planta/Questions (0.1.0),
Traces (0.0.1). Set up @changesets/cli for future version management.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use --filter to only install mana-core-auth and shared-storage deps,
avoiding missing workspace package errors from root package.json.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pnpm resolves patch references from root package.json even for
partial workspace installs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The partial workspace copy causes lockfile mismatches since not all
referenced packages are present in the Docker build context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Better Auth may return error objects instead of throwing for unverified
emails. Now checks result.error before hasUser(), uses case-insensitive
matching, and re-throws NestJS HTTP exceptions to avoid masking them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 22 E2E tests across 5 test suites covering auth, calendar views,
settings, event CRUD, and calendar management. Tests that require the
calendar backend gracefully skip when it's not running.
Also fixes: hooks.server.ts env fallbacks, ThrottlerGuard DI error,
and auth metrics service TypeScript error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Better Auth throws APIError("FORBIDDEN") when email is not verified,
but the signIn catch block didn't handle this case, causing a 500.
Now returns ForbiddenException with EMAIL_NOT_VERIFIED code so the
client can show the resend verification link.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The ResetPasswordDto required 12 characters while registration and
change-password only required 8, causing a mismatch with frontend UI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add shared-pwa, qr-export, and wallpaper-generator packages
to the Docker build context for manacore-web.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The manual bodyParser.json() middleware conflicts with NestJS rawBody mode.
When rawBody: true is enabled, NestJS consumes the body stream first, then
the manual parser tries to read it again causing "stream is not readable".
NestJS handles JSON parsing internally, so the manual middleware was redundant
and causing 500 errors on login requests.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add all new organization management endpoints to API table
- Add new Invitations section for invitation endpoints
- Update controller JSDoc with complete endpoint list
- Update last updated date
Add missing organization features for Teams functionality:
- PUT /auth/organizations/:id - update organization
- DELETE /auth/organizations/:id - delete organization
- PATCH /auth/organizations/:orgId/members/:memberId/role - update member role
- GET /auth/organizations/:id/invitations - list org invitations
- GET /auth/invitations - list user invitations
- DELETE /auth/invitations/:id - cancel or reject invitation
- Add sepa_debit to payment_method_types for credit purchases
- Add sepa_debit to subscription checkout sessions
- Handle payment_intent.processing webhook for SEPA status tracking
- Add blueprint article analyzing payment options (Stripe vs LSV+/FinTS)
SEPA offers lower fees (0.8% vs 1.5%+€0.25) for DACH customers.
Payments are confirmed 3-14 days after checkout (bank processing).
The JSON body parser was consuming the request body before NestJS
could access the rawBody needed for Stripe webhook signature
verification. Now webhooks to /api/v1/webhooks/stripe skip the
body parser middleware.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove free credits system (signup bonus, daily credits) and B2B organization
credits to simplify the codebase. Credits now only come from purchases or gifts.
Changes:
- Remove freeCreditsRemaining, dailyFreeCredits, lastDailyResetAt from balances
- Remove organizationBalances and creditAllocations tables from schema
- Simplify transaction types to: purchase, usage, refund, gift
- Remove B2B endpoints from credits controller
- Remove checkDailyReset, allocateCredits, deductCredits from service
- Add redeemPendingGifts method to auto-redeem gifts on registration
- Update frontend to remove free credits display
- Add database migration for the changes
- Update all related tests to match simplified system
Drizzle ORM requires explicit null values for optional fields instead of
leaving them undefined. Added explicit null for organizationId,
idempotencyKey, portionNumber, and creditTransactionId.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Drizzle ORM throws "UNDEFINED_VALUE" error when inserting undefined values.
Convert dto.sourceAppId to null using nullish coalescing operator.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add matrix-onboarding-bot service that guides users through profile setup
- Extend mana-core-auth GlobalSettings with displayName, interests, onboardingCompleted fields
- Implement state machine for onboarding flow (NAME → INTERESTS → LANGUAGE → SUMMARY)
- Support commands: !start, !profile, !edit, !skip, !help
- Add German and English localization
- Integrate with mana-core-auth Settings API for profile persistence
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
NestJS matches routes in declaration order. The dynamic :code route was
matching before me/created and me/received, treating 'me' as a gift code.
Moved specific routes before the dynamic parameter route.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add recentEmojis field to GlobalSettings in shared-theme
- Create userSettings store for Matrix app with JWT token management
- Exchange session cookie for JWT after SSO login
- Update MessageInput to use userSettings instead of localStorage
- Add recentEmojis support to mana-core-auth settings API
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove duplicate route prefix in GiftsController (was /api/v1/api/v1/gifts)
- Fix JwtAuthGuard to use JWT_ISSUER as fallback when BASE_URL is not set
- Add comprehensive GIFT_CODES.md documentation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add gift code creation, redemption, and refund endpoints
- Add Stripe payment link generation for credits
- Add gifts database schema
- Enhance credits controller with new operations