Skip to content

Commit

Permalink
feat: login and registration with code
Browse files Browse the repository at this point in the history
style: format

refactor: code cleanup and flow refinement

feat: store code credential address type

fix: login resend button and field errors

fix: invalid code handling and error messages

test(e2e): registration with code

test: login and registration code

fix: login and registration tests

style: format

test: registration with code error cases

test: login with code error messages

test: login with code

test: login and registration code

test: login errors

fix: unit tests and verification flow

fix: ui rendering on code group instead of default

fix: sdk generation

fix: sdk generation and tests

chore: improve registration with code test

chore: code review

chore: code review

chore: cleanup based on review comments
  • Loading branch information
Benehiko committed Aug 16, 2023
1 parent 9c1523c commit 951485b
Show file tree
Hide file tree
Showing 216 changed files with 7,818 additions and 1,217 deletions.
12 changes: 12 additions & 0 deletions .schema/openapi/patches/selfservice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
password: "#/components/schemas/updateRegistrationFlowWithPasswordMethod"
oidc: "#/components/schemas/updateRegistrationFlowWithOidcMethod"
webauthn: "#/components/schemas/updateRegistrationFlowWithWebAuthnMethod"
- op: add
path: /components/schemas/registrationFlowState/enum
value:
- choose_method
- sent_email
- passed_challenge
# end

# All modifications for the login flow
Expand All @@ -48,6 +54,12 @@
totp: "#/components/schemas/updateLoginFlowWithTotpMethod"
webauthn: "#/components/schemas/updateLoginFlowWithWebAuthnMethod"
lookup_secret: "#/components/schemas/updateLoginFlowWithLookupSecretMethod"
- op: add
path: /components/schemas/loginFlowState/enum
value:
- choose_method
- sent_email
- passed_challenge
# end

# All modifications for the recovery flow
Expand Down
18 changes: 16 additions & 2 deletions cmd/clidoc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import (
"github.com/ory/x/clidoc"
)

var aSecondAgo = time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC).Add(-time.Second)
var inAMinute = time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC).Add(time.Minute)
var (
aSecondAgo = time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC).Add(-time.Second)
inAMinute = time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC).Add(time.Minute)
)

var messages map[string]*text.Message

Expand Down Expand Up @@ -145,6 +147,18 @@ func init() {
"NewInfoSelfServiceContinueLoginWebAuthn": text.NewInfoSelfServiceContinueLoginWebAuthn(),
"NewInfoSelfServiceLoginContinue": text.NewInfoSelfServiceLoginContinue(),
"NewErrorValidationSuchNoWebAuthnUser": text.NewErrorValidationSuchNoWebAuthnUser(),
"NewRegistrationEmailWithCodeSent": text.NewRegistrationEmailWithCodeSent(),
"NewLoginEmailWithCodeSent": text.NewLoginEmailWithCodeSent(),
"NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed": text.NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed(),
"NewErrorValidationLoginCodeInvalidOrAlreadyUsed": text.NewErrorValidationLoginCodeInvalidOrAlreadyUsed(),
"NewErrorValidationNoCodeUser": text.NewErrorValidationNoCodeUser(),
"NewInfoNodeLabelRegistrationCode": text.NewInfoNodeLabelRegistrationCode(),
"NewInfoNodeLabelLoginCode": text.NewInfoNodeLabelLoginCode(),
"NewErrorValidationLoginRetrySuccessful": text.NewErrorValidationLoginRetrySuccessful(),
"NewErrorValidationTraitsMismatch": text.NewErrorValidationTraitsMismatch(),
"NewInfoSelfServiceLoginCode": text.NewInfoSelfServiceLoginCode(),
"NewErrorValidationRegistrationRetrySuccessful": text.NewErrorValidationRegistrationRetrySuccessful(),
"NewInfoSelfServiceRegistrationRegisterCode": text.NewInfoSelfServiceRegistrationRegisterCode(),
}
}

Expand Down
18 changes: 18 additions & 0 deletions courier/email_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const (
TypeVerificationCodeValid TemplateType = "verification_code_valid"
TypeOTP TemplateType = "otp"
TypeTestStub TemplateType = "stub"
TypeLoginCodeValid TemplateType = "login_code_valid"
TypeRegistrationCodeValid TemplateType = "registration_code_valid"
)

