KeiSeiKit-1.0/skills/auth-setup/phase-2-identity-provider.md
Parfii-bot 505e727dcf feat(skills): /auth-setup 5-phase pipeline
Hub-and-spoke skill that converts "I need auth for app X" into a
reviewable plan across 5 phases: intake (flows/stack/storage/MFA),
identity-provider pick + env scaffold, session strategy + cookies,
authorization model + permission matrix, and threats + mitigations.

- 8 AskUserQuestion calls total (≥6 hub-and-spoke contract; 4 in Phase 1
  + 1 each in Phases 2–5).
- Reads all four _blocks/auth-*.md; never writes production code or
  secret values.
- RULE 0.8 (Secrets SSoT): emits env VARIABLE NAMES only; storage path
  is secrets/auth.env per domain-has-secrets.md.
- Constructor Pattern: 6 files, largest 115 LOC (<200 limit).
- Fail-closed default + NO DOWNGRADE on unsafe combinations
  (passkey-only without recovery → return recovery-path options, not
  "not supported").

Evidence grade [E2] — pipeline mirrors OWASP ASVS v4.0.3 chapters 2–4.
2026-04-21 20:47:21 +08:00

3.2 KiB
Raw Blame History

Phase 2 — Identity provider selection + env-var scaffold

Only runs if FLOWS contains OAuth / social or Enterprise SSO. If neither is selected, skip to Phase 3 (passkey-only or magic-link apps don't need external IdPs).

2a — Provider click (AskUserQuestion, multi-select)

Reference: _blocks/auth-oauth2-oidc.md.

{
  "questions": [
    {
      "question": "Which identity providers to register?",
      "header": "Providers",
      "multiSelect": true,
      "options": [
        {"label": "Google",    "description": "OIDC; discovery at accounts.google.com/.well-known/openid-configuration"},
        {"label": "GitHub",    "description": "OAuth2 only (no OIDC discovery); hard-code endpoints"},
        {"label": "Apple",     "description": "OIDC; name/email returned ONLY on first consent — persist immediately"},
        {"label": "Microsoft", "description": "OIDC multi-tenant via login.microsoftonline.com/common/v2.0"},
        {"label": "Enterprise OIDC (Okta / Auth0 / Keycloak / Entra)", "description": "B2B SSO; per-tenant discovery URL"},
        {"label": "SAML 2.0 (legacy enterprise)", "description": "Use a library like samlify (TS) or python3-saml; NOT OAuth"}
      ]
    }
  ]
}

Store as PROVIDERS. Empty → skip Phase 2.

2b — Emit env-var scaffold (no AskUserQuestion)

For EACH provider in PROVIDERS, emit the env-var rows the user must add to secrets/auth.env. NEVER emit values — names only. Example for Google:

# secrets/auth.env — add these, then `chmod 600` the file
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI=https://<app>/auth/google/callback
GOOGLE_OIDC_DISCOVERY=https://accounts.google.com/.well-known/openid-configuration

Per-provider scaffold rules:

  • Google / Microsoft / Apple / Enterprise OIDC: *_CLIENT_ID, *_CLIENT_SECRET (confidential) OR *_CLIENT_ID + PKCE only (public SPA/mobile), *_REDIRECT_URI, *_OIDC_DISCOVERY.
  • Apple adds APPLE_TEAM_ID, APPLE_KEY_ID, APPLE_PRIVATE_KEY_PATH (path to the .p8 file — stored inside secrets/, never inline).
  • GitHub: GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_REDIRECT_URI. No discovery URL.
  • SAML: SAML_IDP_METADATA_URL, SAML_SP_ENTITY_ID, SAML_SP_ACS_URL, SAML_SP_PRIVATE_KEY_PATH, SAML_SP_CERT_PATH.

Emit the snippet as a fenced code block in chat. Remind the user once: "File secrets/auth.env must be chmod 600 and listed in .gitignore BEFORE the first write. See _blocks/domain-has-secrets.md."

2c — Library pick (emitted inline, no AskUserQuestion)

Driven by STACK from Phase 1:

  • Next.js / Remix / SvelteKit / Astro: Better-Auth (preferred 20252026) OR NextAuth/Auth.js (Next-only, mature). Both support PKCE by default.
  • Rust (axum): openidconnect-rs (OIDC) or oauth2-rs (OAuth2 bare).
  • Python (FastAPI): authlib (covers both OAuth2 and OIDC).
  • Managed (Clerk / Supabase / WorkOS): provider SDK only; this phase just records the SDK name.

Verify-criterion

  • Every provider in PROVIDERS has its env-var scaffold printed.
  • No literal token value appears anywhere in the emitted text (RULE 0.8 / auth-oauth2-oidc.md enforcement).
  • Library pick is one line, matches STACK.