Description
Preflight checklist
- I could not find a solution in the existing issues, docs, nor discussions.
- I agree to follow this project's Code of Conduct.
- I have read and am following this repository's Contribution Guidelines.
- I have joined the Ory Community Slack.
- I am signed up to the Ory Security Patch Newsletter.
Ory Network Project
No response
Describe the bug
In a server-side web application, I am trying to get additional data in the registration form using transient payload, as documented in https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration and https://www.ory.sh/docs/guides/integrate-with-ory-cloud-through-webhooks.
Instead of getting the input value entered in the form, my application web hook receives a transient_payload
value of { }
.
My understanding is that this would work if the registration request was json-encoded. My question is: doesn't transient payload support plain HTML forms (form-encoded)? (with multiple fields in the transient payload). And is this documented?
Reproducing the bug
- Run kratos:
docker compose -f quickstart.yml up --build --force-recreate
- Submit registration form to http://127.0.0.1:4433/self-service/registration?flow=aa5be409-40f2-48eb-95a8-bcae4c59bdc1 - the form includes an HTML input element whose name is
transient_payload.channel_name
(I'm testing with one transient payload field now, but I will need to be able to include multiple fields). - Observe the body of the Ory web hook request received by the application server:
"transient_payload": {}
.
Relevant log output
`ctx` object
This is the ctx
object received by the web application as a result of the web hook.
{
"ctx": {
"flow": {
"expires_at": "2023-08-12T09:05:36.36373425Z",
"id": "771c6318-55d9-4813-b90e-b51c3f16d516",
"issued_at": "2023-08-12T08:55:36.36373425Z",
"request_url": "http://127.0.0.1:4433/self-service/registration/browser",
"transient_payload": {},
"type": "browser",
"ui": {
"action": "http://127.0.0.1:4433/self-service/registration?flow=771c6318-55d9-4813-b90e-b51c3f16d516",
"method": "POST",
"nodes": [
{
"attributes": {
"disabled": false,
"name": "csrf_token",
"node_type": "input",
"required": true,
"type": "hidden",
"value": "goAwCBRkSB6GVDKToagMV45O7u6689IoyO64ON9Ywyw82wCqI21Ggok8fGwFJuXMGyCZbLGbBzBszkyY9TeiFA=="
},
"group": "default",
"messages": [],
"meta": {},
"type": "input"
},
{
"attributes": {
"autocomplete": "email",
"disabled": false,
"name": "traits.email",
"node_type": "input",
"required": true,
"type": "email",
"value": "someone@example.com"
},
"group": "password",
"messages": [],
"meta": {
"label": {
"id": 1070002,
"text": "E-Mail",
"type": "info"
}
},
"type": "input"
},
{
"attributes": {
"autocomplete": "new-password",
"disabled": false,
"name": "password",
"node_type": "input",
"required": true,
"type": "password"
},
"group": "password",
"messages": [
{
"context": {
"reason": "the password has been found in data breaches and must no longer be used"
},
"id": 4000005,
"text": "The password can not be used because the password has been found in data breaches and must no longer be used.",
"type": "error"
}
],
"meta": {
"label": {
"id": 1070001,
"text": "Password",
"type": "info"
}
},
"type": "input"
},
{
"attributes": {
"disabled": false,
"name": "method",
"node_type": "input",
"type": "submit",
"value": "password"
},
"group": "password",
"messages": [],
"meta": {
"label": {
"context": {},
"id": 1040001,
"text": "Sign up",
"type": "info"
}
},
"type": "input"
}
]
}
},
"identity": {
"created_at": "0001-01-01T00:00:00Z",
"id": "00000000-0000-0000-0000-000000000000",
"metadata_public": null,
"recovery_addresses": [
{
"created_at": "0001-01-01T00:00:00Z",
"id": "00000000-0000-0000-0000-000000000000",
"updated_at": "0001-01-01T00:00:00Z",
"value": "someone@example.com",
"via": "email"
}
],
"schema_id": "default",
"schema_url": "",
"state": "active",
"state_changed_at": "2023-08-12T08:56:25.117242092Z",
"traits": {
"email": "someone@example.com"
},
"updated_at": "0001-01-01T00:00:00Z",
"verifiable_addresses": [
{
"created_at": "0001-01-01T00:00:00Z",
"id": "00000000-0000-0000-0000-000000000000",
"status": "pending",
"updated_at": "0001-01-01T00:00:00Z",
"value": "someone@example.com",
"verified": false,
"via": "email"
}
]
},
"request_cookies": {
"csrf_token_806060ca5bf70dff3caa0e5c860002aade9d470a5a4dce73bcfa7ba10778f481": "vlswojcJDpwPaE7/pI7pm5Vud4ILaNUYpCD0oCpvYTg="
},
"request_headers": {
"Accept": [
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
],
"Accept-Encoding": [
"gzip, deflate, br"
],
"Accept-Language": [
"en-US,en;q=0.5"
],
"Connection": [
"keep-alive"
],
"Content-Length": [
"220"
],
"Content-Type": [
"application/x-www-form-urlencoded"
],
"Cookie": [
"csrf_token_806060ca5bf70dff3caa0e5c860002aade9d470a5a4dce73bcfa7ba10778f481=vlswojcJDpwPaE7/pI7pm5Vud4ILaNUYpCD0oCpvYTg="
],
"Origin": [
"null"
],
"Sec-Fetch-Dest": [
"document"
],
"Sec-Fetch-Mode": [
"navigate"
],
"Sec-Fetch-Site": [
"same-site"
],
"Sec-Fetch-User": [
"?1"
],
"Upgrade-Insecure-Requests": [
"1"
],
"User-Agent": [
"Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0"
]
},
"request_method": "POST",
"request_url": "http://127.0.0.1:4433/self-service/registration?flow=771c6318-55d9-4813-b90e-b51c3f16d516"
},
"email": "someone@example.com",
"primary_channel": {
"name": {},
"visibility": "public"
},
"ui_language": "en",
"user_id": {
"created_at": "0001-01-01T00:00:00Z",
"id": "00000000-0000-0000-0000-000000000000",
"metadata_public": null,
"recovery_addresses": [
{
"created_at": "0001-01-01T00:00:00Z",
"id": "00000000-0000-0000-0000-000000000000",
"updated_at": "0001-01-01T00:00:00Z",
"value": "someone@example.com",
"via": "email"
}
],
"schema_id": "default",
"schema_url": "",
"state": "active",
"state_changed_at": "2023-08-12T08:56:25.117242092Z",
"traits": {
"email": "someone@example.com"
},
"updated_at": "0001-01-01T00:00:00Z",
"verifiable_addresses": [
{
"created_at": "0001-01-01T00:00:00Z",
"id": "00000000-0000-0000-0000-000000000000",
"status": "pending",
"updated_at": "0001-01-01T00:00:00Z",
"value": "someone@example.com",
"verified": false,
"via": "email"
}
]
}
}
Relevant configuration
`quickstart.yml`
version: '3.7'
services:
kratos-migrate:
image: oryd/kratos:v1.0.0
environment:
- DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc
volumes:
- type: volume
source: kratos-sqlite
target: /var/lib/sqlite
read_only: false
- type: bind
source: ./kratos-config
target: /etc/config/kratos
command: -c /etc/config/kratos/kratos.yml migrate sql -e --yes
restart: on-failure
networks:
- intranet
kratos:
depends_on:
- kratos-migrate
image: oryd/kratos:v1.0.0
ports:
- '4433:4433' # public
- '4434:4434' # admin
restart: unless-stopped
environment:
- DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true
- LOG_LEVEL=trace
command: serve -c /etc/config/kratos/kratos.yml --dev --watch-courier
volumes:
- type: volume
source: kratos-sqlite
target: /var/lib/sqlite
read_only: false
- type: bind
source: ./kratos-config
target: /etc/config/kratos
networks:
- intranet
mailslurper:
image: oryd/mailslurper:latest-smtps
ports:
- '4436:4436'
- '4437:4437'
networks:
- intranet
networks:
intranet:
volumes:
kratos-sqlite:
`kratos.yml`
version: v0.13.0
dsn: memory
serve:
public:
base_url: http://127.0.0.1:4433/
cors:
enabled: true
admin:
base_url: http://kratos:4434/
selfservice:
default_browser_return_url: http://127.0.0.1:8000/
allowed_return_urls:
- http://127.0.0.1:8000
methods:
password:
enabled: true
totp:
config:
issuer: Kratos
enabled: true
lookup_secret:
enabled: true
link:
enabled: true
code:
enabled: true
flows:
error:
ui_url: http://127.0.0.1:8000/users/error/
settings:
ui_url: http://127.0.0.1:8000/settings
privileged_session_max_age: 15m
required_aal: highest_available
recovery:
enabled: true
ui_url: http://127.0.0.1:8000/recovery
use: code
verification:
enabled: true
ui_url: http://127.0.0.1:8000/users/verify/
use: code
after:
default_browser_return_url: http://127.0.0.1:8000/
logout:
after:
default_browser_return_url: http://127.0.0.1:8000/users/login
login:
ui_url: http://127.0.0.1:8000/users/login
lifespan: 10m
registration:
lifespan: 10m
ui_url: http://127.0.0.1:8000/users/join/
after:
password:
hooks:
- hook: session
- hook: show_verification_ui
- hook: web_hook
config:
url: http://172.17.0.1:8000/users/
method: POST
body: base64://ZnVuY3Rpb24oY3R4KSB7IHVzZXJfaWQ6IGN0eC5pZGVudGl0eSwgZW1haWw6IGN0eC5pZGVudGl0eS50cmFpdHMuZW1haWwsIHByaW1hcnlfY2hhbm5lbDogeyBuYW1lOiBjdHguZmxvdy50cmFuc2llbnRfcGF5bG9hZCwgdmlzaWJpbGl0eTogInB1YmxpYyIgfSwgdWlfbGFuZ3VhZ2U6ICJlbiIsIGN0eDogY3R4IH0=
response:
parse: true
log:
level: debug
format: text
leak_sensitive_values: true
secrets:
cookie:
- PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
cipher:
- 32-LONG-SECRET-NOT-SECURE-AT-ALL
ciphers:
algorithm: xchacha20-poly1305
hashers:
algorithm: bcrypt
bcrypt:
cost: 8
identity:
default_schema_id: default
schemas:
- id: default
url: file:///etc/config/kratos/identity.schema.json
courier:
smtp:
connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true
Web Hook Body
The base64-encoded registration web hook body included in the kratos.yml
file above decodes to:
function(ctx) { user_id: ctx.identity, email: ctx.identity.traits.email, primary_channel: { name: ctx.flow.transient_payload, visibility: "public" }, ui_language: "en", ctx: ctx }
Registration Form
<form action="http://127.0.0.1:4433/self-service/registration?flow=aa5be409-40f2-48eb-95a8-bcae4c59bdc1" method="POST">
<div>
<label for="id_traits.email">E-Mail:</label>
<input type="text" name="traits.email" required id="id_traits.email">
</div>
<div>
<label for="id_password">Password:</label>
<input type="password" name="password" required id="id_password">
</div>
<div>
<label for="id_transient_payload.channel_name">Channel name:</label>
<input type="text" name="transient_payload.channel_name" maxlength="200" required id="id_transient_payload.channel_name">
<input type="hidden" name="csrf_token" value="uJeeOUCKxVmU9CwGM36rYNwSgVMF/p2qyNYfLLGkhhIGzK6bd4PLxZucYvmX8EL7SXz20Q6WSLJs9uuMm8vnKg==" id="id_csrf_token">
</div>
<button name="method" type="submit" value="password">Join</button>
</form>
Version
1.0.0
On which operating system are you observing this issue?
Linux
In which environment are you deploying?
Docker Compose
Additional Context
No response