func GetEmailTemplateType(t EmailTemplate) (TemplateType, error) {
Expand All @@ -60,6 +62,10 @@ func GetEmailTemplateType(t EmailTemplate) (TemplateType, error) {
return TypeVerificationCodeInvalid, nil
case *email.VerificationCodeValid:
return TypeVerificationCodeValid, nil
case *email.LoginCodeValid:
return TypeLoginCodeValid, nil
case *email.RegistrationCodeValid:
return TypeRegistrationCodeValid, nil
case *email.TestStub:
return TypeTestStub, nil
default:
Expand Down Expand Up @@ -123,6 +129,18 @@ func NewEmailTemplateFromMessage(d template.Dependencies, msg Message) (EmailTem
return nil, err
}
return email.NewTestStub(d, &t), nil
case TypeLoginCodeValid:
var t email.LoginCodeValidModel
if err := json.Unmarshal(msg.TemplateData, &t); err != nil {
return nil, err
}
return email.NewLoginCodeValid(d, &t), nil
case TypeRegistrationCodeValid:
var t email.RegistrationCodeValidModel
if err := json.Unmarshal(msg.TemplateData, &t); err != nil {
return nil, err
}
return email.NewRegistrationCodeValid(d, &t), nil
default:
return nil, errors.Errorf("received unexpected message template type: %s", msg.TemplateType)
}
Expand Down
5 changes: 4 additions & 1 deletion courier/email_templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ func TestGetTemplateType(t *testing.T) {
courier.TypeVerificationCodeInvalid: &email.VerificationCodeInvalid{},
courier.TypeVerificationCodeValid: &email.VerificationCodeValid{},
courier.TypeTestStub: &email.TestStub{},
courier.TypeLoginCodeValid: &email.LoginCodeValid{},
courier.TypeRegistrationCodeValid: &email.RegistrationCodeValid{},
} {
t.Run(fmt.Sprintf("case=%s", expectedType), func(t *testing.T) {
actualType, err := courier.GetEmailTemplateType(tmpl)
Expand All @@ -50,6 +52,8 @@ func TestNewEmailTemplateFromMessage(t *testing.T) {
courier.TypeVerificationCodeInvalid: email.NewVerificationCodeInvalid(reg, &email.VerificationCodeInvalidModel{To: "baz"}),
courier.TypeVerificationCodeValid: email.NewVerificationCodeValid(reg, &email.VerificationCodeValidModel{To: "faz", VerificationURL: "http://bar.foo", VerificationCode: "123456678"}),
courier.TypeTestStub: email.NewTestStub(reg, &email.TestStubModel{To: "far", Subject: "test subject", Body: "test body"}),
courier.TypeLoginCodeValid: email.NewLoginCodeValid(reg, &email.LoginCodeValidModel{To: "far", LoginCode: "123456"}),
courier.TypeRegistrationCodeValid: email.NewRegistrationCodeValid(reg, &email.RegistrationCodeValidModel{To: "far", RegistrationCode: "123456"}),
} {
t.Run(fmt.Sprintf("case=%s", tmplType), func(t *testing.T) {
tmplData, err := json.Marshal(expectedTmpl)
Expand Down Expand Up @@ -84,7 +88,6 @@ func TestNewEmailTemplateFromMessage(t *testing.T) {
actualBodyPlaintext, err := actualTmpl.EmailBodyPlaintext(ctx)
require.NoError(t, err)
require.Equal(t, expectedBodyPlaintext, actualBodyPlaintext)

})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Hi,

please login to your account by entering the following code:

{{ .LoginCode }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Hi,

please login to your account by entering the following code:

{{ .LoginCode }}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Login to your account
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Hi,

please complete your account registration by entering the following code:

{{ .RegistrationCode }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Hi,

please complete your account registration by entering the following code:

{{ .RegistrationCode }}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Complete your account registration
51 changes: 51 additions & 0 deletions courier/template/email/login_code_valid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package email

import (
"context"
"encoding/json"
"os"
"strings"

"github.com/ory/kratos/courier/template"
)

type (
LoginCodeValid struct {
deps template.Dependencies
model *LoginCodeValidModel
}
LoginCodeValidModel struct {
To string
LoginCode string
Identity map[string]interface{}
}
)

func NewLoginCodeValid(d template.Dependencies, m *LoginCodeValidModel) *LoginCodeValid {
return &LoginCodeValid{deps: d, model: m}
}

func (t *LoginCodeValid) EmailRecipient() (string, error) {
return t.model.To, nil
}

func (t *LoginCodeValid) EmailSubject(ctx context.Context) (string, error) {
subject, err := template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), "login_code/valid/email.subject.gotmpl", "login_code/valid/email.subject*", t.model, t.deps.CourierConfig().CourierTemplatesLoginCodeValid(ctx).Subject)

return strings.TrimSpace(subject), err
}

func (t *LoginCodeValid) EmailBody(ctx context.Context) (string, error) {
return template.LoadHTML(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), "login_code/valid/email.body.gotmpl", "login_code/valid/email.body*", t.model, t.deps.CourierConfig().CourierTemplatesLoginCodeValid(ctx).Body.HTML)
}

func (t *LoginCodeValid) EmailBodyPlaintext(ctx context.Context) (string, error) {
return template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), "login_code/valid/email.body.plaintext.gotmpl", "login_code/valid/email.body.plaintext*", t.model, t.deps.CourierConfig().CourierTemplatesLoginCodeValid(ctx).Body.PlainText)
}

func (t *LoginCodeValid) MarshalJSON() ([]byte, error) {
return json.Marshal(t.model)
}
30 changes: 30 additions & 0 deletions courier/template/email/login_code_valid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package email_test

import (
"context"
"testing"

"github.com/ory/kratos/courier"
"github.com/ory/kratos/courier/template/email"
"github.com/ory/kratos/courier/template/testhelpers"
"github.com/ory/kratos/internal"
)

func TestLoginCodeValid(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)

t.Run("test=with courier templates directory", func(t *testing.T) {
_, reg := internal.NewFastRegistryWithMocks(t)
tpl := email.NewLoginCodeValid(reg, &email.LoginCodeValidModel{})

testhelpers.TestRendered(t, ctx, tpl)
})

t.Run("test=with remote resources", func(t *testing.T) {
testhelpers.TestRemoteTemplates(t, "../courier/builtin/templates/login_code/valid", courier.TypeLoginCodeValid)
})
}
51 changes: 51 additions & 0 deletions courier/template/email/registration_code_valid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package email

import (
"context"
"encoding/json"
"os"
"strings"

"github.com/ory/kratos/courier/template"
)

type (
RegistrationCodeValid struct {
deps template.Dependencies
model *RegistrationCodeValidModel
}
RegistrationCodeValidModel struct {
To string
Traits map[string]interface{}
RegistrationCode string
}
)

func NewRegistrationCodeValid(d template.Dependencies, m *RegistrationCodeValidModel) *RegistrationCodeValid {
return &RegistrationCodeValid{deps: d, model: m}
}

func (t *RegistrationCodeValid) EmailRecipient() (string, error) {
return t.model.To, nil
}

func (t *RegistrationCodeValid) EmailSubject(ctx context.Context) (string, error) {
subject, err := template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), "registration_code/valid/email.subject.gotmpl", "registration_code/valid/email.subject*", t.model, t.deps.CourierConfig().CourierTemplatesRegistrationCodeValid(ctx).Subject)

return strings.TrimSpace(subject), err
}

