snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-15 21:19:12 +05:00
commit c60cc7ca75
8092 changed files with 394160 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
{
"auth": {
"config": {
"password": "YourPass",
"user": "YourUsername"
},
"type": "basic_auth"
},
"body": "file://some.jsonnet",
"header": {
"Content-Type": "application/json"
},
"method": "POST",
"url": "https://example.com/email"
}

View File

@@ -0,0 +1,15 @@
{
"auth": {
"config": {
"password": "YourPass",
"user": "YourUsername"
},
"type": "basic_auth"
},
"body": "base64://e30=",
"header": {
"Content-Type": "application/x-www-form-urlencoded"
},
"method": "POST",
"url": "https://api.twilio.com/2010-04-01/Accounts/YourAccountID/Messages.json"
}

View File

@@ -0,0 +1 @@
null

View File

@@ -0,0 +1,19 @@
[
"Accept",
"Accept-Encoding",
"Accept-Language",
"Content-Length",
"Content-Type",
"Origin",
"Priority",
"Referer",
"Sec-Ch-Ua",
"Sec-Ch-Ua-Mobile",
"Sec-Ch-Ua-Platform",
"Sec-Fetch-Dest",
"Sec-Fetch-Mode",
"Sec-Fetch-Site",
"Sec-Fetch-User",
"True-Client-Ip",
"User-Agent"
]

View File

@@ -0,0 +1,10 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package config
var (
Version = "master"
Date = "undefined"
Commit = "undefined"
)

1732
driver/config/config.go Normal file

File diff suppressed because it is too large Load Diff

1596
driver/config/config_test.go Normal file

File diff suppressed because it is too large Load Diff

26
driver/config/handler.go Normal file
View File

@@ -0,0 +1,26 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package config
import (
"crypto/sha256"
"fmt"
"net/http"
"github.com/knadh/koanf/parsers/json"
"github.com/ory/x/httprouterx"
)
func RegisterConfigHashRoute(c Provider, router *httprouterx.RouterAdmin) {
router.GET("/health/config", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
if revision := c.Config().GetProvider(r.Context()).String("revision"); len(revision) > 0 {
_, _ = fmt.Fprintf(w, "%s", revision)
} else {
bytes, _ := c.Config().GetProvider(r.Context()).Marshal(json.Parser())
_, _ = fmt.Fprintf(w, "%x", sha256.Sum256(bytes))
}
})
}

View File

@@ -0,0 +1,70 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package config_test
import (
"context"
"io"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/pkg"
"github.com/ory/x/contextx"
"github.com/ory/x/httprouterx"
)
type configProvider struct {
cfg *config.Config
}
func (c *configProvider) Config() *config.Config {
return c.cfg
}
func TestNewConfigHashHandler(t *testing.T) {
ctx := context.Background()
cfg := pkg.NewConfigurationWithDefaults(t)
router := httprouterx.NewRouterAdmin()
config.RegisterConfigHashRoute(&configProvider{cfg: cfg}, router)
ts := contextx.NewConfigurableTestServer(router)
t.Cleanup(ts.Close)
// first request, get baseline hash
res, err := ts.Client(ctx).Get(ts.URL + "/health/config")
require.NoError(t, err)
defer func() { _ = res.Body.Close() }()
require.Equal(t, 200, res.StatusCode)
first, err := io.ReadAll(res.Body)
require.NoError(t, err)
// second request, no config change
res, err = ts.Client(ctx).Get(ts.URL + "/health/config")
require.NoError(t, err)
defer func() { _ = res.Body.Close() }()
require.Equal(t, 200, res.StatusCode)
second, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.Equal(t, first, second)
// third request, with config change
res, err = ts.Client(contextx.WithConfigValue(ctx, config.ViperKeySessionDomain, "foobar")).Get(ts.URL + "/health/config")
require.NoError(t, err)
defer func() { _ = res.Body.Close() }()
require.Equal(t, 200, res.StatusCode)
third, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.NotEqual(t, first, third)
// fourth request, no config change
res, err = ts.Client(ctx).Get(ts.URL + "/health/config")
require.NoError(t, err)
defer func() { _ = res.Body.Close() }()
require.Equal(t, 200, res.StatusCode)
fourth, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.Equal(t, first, fourth)
}

View File

@@ -0,0 +1,6 @@
selfservice:
methods:
password:
enabled: true
flows:
recovery: false

View File

@@ -0,0 +1,28 @@
selfservice:
methods:
password:
enabled: true
flows:
settings:
privileged_session_max_age: 1m
after:
hooks:
profile:
- hook: show_verification_ui
verification:
enabled: true
lifespan: 5s
after:
default_browser_return_url: http://127.0.0.1:4455/
logout:
after:
default_browser_return_url: http://127.0.0.1:4455/auth/login
identity:
default_schema_id: default
schemas:
- id: default
url: file://test/e2e/profiles/verification/identity.traits.schema.json

View File

@@ -0,0 +1,2 @@
selfservice:

View File

@@ -0,0 +1,17 @@
{
"$id": "ory://identity-invalid-schema",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "IdentityInvalidSchema",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {},
"required": [
"other",
"email"
],
"additionalProperties": true
}
}
}

View File

@@ -0,0 +1,32 @@
{
"$id": "ory://identity-other-schema",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "IdentityOtherSchema",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"other": {
"type": "string"
},
"email": {
"type": "string",
"title": "email",
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
}
},
"required": [
"other",
"email"
],
"additionalProperties": true
}
}
}

View File

@@ -0,0 +1,28 @@
{
"$id": "ory://identity-test-schema",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "IdentitySchema",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"name": {
"type": "object",
"properties": {
"first": {
"type": "string"
},
"last": {
"type": "string"
}
}
}
},
"required": [
"name"
],
"additionalProperties": true
}
}
}

View File

@@ -0,0 +1,16 @@
courier:
smtp:
connection_uri: smtp://username:password@smtp.example.com:587
channels:
- id: phone
request_config:
url: https://ory.sh
method: GET
body: base64://ZnVuY3Rpb24oY3R4KSB7CkJvZHk6IGN0eC5ib2R5LApUbzogY3R4LnRvLEZyb206IGN0eC5mcm9tCn0=
headers:
Content-Type: application/x-www-form-urlencoded
auth:
type: basic_auth
config:
user: ABC
password: DEF

View File

@@ -0,0 +1,24 @@
dsn: sqlite://foo.db?mode=memory&_fk=true
selfservice:
default_browser_return_url: https://example.com/return_to
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
courier:
delivery_strategy: http
http:
request_config:
url: https://example.com/email
body: file://some.jsonnet
header:
'Content-Type': 'application/json'
auth:
type: basic_auth
config:
user: YourUsername
password: YourPass

View File

@@ -0,0 +1,2 @@
courier:
message_retries: 10

View File

@@ -0,0 +1,30 @@
dsn: sqlite://foo.db?mode=memory&_fk=true
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
courier:
smtp:
connection_uri: smtp://stub-url
templates:
recovery:
invalid:
email:
body:
html: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu
# omit subject to verify it is validated
valid:
# omit template_root as it is not required on valid
email:
body:
plaintext: base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQp7eyAuUmVjb3ZlcnlVUkwgfX0K
html: base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4=
subject: base64://UmVjb3ZlciBhY2Nlc3MgdG8geW91ciBhY2NvdW50
# omit verification here to test templates override fallback
template_override_path: "../../courier/template/courier/builtin/templates"

View File

@@ -0,0 +1,26 @@
dsn: sqlite://foo.db?mode=memory&_fk=true
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
courier:
smtp:
connection_uri: smtp://stub-url
templates:
recovery:
invalid:
email:
body:
plaintext: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu
html: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu
verification:
valid:
email:
subject: base64://VmVyaWZpY2F0aW9uIEVtYWls
template_override_path: "../../courier/template/courier/builtin/templates"

View File

@@ -0,0 +1,30 @@
dsn: sqlite://foo.db?mode=memory&_fk=true
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
courier:
smtp:
connection_uri: smtp://stub-url
templates:
recovery:
invalid:
email:
body:
plaintext: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu
html: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu
subject: base64://QWNjb3VudCBBY2Nlc3MgQXR0ZW1wdGVk
valid:
email:
body:
plaintext: base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQp7eyAuUmVjb3ZlcnlVUkwgfX0K
html: base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4=
subject: base64://UmVjb3ZlciBhY2Nlc3MgdG8geW91ciBhY2NvdW50
# omit verification here to test templates override fallback
template_override_path: "../../courier/template/courier/builtin/templates"

View File

@@ -0,0 +1,28 @@
dsn: sqlite://foo.db?mode=memory&_fk=true
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
courier:
smtp:
connection_uri: smtp://foo:bar@baz/
sms:
enabled: true
from: '+49123456789'
request_config:
url: https://api.twilio.com/2010-04-01/Accounts/YourAccountID/Messages.json
method: POST
body: base64://e30=
header:
'Content-Type': 'application/x-www-form-urlencoded'
auth:
type: basic_auth
config:
user: YourUsername
password: YourPass

View File

@@ -0,0 +1,14 @@
dsn: sqlite://foo.db?mode=memory&_fk=true
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
identity:
default_schema_id: other
schemas:
- id: other
url: file://stub/.identity.invalid.json
courier:
smtp:
connection_uri: smtp://foo:bar@baz/

View File

@@ -0,0 +1,16 @@
dsn: sqlite://foo.db?mode=memory&_fk=true
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
identity:
default_schema_id: default
schemas:
- id: default
url: http://test.kratos.ory.sh/default-identity.schema.json
- id: other
url: http://test.kratos.ory.sh/other-identity.schema.json
courier:
smtp:
connection_uri: smtp://foo:bar@baz/

View File

@@ -0,0 +1,6 @@
selfservice:
flows:
recovery:
notify_unknown_recipients: true
verification:
notify_unknown_recipients: true

View File

@@ -0,0 +1,5 @@
oauth2_provider:
url: https://oauth2_provider/
headers:
Authorization: Basic
override_return_to: true

View File

@@ -0,0 +1,54 @@
serve:
admin:
base_url: http://admin.kratos.ory.sh
port: 1234
host: admin.kratos.ory.sh
public:
base_url: http://public.kratos.ory.sh
port: 1235
host: public.kratos.ory.sh
dsn: sqlite://foo.db?mode=memory&_fk=true
log:
level: debug
courier:
smtp:
connection_uri: smtp://foo:bar@baz/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
secrets:
cookie:
- session-key-7f8a9b77-1
cipher:
- secret-thirty-two-character-long
ciphers:
algorithm: xchacha20-poly1305
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
methods:
passkey:
enabled: true
config:
rp:
id: example.com
display_name: Example
origins:
- https://example.com
authenticator_selection:
attachment: "cross-platform"
resident_key: "discouraged"
user_verification: "required"
attestation:
preference: "direct"
timeouts:
registration: "30s"
login: "45s"

View File

@@ -0,0 +1,45 @@
serve:
admin:
base_url: http://admin.kratos.ory.sh
port: 1234
host: admin.kratos.ory.sh
public:
base_url: http://public.kratos.ory.sh
port: 1235
host: public.kratos.ory.sh
dsn: sqlite://foo.db?mode=memory&_fk=true
log:
level: debug
courier:
smtp:
connection_uri: smtp://foo:bar@baz/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
secrets:
cookie:
- session-key-7f8a9b77-1
cipher:
- secret-thirty-two-character-long
ciphers:
algorithm: xchacha20-poly1305
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
methods:
passkey:
enabled: true
config:
rp:
id: example.com
display_name: Example
origins:
- https://example.com

View File

