Files
honeyDueAPI/deploy-k3s/manifests/kratos/configmap.yaml
T
Trey t 25897e913e
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
Auto-verify Sign in with Apple emails
Apple OIDC mapper now marks the email verified unconditionally via
verified_addresses. SIWA cryptographically proves control of the Apple ID and
Apple owns/verifies the (relay) email, so a code is redundant. Gating on
Apple's `email_verified` claim was unreliable — Apple omits it on many
authorizations, which made verification random (sometimes a surprise code
prompt). Password sign-ups still verify via the honeyDue API flow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:30:33 -05:00

233 lines
8.3 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.
//
// Sign in with Apple emails are marked verified UNCONDITIONALLY: completing
// SIWA cryptographically proves the user controls that Apple ID, and Apple
// owns/verifies the (relay or real) email, so a 6-digit code would be
// redundant. We deliberately do NOT gate this on Apple's `email_verified`
// claim — Apple omits that claim on many authorizations (only sends it on
// the first grant), which made auto-verification random: sometimes verified,
// sometimes a surprise code prompt (observed 2026-06-03). Marking it
// verified on every SIWA makes the behaviour consistent: Apple users never
// see a code; password sign-ups still verify via the honeyDue API flow.
local claims = std.extVar('claims');
{
identity: {
traits: {
email: claims.email,
},
verified_addresses: std.prune([
if 'email' in claims then {
via: 'email',
value: claims.email,
},
]),
},
}