func (t *RegistrationCodeValid) EmailBody(ctx context.Context) (string, error) {
return template.LoadHTML(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), "registration_code/valid/email.body.gotmpl", "registration_code/valid/email.body*", t.model, t.deps.CourierConfig().CourierTemplatesRegistrationCodeValid(ctx).Body.HTML)
}

func (t *RegistrationCodeValid) EmailBodyPlaintext(ctx context.Context) (string, error) {
return template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), "registration_code/valid/email.body.plaintext.gotmpl", "registration_code/valid/email.body.plaintext*", t.model, t.deps.CourierConfig().CourierTemplatesRegistrationCodeValid(ctx).Body.PlainText)
}

func (t *RegistrationCodeValid) MarshalJSON() ([]byte, error) {
return json.Marshal(t.model)
}
30 changes: 30 additions & 0 deletions courier/template/email/registration_code_valid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package email_test

import (
"context"
"testing"

"github.com/ory/kratos/courier"
"github.com/ory/kratos/courier/template/email"
"github.com/ory/kratos/courier/template/testhelpers"
"github.com/ory/kratos/internal"
)

func TestRegistrationCodeValid(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)

t.Run("test=with courier templates directory", func(t *testing.T) {
_, reg := internal.NewFastRegistryWithMocks(t)
tpl := email.NewRegistrationCodeValid(reg, &email.RegistrationCodeValidModel{})

testhelpers.TestRendered(t, ctx, tpl)
})

t.Run("test=with remote resources", func(t *testing.T) {
testhelpers.TestRemoteTemplates(t, "../courier/builtin/templates/registration_code/valid", courier.TypeRegistrationCodeValid)
})
}
2 changes: 2 additions & 0 deletions courier/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type (
CourierTemplatesVerificationValid() *config.CourierEmailTemplate
CourierTemplatesRecoveryInvalid() *config.CourierEmailTemplate
CourierTemplatesRecoveryValid() *config.CourierEmailTemplate
CourierTemplatesLoginValid() *config.CourierEmailTemplate
CourierTemplatesRegistrationValid() *config.CourierEmailTemplate
}

Dependencies interface {
Expand Down
7 changes: 6 additions & 1 deletion courier/template/testhelpers/testhelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ func SetupRemoteConfig(t *testing.T, ctx context.Context, plaintext string, html
func TestRendered(t *testing.T, ctx context.Context, tpl interface {
EmailBody(context.Context) (string, error)
EmailSubject(context.Context) (string, error)
}) {
},
) {
rendered, err := tpl.EmailBody(ctx)
require.NoError(t, err)
assert.NotEmpty(t, rendered)
Expand Down Expand Up @@ -83,6 +84,10 @@ func TestRemoteTemplates(t *testing.T, basePath string, tmplType courier.Templat
return email.NewVerificationCodeInvalid(d, &email.VerificationCodeInvalidModel{})
case courier.TypeVerificationCodeValid:
return email.NewVerificationCodeValid(d, &email.VerificationCodeValidModel{})
case courier.TypeLoginCodeValid:
return email.NewLoginCodeValid(d, &email.LoginCodeValidModel{})
case courier.TypeRegistrationCodeValid:
return email.NewRegistrationCodeValid(d, &email.RegistrationCodeValidModel{})
default:
return nil
}
Expand Down
Loading

0 comments on commit 951485b

Please sign in to comment.