@@ -0,0 +1,257 @@
# serve controls the configuration for the http(s) daemon
serve:
admin:
base_url: http://admin.kratos.ory.sh
port: 1234
host: admin.kratos.ory.sh
public:
base_url: http://public.kratos.ory.sh
port: 1235
host: public.kratos.ory.sh
dsn: sqlite://foo.db?mode=memory&_fk=true
log:
level: debug
courier:
smtp:
connection_uri: smtp://foo:bar@baz/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
- id: other
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktb3RoZXItc2NoZW1hIiwKICAiJHNjaGVtYSI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLAogICJ0aXRsZSI6ICJJZGVudGl0eU90aGVyU2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm90aGVyIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImVtYWlsIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJ0aXRsZSI6ICJlbWFpbCIsCiAgICAgICAgICAib3J5LnNoL2tyYXRvcyI6IHsKICAgICAgICAgICAgImNyZWRlbnRpYWxzIjogewogICAgICAgICAgICAgICJwYXNzd29yZCI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJvdGhlciIsCiAgICAgICAgImVtYWlsIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
hashers:
argon2:
memory: 1MB
iterations: 2
parallelism: 4
salt_length: 16
key_length: 32
dedicated_memory: 1GB
expected_duration: 500ms
expected_deviation: 500ms
secrets:
cookie:
- session-key-7f8a9b77-1
- session-key-7f8a9b77-2
cipher:
- secret-thirty-two-character-long
ciphers:
algorithm: xchacha20-poly1305
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
allowed_return_urls:
- http://return-to-1-test.ory.sh/
- http://return-to-2-test.ory.sh/
- http://*.wildcards.ory.sh
- http://*.sh
- http://*.com
- http://*.com.pl
- http://*
- /return-to-relative-test/
methods:
totp:
enabled: true
config:
issuer: issuer.ory.sh
password:
enabled: true
oidc:
enabled: true
config:
providers:
- id: github
provider: github
client_id: a
client_secret: b
mapper_url: http://test.kratos.ory.sh/default-identity.schema.json
webauthn:
enabled: true
config:
rp:
id: https://example.com/webauthn
display_name: Webauthn
origin: https://origin-a.example.com
origins:
- https://origin-a.example.com
- https://origin-b.example.com
- https://origin-c.example.com
flows:
error:
ui_url: http://test.kratos.ory.sh/error
logout:
after:
default_browser_return_url: http://test.kratos.ory.sh:4000/
recovery:
enabled: true
ui_url: http://test.kratos.ory.sh/recovery
lifespan: 98m
after:
default_browser_return_url: http://test.kratos.ory.sh/dashboard
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_recovery_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
verification:
enabled: true
lifespan: 97m
ui_url: http://test.kratos.ory.sh/verification
after:
default_browser_return_url: http://test.kratos.ory.sh/dashboard
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_verification_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
settings:
ui_url: http://test.kratos.ory.sh/settings
lifespan: 99m
privileged_session_max_age: 5m
after:
default_browser_return_url: https://self-service/settings/return_to
password:
default_browser_return_url: https://self-service/settings/password/return_to
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
profile:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_profile_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
login:
ui_url: http://test.kratos.ory.sh/login
lifespan: 99m
before:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/before_login_hook
method: POST
headers:
X-Custom-Header: test
after:
default_browser_return_url: https://self-service/login/return_to
password:
default_browser_return_url: https://self-service/login/password/return_to
hooks:
- hook: revoke_active_sessions
- hook: require_verified_address
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
auth:
type: basic_auth
config:
user: test-user
password: super-secret
oidc:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_oidc_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
- hook: revoke_active_sessions
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
registration:
enabled: true
ui_url: http://test.kratos.ory.sh/register
lifespan: 98m
before:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/before_registration_hook
method: GET
headers:
X-Custom-Header: test
after:
default_browser_return_url: https://self-service/registration/return_to
password:
hooks:
- hook: session
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
auth:
type: api_key
config:
name: My-Key
value: My-Key-Value
in: header
oidc:
default_browser_return_url: https://self-service/registration/oidc/return_to
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_oidc_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
- hook: session

View File

@@ -0,0 +1,253 @@
# serve controls the configuration for the http(s) daemon
serve:
admin:
base_url: http://admin.kratos.ory.sh
port: 1234
host: admin.kratos.ory.sh
public:
base_url: http://public.kratos.ory.sh
port: 1235
host: public.kratos.ory.sh
dsn: sqlite://foo.db?mode=memory&_fk=true
log:
level: debug
courier:
smtp:
connection_uri: smtp://foo:bar@baz/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
- id: other
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktb3RoZXItc2NoZW1hIiwKICAiJHNjaGVtYSI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLAogICJ0aXRsZSI6ICJJZGVudGl0eU90aGVyU2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm90aGVyIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImVtYWlsIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJ0aXRsZSI6ICJlbWFpbCIsCiAgICAgICAgICAib3J5LnNoL2tyYXRvcyI6IHsKICAgICAgICAgICAgImNyZWRlbnRpYWxzIjogewogICAgICAgICAgICAgICJwYXNzd29yZCI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJvdGhlciIsCiAgICAgICAgImVtYWlsIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
hashers:
argon2:
memory: 1MB
iterations: 2
parallelism: 4
salt_length: 16
key_length: 32
dedicated_memory: 1GB
expected_duration: 500ms
expected_deviation: 500ms
secrets:
cookie:
- session-key-7f8a9b77-1
- session-key-7f8a9b77-2
cipher:
- secret-thirty-two-character-long
ciphers:
algorithm: xchacha20-poly1305
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
allowed_return_urls:
- http://return-to-1-test.ory.sh/
- http://return-to-2-test.ory.sh/
- http://*.wildcards.ory.sh
- http://*.sh
- http://*.com
- http://*.com.pl
- http://*
- /return-to-relative-test/
methods:
totp:
enabled: true
config:
issuer: issuer.ory.sh
password:
enabled: true
oidc:
enabled: true
config:
providers:
- id: github
provider: github
client_id: a
client_secret: b
mapper_url: http://test.kratos.ory.sh/default-identity.schema.json
webauthn:
enabled: true
config:
rp:
id: https://example.com/webauthn
display_name: Webauthn
origin: https://origin-a.example.com
flows:
error:
ui_url: http://test.kratos.ory.sh/error
logout:
after:
default_browser_return_url: http://test.kratos.ory.sh:4000/
recovery:
enabled: true
ui_url: http://test.kratos.ory.sh/recovery
lifespan: 98m
after:
default_browser_return_url: http://test.kratos.ory.sh/dashboard
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_recovery_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
verification:
enabled: true
lifespan: 97m
ui_url: http://test.kratos.ory.sh/verification
after:
default_browser_return_url: http://test.kratos.ory.sh/dashboard
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_verification_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
settings:
ui_url: http://test.kratos.ory.sh/settings
lifespan: 99m
privileged_session_max_age: 5m
after:
default_browser_return_url: https://self-service/settings/return_to
password:
default_browser_return_url: https://self-service/settings/password/return_to
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
profile:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_profile_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
login:
ui_url: http://test.kratos.ory.sh/login
lifespan: 99m
before:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/before_login_hook
method: POST
headers:
X-Custom-Header: test
after:
default_browser_return_url: https://self-service/login/return_to
password:
default_browser_return_url: https://self-service/login/password/return_to
hooks:
- hook: revoke_active_sessions
- hook: require_verified_address
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
auth:
type: basic_auth
config:
user: test-user
password: super-secret
oidc:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_oidc_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
- hook: revoke_active_sessions
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
registration:
enabled: true
ui_url: http://test.kratos.ory.sh/register
lifespan: 98m
before:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/before_registration_hook
method: GET
headers:
X-Custom-Header: test
after:
default_browser_return_url: https://self-service/registration/return_to
password:
hooks:
- hook: session
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
auth:
type: api_key
config:
name: My-Key
value: My-Key-Value
in: header
oidc:
default_browser_return_url: https://self-service/registration/oidc/return_to
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_oidc_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
- hook: session

View File

@@ -0,0 +1,256 @@
# serve controls the configuration for the http(s) daemon
serve:
admin:
base_url: http://admin.kratos.ory.sh
port: 1234
host: admin.kratos.ory.sh
public:
base_url: http://public.kratos.ory.sh
port: 1235
host: public.kratos.ory.sh
dsn: sqlite://foo.db?mode=memory&_fk=true
log:
level: debug
courier:
smtp:
connection_uri: smtp://foo:bar@baz/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
- id: other
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktb3RoZXItc2NoZW1hIiwKICAiJHNjaGVtYSI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLAogICJ0aXRsZSI6ICJJZGVudGl0eU90aGVyU2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm90aGVyIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImVtYWlsIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJ0aXRsZSI6ICJlbWFpbCIsCiAgICAgICAgICAib3J5LnNoL2tyYXRvcyI6IHsKICAgICAgICAgICAgImNyZWRlbnRpYWxzIjogewogICAgICAgICAgICAgICJwYXNzd29yZCI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJvdGhlciIsCiAgICAgICAgImVtYWlsIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
hashers:
argon2:
memory: 1MB
iterations: 2
parallelism: 4
salt_length: 16
key_length: 32
dedicated_memory: 1GB
expected_duration: 500ms
expected_deviation: 500ms
secrets:
cookie:
- session-key-7f8a9b77-1
- session-key-7f8a9b77-2
cipher:
- secret-thirty-two-character-long
ciphers:
algorithm: xchacha20-poly1305
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
allowed_return_urls:
- http://return-to-1-test.ory.sh/
- http://return-to-2-test.ory.sh/
- http://*.wildcards.ory.sh
- http://*.sh
- http://*.com
- http://*.com.pl
- http://*
- /return-to-relative-test/
methods:
totp:
enabled: true
config:
issuer: issuer.ory.sh
password:
enabled: true
oidc:
enabled: true
config:
providers:
- id: github
provider: github
client_id: a
client_secret: b
mapper_url: http://test.kratos.ory.sh/default-identity.schema.json
webauthn:
enabled: true
config:
rp:
id: https://example.com/webauthn
display_name: Webauthn
origins:
- https://origin-a.example.com
- https://origin-b.example.com
- https://origin-c.example.com
flows:
error:
ui_url: http://test.kratos.ory.sh/error
logout:
after:
default_browser_return_url: http://test.kratos.ory.sh:4000/
recovery:
enabled: true
ui_url: http://test.kratos.ory.sh/recovery
lifespan: 98m
after:
default_browser_return_url: http://test.kratos.ory.sh/dashboard
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_recovery_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
verification:
enabled: true
lifespan: 97m
ui_url: http://test.kratos.ory.sh/verification
after:
default_browser_return_url: http://test.kratos.ory.sh/dashboard
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_verification_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
settings:
ui_url: http://test.kratos.ory.sh/settings
lifespan: 99m
privileged_session_max_age: 5m
after:
default_browser_return_url: https://self-service/settings/return_to
password:
default_browser_return_url: https://self-service/settings/password/return_to
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
profile:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_profile_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
login:
ui_url: http://test.kratos.ory.sh/login
lifespan: 99m
before:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/before_login_hook
method: POST
headers:
X-Custom-Header: test
after:
default_browser_return_url: https://self-service/login/return_to
password:
default_browser_return_url: https://self-service/login/password/return_to
hooks:
- hook: revoke_active_sessions
- hook: require_verified_address
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
auth:
type: basic_auth
config:
user: test-user
password: super-secret
oidc:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_oidc_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
- hook: revoke_active_sessions
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
registration:
enabled: true
ui_url: http://test.kratos.ory.sh/register
lifespan: 98m
before:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/before_registration_hook
method: GET
headers:
X-Custom-Header: test
after:
default_browser_return_url: https://self-service/registration/return_to
password:
hooks:
- hook: session
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
auth:
type: api_key
config:
name: My-Key
value: My-Key-Value
in: header
oidc:
default_browser_return_url: https://self-service/registration/oidc/return_to
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_oidc_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
- hook: session

