Skip to content

Commit

Permalink
feat: login and registration with code
Browse files Browse the repository at this point in the history
  • Loading branch information
Benehiko committed Jul 25, 2023
1 parent 3a79a63 commit e51d791
Show file tree
Hide file tree
Showing 166 changed files with 3,919 additions and 1,436 deletions.
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
56 changes: 55 additions & 1 deletion driver/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ const (
ViperKeyCourierTemplatesVerificationCodeValidEmail = "courier.templates.verification_code.valid.email"
ViperKeyCourierDeliveryStrategy = "courier.delivery_strategy"
ViperKeyCourierHTTPRequestConfig = "courier.http.request_config"
ViperKeyCourierTemplatesLoginCodeValidEmail = "courier.templates.login_code.valid.email"
ViperKeyCourierTemplatesRegistrationCodeValidEmail = "courier.templates.registration_code.valid.email"
ViperKeyCourierSMTPFrom = "courier.smtp.from_address"
ViperKeyCourierSMTPFromName = "courier.smtp.from_name"
ViperKeyCourierSMTPHeaders = "courier.smtp.headers"
Expand Down Expand Up @@ -225,6 +227,11 @@ type (
Enabled bool `json:"enabled"`
Config json.RawMessage `json:"config"`
}
SelfServiceStrategyCode struct {
RegistrationEnabled bool `json:"registration_enabled"`
LoginEnabled bool `json:"login_enabled"`
*SelfServiceStrategy
}
Schema struct {
ID string `json:"id" koanf:"id"`
URL string `json:"url" koanf:"url"`
Expand Down Expand Up @@ -278,6 +285,8 @@ type (
CourierTemplatesRecoveryCodeValid(ctx context.Context) *CourierEmailTemplate
CourierTemplatesVerificationCodeInvalid(ctx context.Context) *CourierEmailTemplate
CourierTemplatesVerificationCodeValid(ctx context.Context) *CourierEmailTemplate
CourierTemplatesLoginCodeValid(ctx context.Context) *CourierEmailTemplate
CourierTemplatesRegistrationCodeValid(ctx context.Context) *CourierEmailTemplate
CourierMessageRetries(ctx context.Context) int
}
)
Expand Down Expand Up @@ -723,7 +732,9 @@ func (p *Config) SelfServiceStrategy(ctx context.Context, strategy string) *Self
config = c
}

enabledKey := fmt.Sprintf("%s.%s.enabled", ViperKeySelfServiceStrategyConfig, strategy)
basePath := fmt.Sprintf("%s.%s", ViperKeySelfServiceStrategyConfig, strategy)

enabledKey := fmt.Sprintf("%s.enabled", basePath)
s := &SelfServiceStrategy{
Enabled: pp.Bool(enabledKey),
Config: json.RawMessage(config),
Expand All @@ -733,6 +744,7 @@ func (p *Config) SelfServiceStrategy(ctx context.Context, strategy string) *Self
// we need to forcibly set these values here:
if !pp.Exists(enabledKey) {
switch strategy {
case "otp":
case "password":
fallthrough
case "profile":
Expand All @@ -749,6 +761,40 @@ func (p *Config) SelfServiceStrategy(ctx context.Context, strategy string) *Self
return s
}

func (p *Config) SelfServiceCodeStrategy(ctx context.Context) *SelfServiceStrategyCode {
pp := p.GetProvider(ctx)

config := "{}"
out, err := pp.Marshal(kjson.Parser())
if err != nil {
p.l.WithError(err).Warn("Unable to marshal self service strategy configuration.")
} else if c := gjson.GetBytes(out,
fmt.Sprintf("%s.%s.config", ViperKeySelfServiceStrategyConfig, "code")).Raw; len(c) > 0 {
config = c
}

basePath := fmt.Sprintf("%s.%s", ViperKeySelfServiceStrategyConfig, "code")
enabledKey := fmt.Sprintf("%s.enabled", basePath)
registrationKey := fmt.Sprintf("%s.registration_enabled", basePath)
loginKey := fmt.Sprintf("%s.login_enabled", basePath)

s := &SelfServiceStrategyCode{
SelfServiceStrategy: &SelfServiceStrategy{
Enabled: pp.Bool(enabledKey),
Config: json.RawMessage(config),
},
RegistrationEnabled: pp.Bool(registrationKey),
LoginEnabled: pp.Bool(loginKey),
}

if !pp.Exists(enabledKey) {
s.RegistrationEnabled = false
s.LoginEnabled = false
s.Enabled = true
}
return s
}

func (p *Config) SecretsDefault(ctx context.Context) [][]byte {
pp := p.GetProvider(ctx)
secrets := pp.Strings(ViperKeySecretsDefault)
Expand Down Expand Up @@ -1090,6 +1136,14 @@ func (p *Config) CourierTemplatesVerificationCodeValid(ctx context.Context) *Cou
return p.CourierTemplatesHelper(ctx, ViperKeyCourierTemplatesVerificationCodeValidEmail)
}

func (p *Config) CourierTemplatesLoginCodeValid(ctx context.Context) *CourierEmailTemplate {
return p.CourierTemplatesHelper(ctx, ViperKeyCourierTemplatesLoginCodeValidEmail)
}

func (p *Config) CourierTemplatesRegistrationCodeValid(ctx context.Context) *CourierEmailTemplate {
return p.CourierTemplatesHelper(ctx, ViperKeyCourierTemplatesRegistrationCodeValidEmail)
}

func (p *Config) CourierMessageRetries(ctx context.Context) int {
return p.GetProvider(ctx).IntF(ViperKeyCourierMessageRetries, 5)
}
Expand Down
Loading

0 comments on commit e51d791

Please sign in to comment.