Skip to content

Commit

Permalink
feat: PassKeys with Resident Keys and two-step registration (#3748)
Browse files Browse the repository at this point in the history
BREAKING CHANGES: This feature enables two-step registration per default. Two-step registration is a significantly improved sign up flow and recommended when using more than one sign up methods. To disable two-step registration, set `selfservice.flows.registration.enable_legacy_flow` to `true`. This value defaults to `false`.
  • Loading branch information
hperl committed Mar 11, 2024
1 parent 0f3d082 commit 3621411
Show file tree
Hide file tree
Showing 187 changed files with 9,769 additions and 384 deletions.
6 changes: 6 additions & 0 deletions cmd/clidoc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func init() {
"NewInfoSelfServiceSettingsUpdateUnlinkOIDC": text.NewInfoSelfServiceSettingsUpdateUnlinkOIDC("{provider}"),
"NewInfoSelfServiceRegisterWebAuthnDisplayName": text.NewInfoSelfServiceRegisterWebAuthnDisplayName(),
"NewInfoSelfServiceRemoveWebAuthn": text.NewInfoSelfServiceRemoveWebAuthn("{display_name}", aSecondAgo),
"NewInfoSelfServiceRemovePasskey": text.NewInfoSelfServiceRemovePasskey("{display_name}", aSecondAgo),
"NewErrorValidationVerificationFlowExpired": text.NewErrorValidationVerificationFlowExpired(aSecondAgo),
"NewInfoSelfServiceVerificationSuccessful": text.NewInfoSelfServiceVerificationSuccessful(),
"NewVerificationEmailSent": text.NewVerificationEmailSent(),
Expand Down Expand Up @@ -136,6 +137,8 @@ func init() {
"NewInfoRegistration": text.NewInfoRegistration(),
"NewInfoRegistrationWith": text.NewInfoRegistrationWith("{provider}"),
"NewInfoRegistrationContinue": text.NewInfoRegistrationContinue(),
"NewInfoRegistrationBack": text.NewInfoRegistrationBack(),
"NewInfoSelfServiceChooseCredentials": text.NewInfoSelfServiceChooseCredentials(),
"NewErrorValidationRegistrationFlowExpired": text.NewErrorValidationRegistrationFlowExpired(aSecondAgo),
"NewErrorValidationRecoveryFlowExpired": text.NewErrorValidationRecoveryFlowExpired(aSecondAgo),
"NewRecoverySuccessful": text.NewRecoverySuccessful(inAMinute),
Expand All @@ -150,9 +153,12 @@ func init() {
"NewInfoNodeLoginAndLinkCredential": text.NewInfoNodeLoginAndLinkCredential(),
"NewInfoNodeLabelContinue": text.NewInfoNodeLabelContinue(),
"NewInfoSelfServiceSettingsRegisterWebAuthn": text.NewInfoSelfServiceSettingsRegisterWebAuthn(),
"NewInfoSelfServiceSettingsRegisterPasskey": text.NewInfoSelfServiceSettingsRegisterPasskey(),
"NewInfoLoginWebAuthnPasswordless": text.NewInfoLoginWebAuthnPasswordless(),
"NewInfoSelfServiceRegistrationRegisterWebAuthn": text.NewInfoSelfServiceRegistrationRegisterWebAuthn(),
"NewInfoSelfServiceContinueLoginWebAuthn": text.NewInfoSelfServiceContinueLoginWebAuthn(),
"NewInfoSelfServiceLoginPasskey": text.NewInfoSelfServiceLoginPasskey(),
"NewInfoSelfServiceRegistrationRegisterPasskey": text.NewInfoSelfServiceRegistrationRegisterPasskey(),
"NewInfoSelfServiceLoginContinue": text.NewInfoSelfServiceLoginContinue(),
"NewErrorValidationSuchNoWebAuthnUser": text.NewErrorValidationSuchNoWebAuthnUser(),
"NewRegistrationEmailWithCodeSent": text.NewRegistrationEmailWithCodeSent(),
Expand Down
57 changes: 57 additions & 0 deletions contrib/quickstart/kratos/all-strategies/identity.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "E-Mail",
"minLength": 3,
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
},
"webauthn": {
"identifier": true
},
"code": {
"identifier": true,
"via": "email"
},
"passkey": {
"display_name": true
}
},
"verification": {
"via": "email"
},
"recovery": {
"via": "email"
}
}
},
"name": {
"type": "object",
"properties": {
"first": {
"title": "First Name",
"type": "string"
},
"last": {
"title": "Last Name",
"type": "string"
}
}
}
},
"required": ["email"],
"additionalProperties": false
}
}
}
120 changes: 120 additions & 0 deletions contrib/quickstart/kratos/all-strategies/kratos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
version: v0.13.0

dsn: memory

serve:
public:
base_url: http://localhost:4433/
cors:
enabled: true
admin:
base_url: http://kratos:4434/

session:
whoami:
required_aal: aal1

selfservice:
default_browser_return_url: http://localhost:4455/
allowed_return_urls:
- http://localhost:4455
- http://localhost:19006/Callback
- exp://localhost:8081/--/Callback