View File

@@ -0,0 +1,254 @@
# serve controls the configuration for the http(s) daemon
serve:
admin:
base_url: http://admin.kratos.ory.sh
port: 1234
host: admin.kratos.ory.sh
public:
base_url: http://public.kratos.ory.sh
port: 1235
host: public.kratos.ory.sh
dsn: sqlite://foo.db?mode=memory&_fk=true
log:
level: debug
courier:
smtp:
connection_uri: smtp://foo:bar@baz/
identity:
default_schema_id: default
schemas:
- id: default
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
- id: other
url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktb3RoZXItc2NoZW1hIiwKICAiJHNjaGVtYSI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLAogICJ0aXRsZSI6ICJJZGVudGl0eU90aGVyU2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm90aGVyIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImVtYWlsIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJ0aXRsZSI6ICJlbWFpbCIsCiAgICAgICAgICAib3J5LnNoL2tyYXRvcyI6IHsKICAgICAgICAgICAgImNyZWRlbnRpYWxzIjogewogICAgICAgICAgICAgICJwYXNzd29yZCI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJvdGhlciIsCiAgICAgICAgImVtYWlsIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9
hashers:
argon2:
memory: 1MB
iterations: 2
parallelism: 4
salt_length: 16
key_length: 32
dedicated_memory: 1GB
expected_duration: 500ms
expected_deviation: 500ms
bcrypt:
cost: 4
secrets:
cookie:
- session-key-7f8a9b77-1
- session-key-7f8a9b77-2
cipher:
- secret-thirty-two-character-long
ciphers:
algorithm: xchacha20-poly1305
selfservice:
default_browser_return_url: http://return-to-3-test.ory.sh/
allowed_return_urls:
- http://return-to-1-test.ory.sh/
- http://return-to-2-test.ory.sh/
- http://*.wildcards.ory.sh
- http://*.sh
- http://*.com
- http://*.com.pl
- http://*
- /return-to-relative-test/
methods:
totp:
enabled: true
config:
issuer: issuer.ory.sh
password:
enabled: true
oidc:
enabled: true
config:
providers:
- id: github
provider: github
client_id: a
client_secret: b
mapper_url: http://test.kratos.ory.sh/default-identity.schema.json
webauthn:
enabled: true
config:
rp:
id: example.com
display_name: Webauthn
flows:
error:
ui_url: http://test.kratos.ory.sh/error
logout:
after:
default_browser_return_url: http://test.kratos.ory.sh:4000/
recovery:
enabled: true
ui_url: http://test.kratos.ory.sh/recovery
lifespan: 98m
after:
default_browser_return_url: http://test.kratos.ory.sh/dashboard
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_recovery_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
verification:
enabled: true
lifespan: 97m
ui_url: http://test.kratos.ory.sh/verification
after:
default_browser_return_url: http://test.kratos.ory.sh/dashboard
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_verification_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
settings:
ui_url: http://test.kratos.ory.sh/settings
lifespan: 99m
privileged_session_max_age: 5m
after:
default_browser_return_url: https://self-service/settings/return_to
password:
default_browser_return_url: https://self-service/settings/password/return_to
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
profile:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_profile_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_settings_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
login:
ui_url: http://test.kratos.ory.sh/login
lifespan: 99m
before:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/before_login_hook
method: POST
headers:
X-Custom-Header: test
after:
default_browser_return_url: https://self-service/login/return_to
password:
default_browser_return_url: https://self-service/login/password/return_to
hooks:
- hook: revoke_active_sessions
- hook: require_verified_address
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
auth:
type: basic_auth
config:
user: test-user
password: super-secret
oidc:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_oidc_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
- hook: revoke_active_sessions
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_login_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
registration:
enabled: true
ui_url: http://test.kratos.ory.sh/register
lifespan: 98m
before:
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/before_registration_hook
method: GET
headers:
X-Custom-Header: test
after:
default_browser_return_url: https://self-service/registration/return_to
password:
hooks:
- hook: session
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_password_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_global_hook
method: POST
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
auth:
type: api_key
config:
name: My-Key
value: My-Key-Value
in: header
oidc:
default_browser_return_url: https://self-service/registration/oidc/return_to
hooks:
- hook: web_hook
config:
url: https://test.kratos.ory.sh/after_registration_oidc_hook
method: GET
headers:
X-Custom-Header: test
body: /path/to/template.jsonnet
- hook: session

59
driver/factory.go Normal file
View File

@@ -0,0 +1,59 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"context"
"io"
"github.com/ory/x/servicelocatorx"
"github.com/ory/kratos/driver/config"
"github.com/ory/x/logrusx"
)
func New(ctx context.Context, stdOutOrErr io.Writer, dOpts ...RegistryOption) (*RegistryDefault, error) {
r, err := NewWithoutInit(ctx, stdOutOrErr, dOpts...)
if err != nil {
return nil, err
}
ctxter := r.Contextualizer()
if err := r.Init(ctx, ctxter, dOpts...); err != nil {
r.Logger().WithError(err).Error("Unable to initialize service registry.")
return nil, err
}
return r, nil
}
func NewWithoutInit(ctx context.Context, stdOutOrErr io.Writer, dOpts ...RegistryOption) (*RegistryDefault, error) {
opts := newOptions(dOpts)
sl := servicelocatorx.NewOptions(opts.serviceLocatorOptions...)
l := sl.Logger()
if l == nil {
l = logrusx.New("Ory Kratos", config.Version)
}
c := opts.config
if c == nil {
var err error
c, err = config.New(ctx, l, stdOutOrErr, sl.Contextualizer(), opts.configOptions...)
if err != nil {
l.WithError(err).Error("Unable to instantiate configuration.")
return nil, err
}
}
r, err := NewRegistryFromDSN(ctx, c, l)
if err != nil {
l.WithError(err).Error("Unable to instantiate service registry.")
return nil, err
}
r.slOptions = sl
r.SetContextualizer(sl.Contextualizer())
return r, nil
}

44
driver/factory_test.go Normal file
View File

@@ -0,0 +1,44 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver_test
import (
"context"
"os"
"testing"
"time"
"github.com/gofrs/uuid"
"github.com/ory/x/configx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ory/kratos/driver"
"github.com/ory/kratos/driver/config"
)
func TestDriverNew(t *testing.T) {
ctx := context.Background()
r, err := driver.New(
context.Background(),
os.Stderr,
driver.WithConfigOptions(
configx.WithValue(config.ViperKeyDSN, config.DefaultSQLiteMemoryDSN),
configx.SkipValidation(),
))
require.NoError(t, err)
assert.EqualValues(t, config.DefaultSQLiteMemoryDSN, r.Config().DSN(ctx))
pingCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
t.Cleanup(cancel)
require.NoError(t, r.Persister().Ping(pingCtx))
assert.NotEqual(t, uuid.Nil.String(), r.Persister().NetworkID(context.Background()).String())
n, err := r.Persister().DetermineNetwork(context.Background())
require.NoError(t, err)
assert.Equal(t, r.Persister().NetworkID(context.Background()), n.ID)
}

313
driver/registry.go Normal file
View File

