81e454d86d
Registration now goes through POST /api/auth/register, which admin-creates the Kratos identity (unverified email, NO auto-sent code). Kratos self-service registration never returns the verification flow id, so the client could never submit the user's code to the right flow; admin creation lets the client own a single verification flow instead. Also surface the live Kratos verified flag and fix Apple audience + team IDs. - kratos.Client.CreateIdentity via admin API; ErrIdentityExists / ErrInvalidCredentials - AuthService.Register + AuthHandler.Register + public POST /api/auth/register/ - CurrentUser overrides stale user_profile.verified with the live Kratos flag; UserRepository.MarkVerified mirrors it back - configmap: additional_id_token_audiences allows the .dev bundle id_token - fix Apple/APNs team id V3PF3M6B6U -> X86BR9WTLD in .env.example + dev init Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
221 lines
7.6 KiB
YAML
221 lines
7.6 KiB
YAML
# Ory Kratos configuration for honeyDue.
|
|
#
|
|
# Secrets are NOT in this ConfigMap. The DSN, cookie/cipher secrets, SMTP URI
|
|
# and OIDC client secrets are injected as environment variables from the
|
|
# kratos-secrets Secret (see kratos.yaml). Kratos is configured natively via
|
|
# env vars, so this is the idiomatic split — only non-secret config here.
|
|
#
|
|
# OIDC scope: Apple-only as of 2026-06-03. Google is intentionally absent;
|
|
# adding it later is additive — append a `- id: google` block under
|
|
# selfservice.methods.oidc.config.providers (it becomes index 1) and bind a
|
|
# matching CLIENT_SECRET env in kratos.yaml.
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: kratos-config
|
|
namespace: honeydue
|
|
labels:
|
|
app.kubernetes.io/name: kratos
|
|
app.kubernetes.io/part-of: honeydue
|
|
data:
|
|
kratos.yml: |
|
|
# version must track the Kratos image tag — kratos.yaml + migrate-job.yaml
|
|
# both pin oryd/kratos:v26.2.0 (2026-06-03). See kratos/README.md.
|
|
version: v1.3.0 # internal config schema version; do not change unless Kratos release notes require it
|
|
|
|
serve:
|
|
public:
|
|
base_url: https://auth.myhoneydue.com/
|
|
cors:
|
|
enabled: true
|
|
allowed_origins:
|
|
- https://myhoneydue.com
|
|
- https://app.myhoneydue.com
|
|
- https://admin.myhoneydue.com
|
|
allowed_methods: [GET, POST, PUT, PATCH, DELETE, OPTIONS]
|
|
allowed_headers: [Authorization, Content-Type, X-Session-Token, Cookie]
|
|
exposed_headers: [Content-Type, Set-Cookie]
|
|
# Required: the web clients call Kratos browser flows with
|
|
# credentials (the ory_kratos_session cookie). Safe here because
|
|
# allowed_origins is an explicit list, never a wildcard.
|
|
allow_credentials: true
|
|
admin:
|
|
base_url: http://kratos.honeydue.svc.cluster.local:4434/
|
|
|
|
selfservice:
|
|
default_browser_return_url: https://app.myhoneydue.com/
|
|
allowed_return_urls:
|
|
- https://app.myhoneydue.com
|
|
- https://myhoneydue.com
|
|
- honeydue://callback
|
|
|
|
methods:
|
|
password:
|
|
enabled: true
|
|
code: # email one-time codes (verify/recover)
|
|
enabled: true
|
|
oidc:
|
|
enabled: true
|
|
config:
|
|
providers:
|
|
# index 0 — Apple Sign In. apple_private_key (.p8 contents) is
|
|
# injected via env SELFSERVICE_METHODS_OIDC_CONFIG_PROVIDERS_0_APPLE_PRIVATE_KEY.
|
|
# client_id is the Apple Services ID (here: the bundle ID, which
|
|
# was configured as a Services ID with Sign In with Apple
|
|
# capability — see operator notes in README.md §5).
|
|
- id: apple
|
|
provider: apple
|
|
# Production bundle id. Apple issues id_tokens with
|
|
# `aud` = the requesting app's bundle id, so this is the
|
|
# primary audience Kratos verifies against.
|
|
client_id: com.myhoneydue.honeyDue
|
|
# Debug builds out of Xcode use a `.dev` bundle id (see
|
|
# iosApp/honeyDue.xcodeproj — Debug config). Their id_tokens
|
|
# therefore have `aud: com.myhoneydue.honeyDue.dev`, which
|
|
# the primary client_id check rejects. Whitelist the dev
|
|
# audience so Apple Sign In works from a non-Release Xcode
|
|
# build without per-build Kratos reconfiguration.
|
|
additional_id_token_audiences:
|
|
- com.myhoneydue.honeyDue.dev
|
|
apple_team_id: X86BR9WTLD
|
|
apple_private_key_id: HQD3NCF99C
|
|
mapper_url: file:///etc/kratos/oidc.apple.jsonnet
|
|
scope: [openid, email, name]
|
|
|
|
flows:
|
|
error:
|
|
ui_url: https://app.myhoneydue.com/auth/error
|
|
login:
|
|
ui_url: https://app.myhoneydue.com/auth/login
|
|
lifespan: 10m
|
|
registration:
|
|
ui_url: https://app.myhoneydue.com/auth/registration
|
|
lifespan: 10m
|
|
after:
|
|
password:
|
|
hooks:
|
|
- hook: session # auto-login after registration
|
|
oidc:
|
|
hooks:
|
|
- hook: session
|
|
verification:
|
|
enabled: true
|
|
ui_url: https://app.myhoneydue.com/auth/verification
|
|
use: code
|
|
after:
|
|
default_browser_return_url: https://app.myhoneydue.com/
|
|
recovery:
|
|
enabled: true
|
|
ui_url: https://app.myhoneydue.com/auth/recovery
|
|
use: code
|
|
settings:
|
|
ui_url: https://app.myhoneydue.com/auth/settings
|
|
privileged_session_max_age: 15m
|
|
logout:
|
|
after:
|
|
default_browser_return_url: https://app.myhoneydue.com/
|
|
|
|
log:
|
|
level: info
|
|
format: json
|
|
leak_sensitive_values: false
|
|
|
|
ciphers:
|
|
algorithm: xchacha20-poly1305
|
|
|
|
hashers:
|
|
algorithm: bcrypt
|
|
bcrypt:
|
|
cost: 12
|
|
|
|
identity:
|
|
default_schema_id: honeydue
|
|
schemas:
|
|
- id: honeydue
|
|
url: file:///etc/kratos/identity.schema.json
|
|
|
|
courier:
|
|
smtp:
|
|
from_address: noreply@myhoneydue.com
|
|
from_name: honeyDue
|
|
# connection_uri is injected via env COURIER_SMTP_CONNECTION_URI
|
|
|
|
session:
|
|
lifespan: 720h # 30-day sessions (mobile)
|
|
cookie:
|
|
domain: myhoneydue.com
|
|
same_site: Lax
|
|
|
|
identity.schema.json: |
|
|
{
|
|
"$id": "https://honeydue.app/identity.schema.json",
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"title": "honeyDue user",
|
|
"type": "object",
|
|
"properties": {
|
|
"traits": {
|
|
"type": "object",
|
|
"properties": {
|
|
"email": {
|
|
"type": "string",
|
|
"format": "email",
|
|
"title": "Email",
|
|
"minLength": 3,
|
|
"maxLength": 320,
|
|
"ory.sh/kratos": {
|
|
"credentials": {
|
|
"password": { "identifier": true },
|
|
"code": { "identifier": true, "via": "email" },
|
|
"totp": { "account_name": true }
|
|
},
|
|
"verification": { "via": "email" },
|
|
"recovery": { "via": "email" }
|
|
}
|
|
},
|
|
"name": {
|
|
"type": "object",
|
|
"title": "Name",
|
|
"properties": {
|
|
"first": { "type": "string", "title": "First name", "maxLength": 100 },
|
|
"last": { "type": "string", "title": "Last name", "maxLength": 100 }
|
|
}
|
|
}
|
|
},
|
|
"required": ["email"],
|
|
"additionalProperties": false
|
|
}
|
|
}
|
|
}
|
|
|
|
oidc.google.jsonnet: |
|
|
// Maps Google OIDC claims onto the honeyDue identity schema.
|
|
local claims = std.extVar('claims');
|
|
{
|
|
identity: {
|
|
traits: {
|
|
email: claims.email,
|
|
[if 'given_name' in claims || 'family_name' in claims then 'name']: {
|
|
first: if 'given_name' in claims then claims.given_name else '',
|
|
last: if 'family_name' in claims then claims.family_name else '',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
oidc.apple.jsonnet: |
|
|
// Maps Apple OIDC claims onto the honeyDue identity schema. Apple only
|
|
// returns the name on the very first authorization and not in the ID
|
|
// token claims, so only email is mapped here.
|
|
//
|
|
// NOTE: we intentionally do NOT carry Apple's email_verified across via
|
|
// verified_addresses. Product decision: every account-creation flow —
|
|
// including Sign in with Apple — must complete an email verification step.
|
|
local claims = std.extVar('claims');
|
|
{
|
|
identity: {
|
|
traits: {
|
|
email: claims.email,
|
|
},
|
|
},
|
|
}
|