methods:
password:
enabled: true
webauthn:
enabled: true
config:
passwordless: true
rp:
display_name: Your Application name
# Set 'id' to the top-level domain.
id: localhost
# Set 'origin' to the exact URL of the page that prompts the user to use WebAuthn. You must include the scheme, host, and port.
origin: http://localhost:4455
passkey:
enabled: true
config:
rp:
display_name: Your Application name
# Set 'id' to the top-level domain.
id: localhost
# Set 'origin' to the exact URL of the page that prompts the user to use WebAuthn. You must include the scheme, host, and port.
origins:
- http://localhost:4455

flows:
error:
ui_url: http://localhost:4455/error

settings:
ui_url: http://localhost:4455/settings
privileged_session_max_age: 15m
required_aal: aal1

recovery:
enabled: true
ui_url: http://localhost:4455/recovery
use: code

verification:
enabled: false
ui_url: http://localhost:4455/verification
use: code
after:
default_browser_return_url: http://localhost:4455/

logout:
after:
default_browser_return_url: http://localhost:4455/login

login:
ui_url: http://localhost:4455/login
lifespan: 10m

registration:
enable_legacy_one_step: false
lifespan: 10m
ui_url: http://localhost:4455/registration
after:
passkey:
hooks:
- hook: session
webauthn:
hooks:
- hook: session
password:
hooks:
- hook: session
- hook: show_verification_ui

log:
level: debug
format: text
leak_sensitive_values: true

secrets:
cookie:
- PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
cipher:
- 32-LONG-SECRET-NOT-SECURE-AT-ALL

ciphers:
algorithm: xchacha20-poly1305

hashers:
algorithm: bcrypt
bcrypt:
cost: 8

identity:
default_schema_id: default
schemas:
- id: default
url: file://./contrib/quickstart/kratos/all-strategies/identity.schema.json

courier:
smtp:
connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true
34 changes: 34 additions & 0 deletions contrib/quickstart/kratos/passkey/identity.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "Your E-Mail",
"minLength": 3,
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
},
"webauthn": {
"identifier": true
},
"passkey": {
"display_name": true
}
}
}
}
},
"required": ["email"],
"additionalProperties": false
}
}
}
107 changes: 107 additions & 0 deletions contrib/quickstart/kratos/passkey/kratos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
serve:
public:
base_url: http://localhost:4433/
cors:
enabled: true
admin:
base_url: http://kratos:4434/

session:
whoami:
required_aal: aal1

selfservice:
default_browser_return_url: http://localhost:4455/
allowed_return_urls:
- http://localhost:4455
- http://localhost:19006/Callback
- exp://example.com/Callback
- https://www.ory.sh/
- https://example.org/
- https://www.example.org/
methods:
link:
config:
lifespan: 1h
code:
config:
lifespan: 1h
totp:
enabled: true
config:
issuer: issuer.ory.sh
lookup_secret:
enabled: true
webauthn:
enabled: true
config:
passwordless: true
rp:
id: localhost
origins:
- http://localhost:4455
display_name: Ory
passkey:
enabled: true
config:
rp:
id: localhost
origins:
- http://localhost:4455
display_name: Ory
flows:
settings:
ui_url: http://localhost:4455/settings
privileged_session_max_age: 5m
required_aal: aal1
logout:
after:
default_browser_return_url: http://localhost:4455/login
registration:
ui_url: http://localhost:4455/registration
after:
password:
hooks:
- hook: session
webauthn:
hooks:
- hook: session
passkey:
hooks:
- hook: session
login:
ui_url: http://localhost:4455/login
error:
ui_url: http://localhost:4455/error
verification:
ui_url: http://localhost:4455/verify
recovery:
ui_url: http://localhost:4455/recovery

log:
level: debug
format: text
leak_sensitive_values: true

secrets:
cookie:
- PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
cipher:
- 32-LONG-SECRET-NOT-SECURE-AT-ALL

ciphers:
algorithm: xchacha20-poly1305

hashers:
algorithm: bcrypt
bcrypt:
cost: 8

identity:
schemas:
- id: default
url: file://contrib/quickstart/kratos/passkey/identity.schema.json

courier:
smtp:
connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true
2 changes: 1 addition & 1 deletion courier/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func (m Message) DefaultPageToken() keysetpagination.PageToken {
}
}

func (m Message) TableName(ctx context.Context) string {
func (m Message) TableName(context.Context) string {
return "courier_messages"
}

Expand Down
6 changes: 5 additions & 1 deletion courier/test/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ func TestPersister(ctx context.Context, newNetworkUnlessExisting NetworkWrapper,

messages := make([]courier.Message, 5)
t.Run("case=add messages to the queue", func(t *testing.T) {
t.Cleanup(func() { pop.SetNowFunc(time.Now) })
now := time.Now()
for k := range messages {
// We need to fake the time func to control the created_at column, which is the
// sort key for the messages.
pop.SetNowFunc(func() time.Time { return now.Add(time.Duration(k) * time.Hour) })
require.NoError(t, faker.FakeData(&messages[k]))
require.NoError(t, p.AddMessage(ctx, &messages[k]))
time.Sleep(time.Second) // wait a bit so that the timestamp ordering works in MySQL.
}
})

Expand Down
Loading

0 comments on commit 3621411

Please sign in to comment.