@@ -0,0 +1,313 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"context"
"io/fs"
"github.com/gorilla/sessions"
"github.com/ory/x/httpx"
"github.com/ory/kratos/cipher"
"github.com/ory/kratos/continuity"
"github.com/ory/kratos/courier"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/hash"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/persistence"
"github.com/ory/kratos/schema"
"github.com/ory/kratos/selfservice/errorx"
"github.com/ory/kratos/selfservice/flow/login"
"github.com/ory/kratos/selfservice/flow/logout"
"github.com/ory/kratos/selfservice/flow/recovery"
"github.com/ory/kratos/selfservice/flow/registration"
"github.com/ory/kratos/selfservice/flow/settings"
"github.com/ory/kratos/selfservice/flow/verification"
"github.com/ory/kratos/selfservice/sessiontokenexchange"
"github.com/ory/kratos/selfservice/strategy/code"
"github.com/ory/kratos/selfservice/strategy/link"
password2 "github.com/ory/kratos/selfservice/strategy/password"
"github.com/ory/kratos/session"
"github.com/ory/kratos/x"
"github.com/ory/kratos/x/nosurfx"
"github.com/ory/nosurf"
"github.com/ory/pop/v6"
"github.com/ory/x/clock"
"github.com/ory/x/configx"
"github.com/ory/x/contextx"
"github.com/ory/x/healthx"
"github.com/ory/x/httprouterx"
"github.com/ory/x/jsonnetsecure"
"github.com/ory/x/logrusx"
"github.com/ory/x/otelx"
"github.com/ory/x/popx"
"github.com/ory/x/servicelocatorx"
)
type Registry interface {
Init(ctx context.Context, ctxer contextx.Contextualizer, opts ...RegistryOption) error
SetLogger(l *logrusx.Logger)
SetJSONNetVMProvider(jsonnetsecure.VMProvider)
// Clock returns the time source used for time-dependent behavior such as
// flow expiry. Tests can override it via RegistryDefault.SetClock.
Clock() clock.Clock
WithCSRFHandler(c nosurf.Handler)
WithCSRFTokenGenerator(cg nosurfx.CSRFToken)
HealthHandler(ctx context.Context) *healthx.Handler
CookieManager(ctx context.Context) sessions.StoreExact
ContinuityCookieManager(ctx context.Context) sessions.StoreExact
RegisterPublicRoutes(ctx context.Context, public *httprouterx.RouterPublic)
RegisterAdminRoutes(ctx context.Context, admin *httprouterx.RouterAdmin)
Tracer(context.Context) *otelx.Tracer
SetTracer(*otelx.Tracer)
config.Provider
CourierConfig() config.CourierConfigs
SetConfig(c *config.Config)
SetContextualizer(ctxer contextx.Contextualizer)
nosurfx.CSRFProvider
httpx.WriterProvider
logrusx.Provider
httpx.ClientProvider
jsonnetsecure.VMProvider
continuity.ManagementProvider
continuity.PersistenceProvider
cipher.Provider
courier.Provider
persistence.Provider
errorx.ManagementProvider
errorx.HandlerProvider
errorx.PersistenceProvider
hash.HashProvider
identity.HandlerProvider
identity.ValidationProvider
identity.PoolProvider
identity.PrivilegedPoolProvider
identity.ManagementProvider
identity.ActiveCredentialsCounterStrategyProvider
courier.HandlerProvider
courier.PersistenceProvider
schema.HandlerProvider
schema.IdentitySchemaProvider
password2.ValidationProvider
session.HandlerProvider
session.ManagementProvider
session.PersistenceProvider
session.TokenizerProvider
settings.HandlerProvider
settings.ErrorHandlerProvider
settings.FlowPersistenceProvider
settings.StrategyProvider
login.FlowPersistenceProvider
login.ErrorHandlerProvider
login.HooksProvider
login.HookExecutorProvider
login.HandlerProvider
login.StrategyProvider
logout.HandlerProvider
registration.FlowPersistenceProvider
registration.ErrorHandlerProvider
registration.HooksProvider
registration.HookExecutorProvider
registration.HandlerProvider
registration.StrategyProvider
verification.FlowPersistenceProvider
verification.ErrorHandlerProvider
verification.HandlerProvider
verification.StrategyProvider
sessiontokenexchange.PersistenceProvider
link.SenderProvider
link.VerificationTokenPersistenceProvider
link.RecoveryTokenPersistenceProvider
code.SenderProvider
code.RecoveryCodePersistenceProvider
recovery.FlowPersistenceProvider
recovery.ErrorHandlerProvider
recovery.HandlerProvider
recovery.StrategyProvider
nosurfx.CSRFTokenGeneratorProvider
}
func NewRegistryFromDSN(ctx context.Context, c *config.Config, l *logrusx.Logger) (*RegistryDefault, error) {
reg := NewRegistryDefault()
tracer, err := otelx.New("Ory Kratos", l, c.Tracing(ctx))
if err != nil {
l.WithError(err).Fatalf("failed to initialize tracer")
tracer = otelx.NewNoop()
}
reg.SetTracer(tracer)
reg.SetLogger(l)
reg.SetConfig(c)
return reg, nil
}
type options struct {
skipNetworkInit bool
config *config.Config
configOptions []configx.OptionModifier
replaceTracer func(*otelx.Tracer) *otelx.Tracer
replaceIdentitySchemaProvider func(Registry) schema.IdentitySchemaProvider
inspect func(Registry) error
extraMigrations []fs.FS
extraGoMigrations popx.Migrations
replacementStrategies []NewStrategy
extraHooks map[string]NewHookFn
extraHashers map[string]NewHasherFn
extraHandlers []NewHandler
disableMigrationLogging bool
jsonnetPool jsonnetsecure.Pool
serviceLocatorOptions []servicelocatorx.Option
dbOpts []func(details *pop.ConnectionDetails)
}
type RegistryOption func(*options)
// WithDBOptions adds database connection options that will be applied to the
// underlying connection.
func WithDBOptions(opts ...func(details *pop.ConnectionDetails)) RegistryOption {
return func(o *options) {
o.dbOpts = append(o.dbOpts, opts...)
}
}
func SkipNetworkInit(o *options) {
o.skipNetworkInit = true
}
func WithJsonnetPool(pool jsonnetsecure.Pool) RegistryOption {
return func(o *options) {
o.jsonnetPool = pool
}
}
func WithConfig(config *config.Config) RegistryOption {
return func(o *options) {
o.config = config
}
}
func WithConfigOptions(opts ...configx.OptionModifier) RegistryOption {
return func(o *options) {
o.configOptions = append(o.configOptions, opts...)
}
}
func WithIdentitySchemaProvider(f func(r Registry) schema.IdentitySchemaProvider) RegistryOption {
return func(o *options) {
o.replaceIdentitySchemaProvider = f
}
}
func ReplaceTracer(f func(*otelx.Tracer) *otelx.Tracer) RegistryOption {
return func(o *options) {
o.replaceTracer = f
}
}
type NewStrategy func(deps any) any
// WithReplaceStrategies adds a strategy to the registry. This is useful if you want to
// add a custom strategy to the registry. Default strategies with the same
// name/ID will be overwritten.
func WithReplaceStrategies(s ...NewStrategy) RegistryOption {
return func(o *options) {
o.replacementStrategies = append(o.replacementStrategies, s...)
}
}
type NewHookFn func(config.SelfServiceHook, Registry) any
func WithExtraHooks(hooks map[string]NewHookFn) RegistryOption {
return func(o *options) {
o.extraHooks = hooks
}
}
type NewHasherFn func(reg Registry) hash.Hasher
// WithExtraHashers registers additional password hashers, keyed by the
// hashers.algorithm value that selects them. An extra hasher takes precedence
// over a built-in hasher with the same name.
func WithExtraHashers(hashers map[string]NewHasherFn) RegistryOption {
return func(o *options) {
o.extraHashers = hashers
}
}
type NewHandler func(deps any) x.Handler
func WithExtraHandlers(handlers ...NewHandler) RegistryOption {
return func(o *options) {
o.extraHandlers = handlers
}
}
func Inspect(f func(reg Registry) error) RegistryOption {
return func(o *options) {
o.inspect = f
}
}
func WithExtraMigrations(m ...fs.FS) RegistryOption {
return func(o *options) {
o.extraMigrations = append(o.extraMigrations, m...)
}
}
func WithExtraGoMigrations(m ...popx.Migration) RegistryOption {
return func(o *options) {
o.extraGoMigrations = append(o.extraGoMigrations, m...)
}
}
func WithDisabledMigrationLogging() RegistryOption {
return func(o *options) {
o.disableMigrationLogging = true
}
}
func WithServiceLocatorOptions(opts ...servicelocatorx.Option) RegistryOption {
return func(o *options) {
o.serviceLocatorOptions = append(o.serviceLocatorOptions, opts...)
}
}
func newOptions(os []RegistryOption) *options {
o := new(options)
for _, f := range os {
f(o)
}
return o
}

978
driver/registry_default.go Normal file
View File

