# Quick start Run the owned issuer in an isolated local environment and test an OIDC authorization code flow. This exercise produces a synthetic account with no Institution or product roles. It cannot serve as a production application registration. > **Integration readiness:** This is an isolated development checkpoint. For application integration, start with [web sign-in](/docs/web), [native sign-in](/docs/native), and [client registration](/docs/client-registration). The runnable fixture below exercises only the existing local portal issuer; it does not create an approved product client or enable production sign-in. ## 1. Verify the local toolchain Start in the Taifa Identity repository root. Keep `TAIFA_IDENTITY_ROOT` in the same shell for the later command blocks. Use Go 1.27.1 and a disposable PostgreSQL database on loopback. The database name must include `test`, `scratch`, `dev`, or `preview`. The issuer refuses any other database in development. Set `AUTH_TEST_DATABASE_URL` to a disposable database URL for the database integration tests. Keep all secrets outside source control. ```sh TAIFA_IDENTITY_ROOT="$PWD" cd "$TAIFA_IDENTITY_ROOT/services/auth" go mod verify go vet ./... go test -race ./... go build ./... ``` The test suite uses `AUTH_TEST_DATABASE_URL` when provided. Database tests use unique disposable schemas. The tests cover PKCE, one-use codes, refresh rotation, expired authority leases, and challenge refusal. They do not require a live application client. ## 2. Create a disposable issuer Apply the numbered migrations from `services/auth/migrations/001_init.sql` through `014_owned_client_management_readback.sql` in order to that disposable database. Migrations 005 and 006 supply the registry, 007 supports enrollment, 008 supports logout, 009 supports durable activation retries, 010 supports private admission mail replay protection, and 011 supports central browser SSO. Migrations 012 and 013 add writer and custody schema foundations. Migration 014 scopes authenticated Management readback and its replay records; it does not register a client or grant access. Reviewed client installation remains unfinished, and this exercise does not enable the separate custody daemon. Holder setup is optional and requires separate signed authority configuration. This synthetic sign-in exercise does not provision a reviewed holder. Set these issuer variables through a private process environment: | Variable | Local value or purpose | | --- | --- | | `AUTH_DEV_INSECURE_LOOPBACK` | `1`; permits only the disposable loopback mode. | | `AUTH_CLIENT_REGISTRY_MODE` | `fixture`; allows only validated local fixture registrations in development. | | `AUTH_DATABASE_URL` | URL of the disposable PostgreSQL database on an explicit loopback IP. | | `AUTH_ISSUER` | Exact HTTPS issuer URL served by a trusted local TLS proxy. | | `AUTH_LOGIN_URL` | Portal's exact HTTPS `/signin` URL. | | `AUTH_CLIENT_REDIRECT_URL` | Portal's exact HTTPS `/auth/callback` URL. | | `AUTH_CLIENT_ID` | `taifa-identity-portal`. | | `AUTH_CLIENT_SECRET` | Independent random secret of at least 32 bytes, shared with the portal as `OIDC_CLIENT_SECRET`. | | `AUTH_BFF_KEY` | Independent random secret of at least 32 bytes, shared only with the portal BFF. | | `AUTH_PUBLIC_LISTEN`, `AUTH_INTERNAL_LISTEN` | Different explicit loopback IP listeners. Keep the internal listener private. | | `AUTH_TOTP_KEY_B64`, `AUTH_CRYPTO_KEY_B64` | Distinct standard Base64 encodings of independent 32-byte keys. | | `AUTH_SIGNING_KEY_FILE`, `AUTH_SIGNING_KEY_ID` | Owner-only RSA private key file, at least 3072 bits, and its public JWKS key ID. | Optional passkeys require all three of `AUTH_PASSKEY_RP_ID`, `AUTH_PASSKEY_ORIGIN`, and `AUTH_PASSKEY_KEY_B64`. The key must be a third independent 32-byte value. An authority lease receiver requires a separately reviewed subject mapping and the `AUTH_ELIGIBILITY_*` configuration in the service README; synthetic accounts do not use that receiver. ## 3. Register the disposable portal fixture In fixture mode, the current issuer validates local fixture registrations stored in `auth_clients`; environment variables alone do not register a client. This local-only fixture creates the portal registration with the SHA-256 digest of its private client secret and the exact configured callback. It refuses production databases and does not update an existing registration. Run it from `services/management`, whose locked environment supplies `psycopg`. These fixture registrations are not reviewed registrations, and migration 012's writer schema is not a runnable registration writer. ```sh cd "$TAIFA_IDENTITY_ROOT/services/management" uv run --locked python - <<'PY' import hashlib import ipaddress import os from urllib.parse import unquote, urlsplit import psycopg url = os.environ["AUTH_DATABASE_URL"] parsed = urlsplit(url) name = unquote(parsed.path.lstrip("/")).lower() try: loopback_host = ipaddress.ip_address(parsed.hostname).is_loopback except ValueError: loopback_host = False if (os.environ.get("AUTH_DEV_INSECURE_LOOPBACK") != "1" or not loopback_host or not any(word in name for word in ("test", "scratch", "dev", "preview"))): raise SystemExit("A disposable loopback database is required") secret = os.environ["AUTH_CLIENT_SECRET"].encode() if not 32 <= len(secret) <= 1024: raise SystemExit("An independent high-entropy client secret is required") with psycopg.connect(url) as connection: connection.execute( "INSERT INTO auth_clients " "(id,secret_hash,redirect_uris,resource_audiences,refresh_allowed," "management_access,review_id,enabled) " "VALUES (%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT (id) DO NOTHING", ("taifa-identity-portal", hashlib.sha256(secret).digest(), [os.environ["AUTH_CLIENT_REDIRECT_URL"]], ["taifa-management"], True, True, "disposable-local-exercise", True), ) PY ``` Keep the fixture database and all secrets private. Startup rejects missing, disabled, malformed or mismatched portal registrations. The registration gives the synthetic person no management role. Start the issuer: ```sh cd "$TAIFA_IDENTITY_ROOT/services/auth" go run ./cmd/authd ``` A local TLS proxy may expose the configured HTTPS issuer while forwarding to the loopback public listener. Do not expose the private challenge listener. The issuer publishes discovery, `/authorize`, `/oauth/token`, and `/keys`; the browser challenge API is private to the portal BFF. ## 4. Create one synthetic account Set `AUTH_BOOTSTRAP_USERNAME` and `AUTH_BOOTSTRAP_OUTPUT` to a path in a private `0700` directory. With the same disposable database and TOTP encryption key, run: ```sh cd "$TAIFA_IDENTITY_ROOT/services/auth" go run ./cmd/auth-bootstrap ``` The output file is `0600` and contains the generated subject, password, and TOTP seed. Use it only for the local exercise. The account's lease lasts one hour and it has no management privilege. ## 5. Exercise the browser flow Apply `apps/portal/migrations/001_sessions.sql` to a separate disposable portal database, or set `PORTAL_DATABASE_URL` and run `npm run migrate` from `apps/portal`. Install the locked portal dependencies with `npm ci`. The portal needs `PORTAL_ORIGIN` (trusted HTTPS origin), `PORTAL_DATABASE_URL`, `PORTAL_SESSION_KEY` (standard Base64 encoding of exactly 32 cryptographically random bytes), `OIDC_ISSUER` (equal to `AUTH_ISSUER`), `OIDC_CLIENT_ID=taifa-identity-portal`, `OIDC_CLIENT_SECRET` (equal to `AUTH_CLIENT_SECRET`), `AUTH_ISSUER_URL` (equal to `OIDC_ISSUER`), `AUTH_INTERNAL_URL` (the private issuer listener), `AUTH_BFF_KEY`, `AUTH_ENGINE_MODE=custom`, and `AUTH_DEV_INSECURE_LOOPBACK=1`. Trust the local TLS certificate in the browser and backend. Keep the private listener off public ingress. ```sh cd "$TAIFA_IDENTITY_ROOT/apps/portal" npm ci npm run migrate npm run check npm run build npm run dev ``` Serve the portal development process through the trusted HTTPS origin named by `PORTAL_ORIGIN`, then begin at `PORTAL_ORIGIN/auth/login`. The BFF maintains the login transaction, an HttpOnly challenge cookie, and CSRF protection. Password plus TOTP or a configured passkey completes the private challenge. The browser follows the issuer callback URL, and the BFF redeems the code with the original S256 verifier. Inspect the resulting verified `iss` and `sub` in the session and the account screen. The synthetic subject has no product access or management roles; protected account screens may refuse it. Requesting `offline_access` with the initial scope permits refresh token issuance; refresh tokens rotate within the original eight-hour session. An ID token is never a management API token. For an application-shaped code sample, run the injected protocol tests in `examples/node`, `examples/fastapi`, or `examples/native`. Those examples need a separately approved client before a real provider sign-in. ## Next step Read [client registration](/docs/client-registration) for the remaining multi-client gate, then [web sign-in](/docs/web) or [native sign-in](/docs/native). Do not route a product's workforce traffic to the disposable portal client.