81 lines
4.3 KiB
Markdown
81 lines
4.3 KiB
Markdown
# Self-hosted identity operations
|
|
|
|
This milestone deploys Better Auth beside the existing Supabase login. It does not authorize the final authentication cutover or removal of Supabase-backed business routes.
|
|
|
|
## Safe staging mode
|
|
|
|
Keep these two values exactly as shown while profile, consultation, credits, chat, and report routes still rely on Supabase JWT/RLS:
|
|
|
|
```dotenv
|
|
AUTH_PROVIDER=supabase
|
|
SELF_HOSTED_IDENTITY_ENABLED=true
|
|
```
|
|
|
|
This combination keeps `staging.jyotisha.chat/login` on Supabase, enables `/api/auth/**` for integration tests, and makes `admin.staging.jyotisha.chat/login` use the isolated Better Auth admin surface. The public and admin sessions have different secrets and host-only cookie prefixes. The staging validator deliberately rejects `AUTH_PROVIDER=self-hosted` in this milestone.
|
|
|
|
Use [the tracked staging identity example](../../deploy/.env.staging.identity.example) as a list of names only. Replace bracketed values directly on the server and keep `/opt/jyotisha-staging/.env.staging` owned by `deploy` with mode `0600`.
|
|
|
|
Generate separate secrets locally on the server:
|
|
|
|
```bash
|
|
openssl rand -base64 32
|
|
openssl rand -base64 32
|
|
```
|
|
|
|
Do not reuse either value as a PostgreSQL password. `IDENTITY_DATABASE_URL` uses the existing `IDENTITY_RUNTIME_PASSWORD` from `.env.staging.database`, percent-encoded only in the URL password component. It must point to the private Compose hostname `postgres:5432/jyotisha`; never publish PostgreSQL on a host port.
|
|
|
|
The Resend key must be staging-only. `RESEND_FROM_EMAIL` must use a sender/domain verified in Resend. CI never receives this key and uses an in-memory sender.
|
|
|
|
Validate without printing values:
|
|
|
|
```bash
|
|
cd /opt/jyotisha-staging
|
|
chmod 600 .env.staging
|
|
bash deploy/validate-staging-env.sh .env.staging
|
|
```
|
|
|
|
## Migration and smoke checks
|
|
|
|
Apply the reviewed PostgreSQL migrations through the existing `Migrate Staging Database` workflow before deploying the web image. The identity migration creates `identity.users`, `identity.sessions`, `identity.accounts`, `identity.verifications`, and `identity.otp_rate_limits` under least-privilege roles.
|
|
|
|
After deployment:
|
|
|
|
```bash
|
|
curl -fsS https://admin.staging.jyotisha.chat/login >/dev/null
|
|
curl -fsS https://admin.staging.jyotisha.chat/api/auth/get-session
|
|
test "$(curl -sS -o /dev/null -w '%{http_code}' https://admin.staging.jyotisha.chat/)" = 404
|
|
```
|
|
|
|
An unknown or unpromoted email cannot create an admin session. Promote an imported staging user only through a reviewed database/admin operation; the persisted `identity.users.role` value must include `admin` before the admin OTP flow can issue a cookie.
|
|
|
|
## Import rehearsal
|
|
|
|
Export Supabase Auth users to a JSON array in the supported fixture shape, then run a redacted dry-run first:
|
|
|
|
```bash
|
|
cd /opt/jyotisha-staging/frontend
|
|
node scripts/import-supabase-auth-users.mjs /secure/path/auth-users.json
|
|
```
|
|
|
|
The summary contains only counts. It preserves UUID, normalized email, verification timestamps, display metadata, and created/updated timestamps. It intentionally ignores passwords, sessions, JWTs, provider secrets, and Supabase platform fields.
|
|
|
|
Apply only after reviewing the dry-run and taking a local encrypted staging backup:
|
|
|
|
```bash
|
|
set -a
|
|
. ../.env.staging
|
|
set +a
|
|
node scripts/import-supabase-auth-users.mjs /secure/path/auth-users.json --apply
|
|
unset IDENTITY_DATABASE_URL
|
|
```
|
|
|
|
Reruns are idempotent by UUID and the whole import is transactional. Duplicate canonical emails abort before database writes.
|
|
|
|
## Rollback and rotation
|
|
|
|
To disable the new identity service without touching Supabase login, set `SELF_HOSTED_IDENTITY_ENABLED=false`, remove the identity-only smoke check for that separately reviewed rollback revision, and redeploy. Existing self-hosted sessions become unreachable; do not delete identity rows during application rollback.
|
|
|
|
Rotating either Better Auth secret invalidates only that surface's existing sessions. Rotate user and admin secrets separately, restart the web service, and verify the corresponding host. Rotate a leaked Resend key in Resend first, replace the server value, then restart. Never print the old or new values.
|
|
|
|
Final `AUTH_PROVIDER=self-hosted` cutover is blocked until all business modules authorize with the self-hosted session boundary, reconciliation passes, production backups and restore drills exist, and a separate reviewed cutover plan is approved.
|