@@ -0,0 +1,978 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"context"
"crypto/sha256"
"net/http"
"strings"
"sync"
"testing"
"time"
"github.com/cenkalti/backoff"
"github.com/dgraph-io/ristretto/v2"
"github.com/gofrs/uuid"
"github.com/gorilla/sessions"
"github.com/hashicorp/go-retryablehttp"
"github.com/lestrrat-go/jwx/jwk"
"github.com/pkg/errors"
"github.com/urfave/negroni"
"github.com/ory/herodot"
"github.com/ory/kratos/cipher"
"github.com/ory/kratos/continuity"
"github.com/ory/kratos/courier"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/hash"
"github.com/ory/kratos/hydra"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/persistence"
"github.com/ory/kratos/persistence/sql"
"github.com/ory/kratos/schema"
"github.com/ory/kratos/selfservice/errorx"
"github.com/ory/kratos/selfservice/flow"
"github.com/ory/kratos/selfservice/flow/login"
"github.com/ory/kratos/selfservice/flow/logout"
"github.com/ory/kratos/selfservice/flow/recovery"
"github.com/ory/kratos/selfservice/flow/registration"
"github.com/ory/kratos/selfservice/flow/settings"
"github.com/ory/kratos/selfservice/flow/verification"
"github.com/ory/kratos/selfservice/hook"
"github.com/ory/kratos/selfservice/strategy/code"
"github.com/ory/kratos/selfservice/strategy/idfirst"
"github.com/ory/kratos/selfservice/strategy/link"
"github.com/ory/kratos/selfservice/strategy/lookup"
"github.com/ory/kratos/selfservice/strategy/oidc"
"github.com/ory/kratos/selfservice/strategy/passkey"
"github.com/ory/kratos/selfservice/strategy/password"
"github.com/ory/kratos/selfservice/strategy/profile"
"github.com/ory/kratos/selfservice/strategy/totp"
"github.com/ory/kratos/selfservice/strategy/webauthn"
"github.com/ory/kratos/session"
"github.com/ory/kratos/x"
"github.com/ory/kratos/x/nosurfx"
"github.com/ory/kratos/x/transaction"
"github.com/ory/kratos/x/webauthnx"
"github.com/ory/nosurf"
"github.com/ory/pop/v6"
"github.com/ory/x/clock"
"github.com/ory/x/contextx"
"github.com/ory/x/dbal"
"github.com/ory/x/healthx"
"github.com/ory/x/httprouterx"
"github.com/ory/x/httpx"
"github.com/ory/x/jsonnetsecure"
"github.com/ory/x/jwksx"
"github.com/ory/x/logrusx"
"github.com/ory/x/otelx"
"github.com/ory/x/popx"
"github.com/ory/x/prometheusx"
"github.com/ory/x/servicelocatorx"
"github.com/ory/x/sqlcon"
"github.com/ory/x/sqlxx"
)
type RegistryDefault struct {
l *logrusx.Logger
c *config.Config
// clock is the time source for time-dependent behavior such as flow expiry.
// It is set eagerly in initCheapMembers and replaced in tests via SetClock.
clock clock.Clock
ctxer contextx.Contextualizer
injectedSelfserviceHooks map[string]NewHookFn
extraHandlerFactories []NewHandler
extraHandlers []x.Handler
slOptions *servicelocatorx.Options
nosurf nosurf.Handler
trc *otelx.Tracer
writer herodot.Writer
healthxHandler *healthx.Handler
persister persistence.Persister
migrationStatus popx.MigrationStatuses
hookVerifier *hook.Verifier
hookSessionIssuer *hook.SessionIssuer
hookSessionDestroyer *hook.SessionDestroyer
hookAddressVerifier *hook.AddressVerifier
hookShowVerificationUI *hook.ShowVerificationUIHook
hookVerifyNewAddress *hook.VerifyNewAddress
identityHandler *identity.Handler
identityValidator *identity.Validator
identityManager *identity.Manager
identitySchemaProvider schema.IdentitySchemaProvider
courierHandler *courier.Handler
continuityManager *continuity.Manager
schemaHandler *schema.Handler
sessionHandler *session.Handler
sessionManager session.Manager
sessionTokenizer initOnce[*session.Tokenizer]
passwordHasher initOnce[hash.Hasher]
extraHashers map[string]NewHasherFn
passwordValidator initOnce[password.Validator]
crypter initOnce[cipher.Cipher]
errorHandler *errorx.Handler
errorManager *errorx.Manager
selfserviceRegistrationExecutor *registration.HookExecutor
selfserviceRegistrationHandler *registration.Handler
seflserviceRegistrationErrorHandler *registration.ErrorHandler
selfserviceRegistrationRequestErrorHandler *registration.ErrorHandler
selfserviceLoginExecutor *login.HookExecutor
selfserviceLoginHandler *login.Handler
selfserviceLoginRequestErrorHandler *login.ErrorHandler
selfserviceSettingsHandler *settings.Handler
selfserviceSettingsErrorHandler *settings.ErrorHandler
selfserviceSettingsExecutor *settings.HookExecutor
selfserviceVerifyErrorHandler *verification.ErrorHandler
selfserviceVerifyManager *identity.Manager
selfserviceVerifyHandler *verification.Handler
selfserviceVerificationExecutor *verification.HookExecutor
selfserviceLinkSender *link.Sender
selfserviceCodeSender *code.Sender
selfserviceRecoveryErrorHandler *recovery.ErrorHandler
selfserviceRecoveryHandler *recovery.Handler
selfserviceRecoveryExecutor *recovery.HookExecutor
selfserviceLogoutHandler *logout.Handler
selfserviceStrategies []any
replacementSelfserviceStrategies []NewStrategy
hydra initOnce[hydra.Hydra]
csrfTokenGenerator nosurfx.CSRFToken
jsonnetVMProvider initOnce[jsonnetsecure.VMProvider]
jsonnetPool jsonnetsecure.Pool
jwkFetcher initOnce[*jwksx.FetcherNext]
}
func (m *RegistryDefault) JsonnetVM(ctx context.Context) (jsonnetsecure.VM, error) {
return m.jsonnetVMProvider.Get(func() jsonnetsecure.VMProvider {
return &jsonnetsecure.DefaultProvider{Subcommand: "jsonnet", Pool: m.jsonnetPool}
}).JsonnetVM(ctx)
}
func (m *RegistryDefault) Audit() *logrusx.Logger {
return m.Logger().WithField("audience", "audit")
}
func (m *RegistryDefault) RegisterPublicRoutes(ctx context.Context, router *httprouterx.RouterPublic) {
for _, h := range m.ExtraHandlers() {
h.RegisterPublicRoutes(router)
}
m.LoginHandler().RegisterPublicRoutes(router)
m.RegistrationHandler().RegisterPublicRoutes(router)
m.LogoutHandler().RegisterPublicRoutes(router)
m.SettingsHandler().RegisterPublicRoutes(router)
m.IdentityHandler().RegisterPublicRoutes(router)
m.CourierHandler().RegisterPublicRoutes(router)
m.SessionHandler().RegisterPublicRoutes(router)
m.SelfServiceErrorHandler().RegisterPublicRoutes(router)
m.SchemaHandler().RegisterPublicRoutes(router)
m.RecoveryHandler().RegisterPublicRoutes(router)
m.VerificationHandler().RegisterPublicRoutes(router)
m.HealthHandler(ctx).SetHealthRoutes(router, false)
webauthnx.RegisterWebauthnRoute(router)
for _, s := range m.selfServiceStrategies() {
s, ok := s.(x.PublicHandler)
if ok {
s.RegisterPublicRoutes(router)
}
}
}
func (m *RegistryDefault) RegisterAdminRoutes(ctx context.Context, router *httprouterx.RouterAdmin) {
for _, h := range m.ExtraHandlers() {
h.RegisterAdminRoutes(router)
}
m.RegistrationHandler().RegisterAdminRoutes(router)
m.LoginHandler().RegisterAdminRoutes(router)
m.LogoutHandler().RegisterAdminRoutes(router)
m.SchemaHandler().RegisterAdminRoutes(router)
m.SettingsHandler().RegisterAdminRoutes(router)
m.IdentityHandler().RegisterAdminRoutes(router)
m.CourierHandler().RegisterAdminRoutes(router)
m.SelfServiceErrorHandler().RegisterAdminRoutes(router)
m.RecoveryHandler().RegisterAdminRoutes(router)
m.SessionHandler().RegisterAdminRoutes(router)
m.VerificationHandler().RegisterAdminRoutes(router)
m.HealthHandler(ctx).SetHealthRoutes(router, true)
m.HealthHandler(ctx).SetVersionRoutes(router)
router.GET(prometheusx.MetricsPrometheusPath, prometheusx.Handler().ServeHTTP)
config.RegisterConfigHashRoute(m, router)
for _, s := range m.selfServiceStrategies() {
s, ok := s.(x.AdminHandler)
if ok {
s.RegisterAdminRoutes(router)
}
}
}
func (m *RegistryDefault) HTTPMiddlewares() []negroni.Handler {
return m.slOptions.HTTPMiddlewares()
}
func NewRegistryDefault() *RegistryDefault {
r := &RegistryDefault{
trc: otelx.NewNoop(),
slOptions: &servicelocatorx.Options{},
}
r.initCheapMembers()
return r
}
func (m *RegistryDefault) SetLogger(l *logrusx.Logger) {
m.l = l
}
// Clock returns the registry's time source. It is eagerly initialized in
// initCheapMembers, so this getter never lazily initializes.
func (m *RegistryDefault) Clock() clock.Clock {
return m.clock
}
// SetClock replaces the registry's time source. Intended for tests that need
// deterministic control over time-dependent behavior such as flow expiry.
func (m *RegistryDefault) SetClock(c clock.Clock) {
m.clock = c
}
func (m *RegistryDefault) SetJSONNetVMProvider(p jsonnetsecure.VMProvider) {
m.jsonnetVMProvider.Set(p)
}
func (m *RegistryDefault) LogoutHandler() *logout.Handler {
if m.selfserviceLogoutHandler == nil {
m.selfserviceLogoutHandler = logout.NewHandler(m)
}
return m.selfserviceLogoutHandler
}
func (m *RegistryDefault) HealthHandler(_ context.Context) *healthx.Handler {
if m.healthxHandler == nil {
m.healthxHandler = healthx.NewHandler(m.Writer(), config.Version,
healthx.ReadyCheckers{
"database": func(r *http.Request) error {
return m.PingContext(r.Context())
},
"migrations": func(r *http.Request) error {
if m.migrationStatus != nil && !m.migrationStatus.HasPending() {
return nil
}
status, err := m.Persister().MigrationStatus(r.Context())
if err != nil {
return err
}
if status.HasPending() {
return errors.Errorf("migrations have not yet been fully applied")
}
m.migrationStatus = status
return nil
},
})
}
return m.healthxHandler
}
func (m *RegistryDefault) WithCSRFHandler(c nosurf.Handler) {
m.nosurf = c
}
func (m *RegistryDefault) CSRFHandler() nosurf.Handler {
if m.nosurf == nil {
panic("csrf handler is not set")
}
return m.nosurf
}
func (m *RegistryDefault) Config() *config.Config {
if m.c == nil {
panic("configuration not set")
}
return m.c
}
func (m *RegistryDefault) CourierConfig() config.CourierConfigs {
return m.Config()
}
func (m *RegistryDefault) selfServiceStrategies() []any {
if len(m.selfserviceStrategies) == 0 {
if m.replacementSelfserviceStrategies != nil {
// Construct self-service strategies from the replacements
for _, newStrategy := range m.replacementSelfserviceStrategies {
m.selfserviceStrategies = append(m.selfserviceStrategies, newStrategy(m))
}
} else {
// Construct the default list of strategies
m.selfserviceStrategies = []any{
profile.NewStrategy(m), // <- should remain first
password.NewStrategy(m),
oidc.NewStrategy(m),
code.NewStrategy(m),
link.NewStrategy(m),
totp.NewStrategy(m),
passkey.NewStrategy(m),
webauthn.NewStrategy(m),
lookup.NewStrategy(m),
idfirst.NewStrategy(m),
}
}
}
return m.selfserviceStrategies
}
func (m *RegistryDefault) strategyRegistrationEnabled(ctx context.Context, id string) bool {
if id == "profile" {
return true
}
return m.Config().SelfServiceStrategy(ctx, id).Enabled
}
func (m *RegistryDefault) strategyLoginEnabled(ctx context.Context, id string) bool {
return m.Config().SelfServiceStrategy(ctx, id).Enabled
}
func (m *RegistryDefault) RegistrationStrategies(ctx context.Context, filters ...registration.StrategyFilter) (registrationStrategies registration.Strategies) {
nextStrategy:
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(registration.Strategy); ok {
for _, filter := range filters {
if !filter(s) {
continue nextStrategy
}
}
if m.strategyRegistrationEnabled(ctx, s.ID().String()) || supportsOrganizations(strategy) {
registrationStrategies = append(registrationStrategies, s)
}
}
}
return
}
func (m *RegistryDefault) AllRegistrationStrategies() registration.Strategies {
var registrationStrategies []registration.Strategy
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(registration.Strategy); ok {
registrationStrategies = append(registrationStrategies, s)
}
}
return registrationStrategies
}
func (m *RegistryDefault) LoginStrategies(ctx context.Context, filters ...login.StrategyFilter) (loginStrategies login.Strategies) {
nextStrategy:
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(login.Strategy); ok {
for _, filter := range filters {
if !filter(s) {
continue nextStrategy
}
}
if m.strategyLoginEnabled(ctx, s.ID().String()) || supportsOrganizations(strategy) {
loginStrategies = append(loginStrategies, s)
}
}
}
return
}
// TestStrategy returns the OIDC strategy as a login.TestStrategy for
// admin-created test login flows. Only the OIDC strategy implements the
// test-mode single-provider UI. Returns nil if the OIDC strategy is not
// registered or disabled in config; callers must handle that case as a
// misconfiguration rather than crashing the process.
//
// The strategy list may contain several strategies that implement the test
// interface: derivatives of the OIDC strategy registered under a different
// credentials type (e.g. SAML or organization sign-in) inherit it through
// embedding, but they resolve providers from their own method's config
// section. Only the strategy with the OIDC credentials type reads
// selfservice.methods.oidc.config.providers, so select by credentials type
// rather than returning the first match.
func (m *RegistryDefault) TestStrategy(ctx context.Context) login.TestStrategy {
type testStrategy interface {
login.TestStrategy
login.Strategy
}
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(testStrategy); ok {
if s.ID() == identity.CredentialsTypeOIDC && m.strategyLoginEnabled(ctx, s.ID().String()) {
return s
}
}
}
return nil
}
// supportsOrganizations checks if a strategy implements organization-based authentication.
// Organization strategies manage their own enablement via provider configuration,
// not via the strategy-enabled config flag, so they bypass the strategyLoginEnabled /
// strategyRegistrationEnabled check.
func supportsOrganizations(s any) bool {
o, ok := s.(flow.OrganizationImplementor)
return ok && o.SupportsOrganizations()
}
func (m *RegistryDefault) AllLoginStrategies() login.Strategies {
var loginStrategies []login.Strategy
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(login.Strategy); ok {
loginStrategies = append(loginStrategies, s)
}
}
return loginStrategies
}
func (m *RegistryDefault) ActiveCredentialsCounterStrategies(_ context.Context) (activeCredentialsCounterStrategies []identity.ActiveCredentialsCounter) {
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(identity.ActiveCredentialsCounter); ok {
activeCredentialsCounterStrategies = append(activeCredentialsCounterStrategies, s)
}
}
return
}
func (m *RegistryDefault) IdentityValidator() *identity.Validator {
return m.identityValidator
}
func (m *RegistryDefault) SetConfig(c *config.Config) {
m.c = c
}
// WithSelfserviceStrategies is only available in testing and overrides the
// selfservice strategies with the given ones.
func (m *RegistryDefault) WithSelfserviceStrategies(t testing.TB, strategies []any) Registry {
if t == nil {
panic("Passing selfservice strategies is only supported in testing")
}
m.selfserviceStrategies = strategies
return m
}
func (m *RegistryDefault) Writer() herodot.Writer {
if m.writer == nil {
h := herodot.NewJSONWriter(m.Logger())
m.writer = h
}
return m.writer
}
func (m *RegistryDefault) Logger() *logrusx.Logger {
if m.l == nil {
m.l = logrusx.New("Ory Kratos", config.Version)
}
return m.l
}
func (m *RegistryDefault) IdentityHandler() *identity.Handler {
if m.identityHandler == nil {
m.identityHandler = identity.NewHandler(m)
}
return m.identityHandler
}
func (m *RegistryDefault) CourierHandler() *courier.Handler {
if m.courierHandler == nil {
m.courierHandler = courier.NewHandler(m)
}
return m.courierHandler
}
func (m *RegistryDefault) SchemaHandler() *schema.Handler {
if m.schemaHandler == nil {
m.schemaHandler = schema.NewHandler(m)
}
return m.schemaHandler
}
func (m *RegistryDefault) SessionHandler() *session.Handler {
if m.sessionHandler == nil {
m.sessionHandler = session.NewHandler(m)
}
return m.sessionHandler
}
func (m *RegistryDefault) Cipher(ctx context.Context) cipher.Cipher {
return m.crypter.Get(func() cipher.Cipher {
switch m.c.CipherAlgorithm(ctx) {
case "xchacha20-poly1305":
return cipher.NewCryptChaCha20(m.Config())
case "aes":
return cipher.NewCryptAES(m.Config())
default:
m.l.Logger.Warning("No encryption configuration found. The default algorithm (noop) will be used, resulting in sensitive data being stored in plaintext")
return cipher.NewNoop()
}
})
}
// WithHashers registers additional password hashers, keyed by the
// hashers.algorithm value that selects them. See WithExtraHashers.
func (m *RegistryDefault) WithHashers(hashers map[string]NewHasherFn) {
m.extraHashers = hashers
}
func (m *RegistryDefault) Hasher(ctx context.Context) hash.Hasher {
return m.passwordHasher.Get(func() hash.Hasher {
alg := m.c.HasherPasswordHashingAlgorithm(ctx)
if newHasher, ok := m.extraHashers[alg]; ok {
return newHasher(m)
}
if alg == "bcrypt" {
return hash.NewHasherBcrypt(m)
}
return hash.NewHasherArgon2(m)
})
}
func (m *RegistryDefault) PasswordValidator() password.Validator {
m.passwordValidator.Get(func() password.Validator {
val, err := password.NewDefaultPasswordValidatorStrategy(m)
if err != nil {
m.Logger().WithError(err).Fatal("could not initialize DefaultPasswordValidator")
}
return val
})
return m.passwordValidator.value
}
func (m *RegistryDefault) SelfServiceErrorHandler() *errorx.Handler {
if m.errorHandler == nil {
m.errorHandler = errorx.NewHandler(m)
}
return m.errorHandler
}
// cookieStoreKeyPairs derives the (hashKey, blockKey) pairs securecookie
// expects: each secret signs, and its SHA-256 digest encrypts. This way a
// single configured secret yields both signing and encryption, and rotation
// rotates both.
func cookieStoreKeyPairs(secrets [][]byte) [][]byte {
keys := make([][]byte, 0, 2*len(secrets))
for _, k := range secrets {
encrypt := sha256.Sum256(k)
keys = append(keys, k, encrypt[:])
}
return keys
}
func (m *RegistryDefault) CookieManager(ctx context.Context) sessions.StoreExact {
cs := sessions.NewCookieStore(cookieStoreKeyPairs(m.Config().SecretsSession(ctx))...)
cs.Options.Secure = m.Config().SessionCookieSecure(ctx)
cs.Options.HttpOnly = true
if domain := m.Config().SessionDomain(ctx); domain != "" {
cs.Options.Domain = domain
}
if path := m.Config().SessionPath(ctx); path != "" {
cs.Options.Path = path
}
if sameSite := m.Config().SessionSameSiteMode(ctx); sameSite != 0 {
cs.Options.SameSite = sameSite
}
cs.Options.MaxAge = 0
if m.Config().SessionPersistentCookie(ctx) {
cs.Options.MaxAge = int(m.Config().SessionLifespan(ctx).Seconds())
cs.MaxAge(cs.Options.MaxAge)
}
return cs
}
func (m *RegistryDefault) ContinuityCookieManager(ctx context.Context) sessions.StoreExact {
// To support hot reloading, this can not be instantiated only once.
secrets := m.Config().SecretsSession(ctx)
// Cookies issued before the derived-block-key fix used the raw secrets as
// (hashKey, blockKey) pairs. Appending the raw secrets after the derived
// pairs keeps those cookies readable as a decode fallback (encoding always
// uses the first pair). Remove once cookies issued by pre-fix versions
// have expired.
cs := sessions.NewCookieStore(append(cookieStoreKeyPairs(secrets), secrets...)...)
cs.Options.Secure = m.Config().CookieSecure(ctx)
cs.Options.HttpOnly = true
cs.Options.SameSite = http.SameSiteLaxMode
return cs
}
func (m *RegistryDefault) Tracer(context.Context) *otelx.Tracer {
if m.trc == nil {
return otelx.NewNoop()
}
return m.trc
}
func (m *RegistryDefault) SetTracer(t *otelx.Tracer) {
m.trc = t
}
func (m *RegistryDefault) SessionManager() session.Manager {
return m.sessionManager
}
func (m *RegistryDefault) Hydra() hydra.Hydra {
return m.hydra.Get(func() hydra.Hydra {
return hydra.NewDefaultHydra(m)
})
}
func (m *RegistryDefault) SetHydra(h hydra.Hydra) {
m.hydra.Set(h)
}
func (m *RegistryDefault) SelfServiceErrorManager() *errorx.Manager {
return m.errorManager
}
func (m *RegistryDefault) Init(ctx context.Context, ctxer contextx.Contextualizer, opts ...RegistryOption) error {
if m.persister != nil {
// The DSN connection can not be hot-reloaded!
panic("RegistryDefault.Init() must not be called more than once.")
}
o := newOptions(opts)
m.jsonnetPool = o.jsonnetPool
if o.replaceTracer != nil {
m.trc = o.replaceTracer(m.trc)
}
if o.replacementStrategies != nil {
m.replacementSelfserviceStrategies = o.replacementStrategies
}
if o.extraHooks != nil {
m.WithHooks(o.extraHooks)
}
if o.extraHashers != nil {
m.WithHashers(o.extraHashers)
}
if o.extraHandlers != nil {
m.WithExtraHandlers(o.extraHandlers)
}
if o.replaceIdentitySchemaProvider != nil {
m.identitySchemaProvider = o.replaceIdentitySchemaProvider(m)
}
bc := backoff.NewExponentialBackOff()
bc.MaxElapsedTime = time.Minute * 5
bc.Reset()
err := backoff.Retry(func() error {
m.SetContextualizer(ctxer)
pool, idlePool, connMaxLifetime, connMaxIdleTime, cleanedDSN := sqlcon.ParseConnectionOptions(m.l, m.Config().DSN(ctx))
dbOpts := &pop.ConnectionDetails{
URL: sqlcon.FinalizeDSN(m.l, cleanedDSN),
IdlePool: idlePool,
ConnMaxLifetime: connMaxLifetime,
ConnMaxIdleTime: connMaxIdleTime,
Pool: pool,
TracerProvider: m.Tracer(ctx).Provider(),
}
for _, f := range o.dbOpts {
f(dbOpts)
}
scheme, _, _ := sqlxx.ExtractSchemeFromDSN(dbOpts.URL)
if !dbOpts.AllowMinPool && scheme == "postgres" && strings.Contains(dbOpts.URL, "pool_min_conns=") {
err := errors.Errorf("attempting to use the option 'pool_min_conns' with Postgres, but the pgxpool connection pool is disabled, this will be rejected by the database: dsn=%s dbOpts.AllowMinPool=%v", dbOpts.URL, dbOpts.AllowMinPool)
return backoff.Permanent(err)
}
if (scheme == "sqlite" || scheme == "mysql") && strings.Contains(dbOpts.URL, "pool_min_conns=") {
err := errors.Errorf("attempting to use the option 'pool_min_conns' with %s, but connection pooling is not supported for this case: dsn=%s", scheme, dbOpts.URL)
return backoff.Permanent(err)
}
m.Logger().
WithField("pool", pool).
WithField("idlePool", idlePool).
WithField("connMaxLifetime", connMaxLifetime).
Debug("Connecting to SQL Database")
c, err := pop.NewConnection(dbOpts)
if err != nil {
m.Logger().WithError(err).Warnf("Unable to connect to database, retrying.")
return errors.WithStack(err)
}
if err := c.Open(); err != nil {
m.Logger().WithError(err).Warnf("Unable to open database, retrying.")
return errors.WithStack(err)
}
p, err := sql.NewPersister(m, c,
sql.WithExtraMigrations(o.extraMigrations...),
sql.WithExtraGoMigrations(o.extraGoMigrations...),
sql.WithDisabledLogging(o.disableMigrationLogging))
if err != nil {
m.Logger().WithError(err).Warnf("Unable to initialize persister, retrying.")
return err
}
pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
if err := c.Store.SQLDB().PingContext(pingCtx); err != nil {
m.Logger().WithError(err).Warnf("Unable to ping database, retrying.")
return err
}
// if dsn is memory we have to run the migrations on every start
if dbal.IsMemorySQLite(m.Config().DSN(ctx)) || m.Config().DSN(ctx) == "memory" {
m.Logger().Infoln("Ory Kratos is running migrations on every startup as DSN is memory. This means your data is lost when Kratos terminates.")
if err := p.MigrateUp(ctx); err != nil {
m.Logger().WithError(err).Warnf("Unable to run migrations, retrying.")
return err
}
}
if o.skipNetworkInit {
m.persister = p
return nil
}
net, err := p.DetermineNetwork(ctx)
if err != nil {
m.Logger().WithError(err).Warnf("Unable to determine network, retrying.")
return err
}
m.persister = p.WithNetworkID(net.ID)
return nil
}, bc)
if err != nil {
return err
}
if o.inspect != nil {
if err := o.inspect(m); err != nil {
return errors.WithStack(err)
}
}
return nil
}
func (m *RegistryDefault) SetPersister(p persistence.Persister) {
m.persister = p
}
func (m *RegistryDefault) Courier(ctx context.Context) (courier.Courier, error) {
return courier.NewCourier(ctx, m)
}
func (m *RegistryDefault) ContinuityManager() *continuity.Manager {
return m.continuityManager
}
func (m *RegistryDefault) Persister() persistence.Persister { return m.persister }
func (m *RegistryDefault) ContinuityPersister() continuity.Persister { return m.persister }
func (m *RegistryDefault) IdentityPool() identity.Pool { return m.persister }
func (m *RegistryDefault) PrivilegedIdentityPool() identity.PrivilegedPool { return m.persister }
func (m *RegistryDefault) FlowForTokenExchange() session.FlowForTokenExchange {
return m
}
func (m *RegistryDefault) GetFlowForTokenExchange(ctx context.Context, flowID uuid.UUID) (any, error) {
rf, err := m.RegistrationFlowPersister().GetRegistrationFlow(ctx, flowID)
if err == nil {
return rf, nil
}
if !errors.Is(err, sqlcon.ErrNoRows()) {
return nil, err
}
lf, err := m.LoginFlowPersister().GetLoginFlow(ctx, flowID)
if err == nil {
return lf, nil
}
if !errors.Is(err, sqlcon.ErrNoRows()) {
return nil, err
}
return nil, errors.WithStack(sqlcon.ErrNoRows())
}
func (m *RegistryDefault) RegistrationFlowPersister() registration.FlowPersister { return m.persister }
func (m *RegistryDefault) RecoveryFlowPersister() recovery.FlowPersister { return m.persister }
func (m *RegistryDefault) LoginFlowPersister() login.FlowPersister { return m.persister }
func (m *RegistryDefault) SettingsFlowPersister() settings.FlowPersister { return m.persister }
func (m *RegistryDefault) SelfServiceErrorPersister() errorx.Persister { return m.persister }
func (m *RegistryDefault) SessionPersister() session.Persister { return m.persister }
func (m *RegistryDefault) CourierPersister() courier.Persister { return m.persister }
func (m *RegistryDefault) RecoveryTokenPersister() link.RecoveryTokenPersister { return m.persister }
func (m *RegistryDefault) RecoveryCodePersister() code.RecoveryCodePersister { return m.persister }
func (m *RegistryDefault) LoginCodePersister() code.LoginCodePersister { return m.persister }
func (m *RegistryDefault) VerificationTokenPersister() link.VerificationTokenPersister {
return m.persister
}
func (m *RegistryDefault) VerificationCodePersister() code.VerificationCodePersister {
return m.persister
}
func (m *RegistryDefault) RegistrationCodePersister() code.RegistrationCodePersister {
return m.persister
}
func (m *RegistryDefault) PendingTraitsChangePersister() identity.PendingTraitsChangePersister {
return m.Persister()
}
func (m *RegistryDefault) TransactionalPersisterProvider() transaction.Persister {
return m.persister
}
func (m *RegistryDefault) PingContext(ctx context.Context) error {
return m.persister.Ping(ctx)
}
func (m *RegistryDefault) Ping() error {
return m.persister.Ping(context.Background())
}
func (m *RegistryDefault) WithCSRFTokenGenerator(cg nosurfx.CSRFToken) {
m.csrfTokenGenerator = cg
}
func (m *RegistryDefault) GenerateCSRFToken(r *http.Request) string {
if m.csrfTokenGenerator == nil {
m.csrfTokenGenerator = nosurfx.DefaultCSRFToken
}
return m.csrfTokenGenerator(r)
}
func (m *RegistryDefault) IdentityManager() *identity.Manager {
return m.identityManager
}
func (m *RegistryDefault) HTTPClient(_ context.Context, opts ...httpx.ResilientOptions) *retryablehttp.Client {
opts = append([]httpx.ResilientOptions{
httpx.ResilientClientWithLogger(m.Logger()),
httpx.ResilientClientWithMaxRetry(2),
httpx.ResilientClientWithConnectionTimeout(30 * time.Second),
}, opts...)
// One of the few exceptions, this usually should not be hot reloaded.
if m.Config().ClientHTTPNoPrivateIPRanges(contextx.RootContext) {
opts = append(
opts,
httpx.ResilientClientDisallowInternalIPs(),
// One of the few exceptions, this usually should not be hot reloaded.
httpx.ResilientClientAllowInternalIPRequestsTo(m.Config().ClientHTTPPrivateIPExceptionURLs(contextx.RootContext)...),
)
}
return httpx.NewResilientClient(opts...)
}
func (m *RegistryDefault) SetContextualizer(ctxer contextx.Contextualizer) {
m.ctxer = ctxer
}
func (m *RegistryDefault) Contextualizer() contextx.Contextualizer {
if m.ctxer == nil {
panic("registry Contextualizer not set")
}
return m.ctxer
}
func (m *RegistryDefault) JWKSFetcher() *jwksx.FetcherNext {
return m.jwkFetcher.Get(func() *jwksx.FetcherNext {
maxItems := int64(10_000_000)
cache, _ := ristretto.NewCache(&ristretto.Config[[]byte, jwk.Set]{
NumCounters: maxItems * 10,
MaxCost: maxItems,
BufferItems: 64,
Metrics: true,
IgnoreInternalCost: true,
Cost: func(value jwk.Set) int64 {
return 1
},
})
return jwksx.NewFetcherNext(cache)
})
}
func (m *RegistryDefault) SessionTokenizer() *session.Tokenizer {
return m.sessionTokenizer.Get(func() *session.Tokenizer { return session.NewTokenizer(m) })
}
func (m *RegistryDefault) ExtraHandlers() []x.Handler {
if m.extraHandlers == nil {
for _, newHandler := range m.extraHandlerFactories {
m.extraHandlers = append(m.extraHandlers, newHandler(m))
}
}
return m.extraHandlers
}
// initCheapMembers initializes members that are cheap to initialize.
func (m *RegistryDefault) initCheapMembers() {
m.clock = clock.New()
m.identityValidator = identity.NewValidator(m)
m.identitySchemaProvider = schema.NewDefaultIdentityTraitsProvider(m)
m.identityManager = identity.NewManager(m)
m.sessionManager = session.NewManagerHTTP(m)
m.errorManager = errorx.NewManager(m)
m.continuityManager = continuity.NewManager(m)
}
type initOnce[T any] struct {
value T
init sync.Once
}
// Set sets the value of the initOnce, marking it as initialized.
func (o *initOnce[T]) Set(val T) {
// We need to both set the value and run the init function to mark the value as
// initialized.
o.init.Do(func() { o.value = val })
o.value = val
}
// Get returns the value of the initOnce, initializing it with newT if it has not
// been initialized yet. `newT` is only called once, even if Get is called
// concurrently from multiple goroutines or with different `newT` functions.
func (o *initOnce[T]) Get(newT func() T) T {
o.init.Do(func() { o.value = newT() })
return o.value
}

View File

@@ -0,0 +1,142 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/request"
"github.com/ory/kratos/selfservice/hook"
)
func (m *RegistryDefault) HookVerifier() *hook.Verifier {
if m.hookVerifier == nil {
m.hookVerifier = hook.NewVerifier(m)
}
return m.hookVerifier
}
func (m *RegistryDefault) HookSessionIssuer() *hook.SessionIssuer {
if m.hookSessionIssuer == nil {
m.hookSessionIssuer = hook.NewSessionIssuer(m)
}
return m.hookSessionIssuer
}
func (m *RegistryDefault) HookSessionDestroyer() *hook.SessionDestroyer {
if m.hookSessionDestroyer == nil {
m.hookSessionDestroyer = hook.NewSessionDestroyer(m)
}
return m.hookSessionDestroyer
}
func (m *RegistryDefault) HookAddressVerifier() *hook.AddressVerifier {
if m.hookAddressVerifier == nil {
m.hookAddressVerifier = hook.NewAddressVerifier(m)
}
return m.hookAddressVerifier
}
func (m *RegistryDefault) HookShowVerificationUI() *hook.ShowVerificationUIHook {
if m.hookShowVerificationUI == nil {
m.hookShowVerificationUI = hook.NewShowVerificationUIHook(m)
}
return m.hookShowVerificationUI
}
func (m *RegistryDefault) HookVerifyNewAddress() *hook.VerifyNewAddress {
if m.hookVerifyNewAddress == nil {
m.hookVerifyNewAddress = hook.NewVerifyNewAddress(m)
}
return m.hookVerifyNewAddress
}
func (m *RegistryDefault) HookNotifyPreviousAddresses(c *hook.NotifyPreviousAddressesConfig) *hook.NotifyPreviousAddresses {
return hook.NewNotifyPreviousAddresses(m, c)
}
func (m *RegistryDefault) WithHooks(hooks map[string]NewHookFn) {
m.injectedSelfserviceHooks = hooks
}
func (m *RegistryDefault) WithExtraHandlers(handlers []NewHandler) {
m.extraHandlerFactories = handlers
}
func getHooks[T any](m *RegistryDefault, credentialsType string, configs []config.SelfServiceHook) ([]T, error) {
hooks := make([]T, 0, len(configs))
var addSessionIssuer bool
allHooksLoop:
for _, hookConfig := range configs {
switch hookConfig.Name {
case hook.KeySessionIssuer:
// The session issuer hook always needs to come last.
addSessionIssuer = true
case hook.KeySessionDestroyer:
if h, ok := any(m.HookSessionDestroyer()).(T); ok {
hooks = append(hooks, h)
}
case hook.KeyWebHook:
cfg := request.Config{}
if err := json.Unmarshal(hookConfig.Config, &cfg); err != nil {
m.l.WithError(err).WithField("raw_config", string(hookConfig.Config)).Error("failed to unmarshal hook configuration, ignoring hook")
return nil, errors.WithStack(fmt.Errorf("failed to unmarshal webhook configuration for %s: %w", credentialsType, err))
}
if h, ok := any(hook.NewWebHook(m, &cfg)).(T); ok {
hooks = append(hooks, h)
}
case hook.KeyRequireVerifiedAddress:
if h, ok := any(m.HookAddressVerifier()).(T); ok {
hooks = append(hooks, h)
}
case hook.KeyVerificationUI:
if h, ok := any(m.HookShowVerificationUI()).(T); ok {
hooks = append(hooks, h)
}
case hook.KeyVerifier:
if h, ok := any(m.HookVerifier()).(T); ok {
hooks = append(hooks, h)
}
case hook.KeyVerifyNewAddress:
if h, ok := any(m.HookVerifyNewAddress()).(T); ok {
hooks = append(hooks, h)
}
case hook.KeyNotifyPreviousAddresses:
cfg := &hook.NotifyPreviousAddressesConfig{}
if len(hookConfig.Config) > 0 {
if err := json.Unmarshal(hookConfig.Config, cfg); err != nil {
m.l.WithError(err).WithField("raw_config", string(hookConfig.Config)).Error("failed to unmarshal hook configuration, ignoring hook")
return nil, errors.WithStack(fmt.Errorf("failed to unmarshal notify_previous_addresses configuration for %s: %w", credentialsType, err))
}
}
if h, ok := any(m.HookNotifyPreviousAddresses(cfg)).(T); ok {
hooks = append(hooks, h)
}
default:
for name, newHook := range m.injectedSelfserviceHooks {
if name == hookConfig.Name {
if h, ok := newHook(hookConfig, m).(T); ok {
hooks = append(hooks, h)
}
continue allHooksLoop
}
}
m.l.
WithField("for", credentialsType).
WithField("hook", hookConfig.Name).
Warn("A configuration for a non-existing hook was found and will be ignored.")
}
}
if addSessionIssuer {
if h, ok := any(m.HookSessionIssuer()).(T); ok {
hooks = append(hooks, h)
}
}
return hooks, nil
}

View File

@@ -0,0 +1,53 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"context"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/selfservice/flow/login"
)
func (m *RegistryDefault) LoginHookExecutor() *login.HookExecutor {
if m.selfserviceLoginExecutor == nil {
m.selfserviceLoginExecutor = login.NewHookExecutor(m)
}
return m.selfserviceLoginExecutor
}
func (m *RegistryDefault) PreLoginHooks(ctx context.Context) ([]login.PreHookExecutor, error) {
return getHooks[login.PreHookExecutor](m, "", m.Config().SelfServiceFlowLoginBeforeHooks(ctx))
}
func (m *RegistryDefault) PostLoginHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]login.PostHookExecutor, error) {
hooks, err := getHooks[login.PostHookExecutor](m, string(credentialsType), m.Config().SelfServiceFlowLoginAfterHooks(ctx, string(credentialsType)))
if err != nil {
return nil, err
}
if len(hooks) > 0 {
return hooks, nil
}
// since we don't want merging hooks defined in a specific strategy and global hooks
// global hooks are added only if no strategy specific hooks are defined
return getHooks[login.PostHookExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowLoginAfterHooks(ctx, config.HookGlobal))
}
func (m *RegistryDefault) LoginHandler() *login.Handler {
if m.selfserviceLoginHandler == nil {
m.selfserviceLoginHandler = login.NewHandler(m)
}
return m.selfserviceLoginHandler
}
func (m *RegistryDefault) LoginFlowErrorHandler() *login.ErrorHandler {
if m.selfserviceLoginRequestErrorHandler == nil {
m.selfserviceLoginRequestErrorHandler = login.NewFlowErrorHandler(m)
}
return m.selfserviceLoginRequestErrorHandler
}

View File

@@ -0,0 +1,87 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"context"
"github.com/pkg/errors"
"github.com/ory/herodot"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/selfservice/flow/recovery"
"github.com/ory/kratos/selfservice/strategy/code"
)
func (m *RegistryDefault) RecoveryFlowErrorHandler() *recovery.ErrorHandler {
if m.selfserviceRecoveryErrorHandler == nil {
m.selfserviceRecoveryErrorHandler = recovery.NewErrorHandler(m)
}
return m.selfserviceRecoveryErrorHandler
}
func (m *RegistryDefault) RecoveryHandler() *recovery.Handler {
if m.selfserviceRecoveryHandler == nil {
m.selfserviceRecoveryHandler = recovery.NewHandler(m)
}
return m.selfserviceRecoveryHandler
}
func (m *RegistryDefault) RecoveryStrategies(ctx context.Context) (recoveryStrategies recovery.Strategies) {
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(recovery.Strategy); ok {
if m.Config().SelfServiceStrategy(ctx, s.RecoveryStrategyID()).Enabled {
recoveryStrategies = append(recoveryStrategies, s)
}
}
}
return
}
// GetActiveRecoveryStrategies returns the currently active recovery strategies.
// It returns a list of all strategies and the specific primary strategy.
// If no primary recovery strategy has been set, an error is returned.
func (m *RegistryDefault) GetActiveRecoveryStrategies(ctx context.Context) (active recovery.Strategies, primary recovery.Strategy, err error) {
as := m.Config().SelfServiceFlowRecoveryUse(ctx)
s, ps, err := m.RecoveryStrategies(ctx).ActiveStrategies(as)
if err != nil {
return nil, ps, errors.WithStack(herodot.ErrBadRequest().
WithReasonf("You attempted recovery using %s, which is not enabled or does not exist. An administrator needs to enable this recovery method.", as))
}
return s, ps, nil
}
func (m *RegistryDefault) AllRecoveryStrategies() (recoveryStrategies recovery.Strategies) {
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(recovery.Strategy); ok {
recoveryStrategies = append(recoveryStrategies, s)
}
}
return
}
func (m *RegistryDefault) RecoveryExecutor() *recovery.HookExecutor {
if m.selfserviceRecoveryExecutor == nil {
m.selfserviceRecoveryExecutor = recovery.NewHookExecutor(m)
}
return m.selfserviceRecoveryExecutor
}
func (m *RegistryDefault) PreRecoveryHooks(ctx context.Context) ([]recovery.PreHookExecutor, error) {
return getHooks[recovery.PreHookExecutor](m, "", m.Config().SelfServiceFlowRecoveryBeforeHooks(ctx))
}
func (m *RegistryDefault) PostRecoveryHooks(ctx context.Context) ([]recovery.PostHookExecutor, error) {
return getHooks[recovery.PostHookExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowRecoveryAfterHooks(ctx, config.HookGlobal))
}
func (m *RegistryDefault) CodeSender() *code.Sender {
if m.selfserviceCodeSender == nil {
m.selfserviceCodeSender = code.NewSender(m)
}
return m.selfserviceCodeSender
}

View File

@@ -0,0 +1,85 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"context"
"slices"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/selfservice/flow/registration"
)
func (m *RegistryDefault) PostRegistrationPrePersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]registration.PostHookPrePersistExecutor, error) {
hooks, err := getHooks[registration.PostHookPrePersistExecutor](m, string(credentialsType), m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, string(credentialsType)))
if err != nil {
return nil, err
}
return hooks, nil
}
func (m *RegistryDefault) PostRegistrationPostPersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]registration.PostHookPostPersistExecutor, error) {
hooks, err := getHooks[registration.PostHookPostPersistExecutor](m, string(credentialsType), m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, string(credentialsType)))
if err != nil {
return nil, err
}
if len(hooks) == 0 {
// since we don't want merging hooks defined in a specific strategy and
// global hooks are added only if no strategy specific hooks are defined
hooks, err = getHooks[registration.PostHookPostPersistExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, config.HookGlobal))
if err != nil {
return nil, err
}
}
// WARNING - If you remove this, no verification emails / sms will be sent post-registration.
if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
hooks = slices.Insert(hooks, 0, registration.PostHookPostPersistExecutor(m.HookVerifier()))
}
return hooks, nil
}
func (m *RegistryDefault) PreRegistrationHooks(ctx context.Context) ([]registration.PreHookExecutor, error) {
return getHooks[registration.PreHookExecutor](m, "", m.Config().SelfServiceFlowRegistrationBeforeHooks(ctx))
}
func (m *RegistryDefault) RegistrationExecutor() *registration.HookExecutor {
if m.selfserviceRegistrationExecutor == nil {
m.selfserviceRegistrationExecutor = registration.NewHookExecutor(m)
}
return m.selfserviceRegistrationExecutor
}
func (m *RegistryDefault) RegistrationHookExecutor() *registration.HookExecutor {
if m.selfserviceRegistrationExecutor == nil {
m.selfserviceRegistrationExecutor = registration.NewHookExecutor(m)
}
return m.selfserviceRegistrationExecutor
}
func (m *RegistryDefault) RegistrationErrorHandler() *registration.ErrorHandler {
if m.seflserviceRegistrationErrorHandler == nil {
m.seflserviceRegistrationErrorHandler = registration.NewErrorHandler(m)
}
return m.seflserviceRegistrationErrorHandler
}
func (m *RegistryDefault) RegistrationHandler() *registration.Handler {
if m.selfserviceRegistrationHandler == nil {
m.selfserviceRegistrationHandler = registration.NewHandler(m)
}
return m.selfserviceRegistrationHandler
}
func (m *RegistryDefault) RegistrationFlowErrorHandler() *registration.ErrorHandler {
if m.selfserviceRegistrationRequestErrorHandler == nil {
m.selfserviceRegistrationRequestErrorHandler = registration.NewErrorHandler(m)
}
return m.selfserviceRegistrationRequestErrorHandler
}

View File

@@ -0,0 +1,17 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"context"
"github.com/ory/kratos/schema"
)
// IdentityTraitsSchemas returns the identity traits schemas. The provider is
// eagerly initialized in initCheapMembers (or replaced in Init), so this getter
// only reads the field and never lazily initializes it.
func (m *RegistryDefault) IdentityTraitsSchemas(ctx context.Context) (schema.IdentitySchemaList, error) {
return m.identitySchemaProvider.IdentityTraitsSchemas(ctx)
}

View File

@@ -0,0 +1,45 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver_test
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/pkg"
"github.com/ory/kratos/schema"
"github.com/ory/x/urlx"
)
func TestRegistryDefault_IdentityTraitsSchemas(t *testing.T) {
ctx := context.Background()
conf, reg := pkg.NewFastRegistryWithMocks(t)
defaultSchema := schema.Schema{
ID: "default",
URL: urlx.ParseOrPanic("file://default.schema.json"),
RawURL: "file://default.schema.json",
}
altSchema := schema.Schema{
ID: "alt",
URL: urlx.ParseOrPanic("file://other.schema.json"),
RawURL: "file://other.schema.json",
}
conf.MustSet(ctx, config.ViperKeyIdentitySchemas, []config.Schema{
{ID: altSchema.ID, URL: altSchema.RawURL},
{ID: defaultSchema.ID, URL: defaultSchema.RawURL},
})
ss, err := reg.IdentityTraitsSchemas(context.Background())
require.NoError(t, err)
assert.Equal(t, 2, ss.Total())
assert.Contains(t, ss, defaultSchema)
assert.Contains(t, ss, altSchema)
}

View File

@@ -0,0 +1,10 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import "github.com/ory/kratos/selfservice/sessiontokenexchange"
func (m *RegistryDefault) SessionTokenExchangePersister() sessiontokenexchange.Persister {
return m.Persister()
}

View File

@@ -0,0 +1,91 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"context"
"slices"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/selfservice/flow/settings"
)
func (m *RegistryDefault) PostSettingsPrePersistHooks(ctx context.Context, settingsType string) ([]settings.PostHookPrePersistExecutor, error) {
return getHooks[settings.PostHookPrePersistExecutor](m, settingsType, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, settingsType))
}
func (m *RegistryDefault) PreSettingsHooks(ctx context.Context) ([]settings.PreHookExecutor, error) {
return getHooks[settings.PreHookExecutor](m, "", m.Config().SelfServiceFlowSettingsBeforeHooks(ctx))
}
func (m *RegistryDefault) PostSettingsPostPersistHooks(ctx context.Context, settingsType string) ([]settings.PostHookPostPersistExecutor, error) {
hooks, err := getHooks[settings.PostHookPostPersistExecutor](m, settingsType, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, settingsType))
if err != nil {
return nil, err
}
if len(hooks) == 0 {
// since we don't want merging hooks defined in a specific strategy and
// global hooks are added only if no strategy specific hooks are defined
hooks, err = getHooks[settings.PostHookPostPersistExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, config.HookGlobal))
if err != nil {
return nil, err
}
}
if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
hooks = slices.Insert(hooks, 0, settings.PostHookPostPersistExecutor(m.HookVerifier()))
}
return hooks, nil
}
func (m *RegistryDefault) SettingsHookExecutor() *settings.HookExecutor {
if m.selfserviceSettingsExecutor == nil {
m.selfserviceSettingsExecutor = settings.NewHookExecutor(m)
}
return m.selfserviceSettingsExecutor
}
func (m *RegistryDefault) SettingsHandler() *settings.Handler {
if m.selfserviceSettingsHandler == nil {
m.selfserviceSettingsHandler = settings.NewHandler(m)
}
return m.selfserviceSettingsHandler
}
func (m *RegistryDefault) SettingsFlowErrorHandler() *settings.ErrorHandler {
if m.selfserviceSettingsErrorHandler == nil {
m.selfserviceSettingsErrorHandler = settings.NewErrorHandler(m)
}
return m.selfserviceSettingsErrorHandler
}
func (m *RegistryDefault) SettingsStrategies(ctx context.Context, filters ...settings.StrategyFilter) (profileStrategies settings.Strategies) {
nextStrategy:
for _, strategy := range m.selfServiceStrategies() {
s, ok := strategy.(settings.Strategy)
if !ok {
continue
}
for _, filter := range filters {
if !filter(s) {
continue nextStrategy
}
}
if m.Config().SelfServiceStrategy(ctx, s.SettingsStrategyID()).Enabled || supportsOrganizations(strategy) {
profileStrategies = append(profileStrategies, s)
}
}
return
}
func (m *RegistryDefault) AllSettingsStrategies() settings.Strategies {
var profileStrategies []settings.Strategy
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(settings.Strategy); ok {
profileStrategies = append(profileStrategies, s)
}
}
return profileStrategies
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package driver
import (
"context"
"github.com/pkg/errors"
"github.com/ory/herodot"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/selfservice/flow/verification"
"github.com/ory/kratos/selfservice/strategy/link"
)
func (m *RegistryDefault) VerificationFlowPersister() verification.FlowPersister {
return m.persister
}
func (m *RegistryDefault) VerificationFlowErrorHandler() *verification.ErrorHandler {
if m.selfserviceVerifyErrorHandler == nil {
m.selfserviceVerifyErrorHandler = verification.NewErrorHandler(m)
}
return m.selfserviceVerifyErrorHandler
}
func (m *RegistryDefault) VerificationManager() *identity.Manager {
if m.selfserviceVerifyManager == nil {
m.selfserviceVerifyManager = identity.NewManager(m)
}
return m.selfserviceVerifyManager
}
func (m *RegistryDefault) VerificationHandler() *verification.Handler {
if m.selfserviceVerifyHandler == nil {
m.selfserviceVerifyHandler = verification.NewHandler(m)
}
return m.selfserviceVerifyHandler
}
func (m *RegistryDefault) LinkSender() *link.Sender {
if m.selfserviceLinkSender == nil {
m.selfserviceLinkSender = link.NewSender(m)
}
return m.selfserviceLinkSender
}
// GetActiveVerificationStrategies returns the currently active verification strategies.
// It returns a list of all strategies and the specific primary strategy.
// If no primary verification strategy has been set, an error is returned.
func (m *RegistryDefault) GetActiveVerificationStrategies(ctx context.Context) (active verification.Strategies, primary verification.PrimaryStrategy, err error) {
as := m.Config().SelfServiceFlowVerificationUse(ctx)
s, ps, err := m.VerificationStrategies(ctx).ActiveStrategies(as)
if err != nil {
return nil, ps, errors.WithStack(herodot.ErrBadRequest().
WithReasonf("The active verification strategy %s is not enabled. Please enable it in the configuration.", as))
}
return s, ps, nil
}
func (m *RegistryDefault) VerificationStrategies(ctx context.Context) (verificationStrategies verification.Strategies) {
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(verification.Strategy); ok {
if m.Config().SelfServiceStrategy(ctx, s.VerificationStrategyID()).Enabled {
verificationStrategies = append(verificationStrategies, s)
}
}
}
return
}
func (m *RegistryDefault) AllVerificationStrategies() (verificationStrategies verification.Strategies) {
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(verification.Strategy); ok {
verificationStrategies = append(verificationStrategies, s)
}
}
return
}
func (m *RegistryDefault) VerificationExecutor() *verification.HookExecutor {
if m.selfserviceVerificationExecutor == nil {
m.selfserviceVerificationExecutor = verification.NewHookExecutor(m)
}
return m.selfserviceVerificationExecutor
}
func (m *RegistryDefault) PreVerificationHooks(ctx context.Context) ([]verification.PreHookExecutor, error) {
return getHooks[verification.PreHookExecutor](m, "", m.Config().SelfServiceFlowVerificationBeforeHooks(ctx))
}
func (m *RegistryDefault) PostVerificationHooks(ctx context.Context) ([]verification.PostHookExecutor, error) {
return getHooks[verification.PostHookExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowVerificationAfterHooks(ctx, config.HookGlobal))
}