Merge pull request #11 from Nucrea/feature/email-verification
Feature/email verification
This commit is contained in:
commit
9e87566c5d
@ -1,5 +1,5 @@
|
|||||||
port: 8080
|
port: 8080
|
||||||
postgres_url: "postgres://postgres:postgres@localhost:5432/postgres"
|
postgres_url: "postgres://postgres:postgres@localhost:5432/postgres"
|
||||||
jwt_signing_key: "./config_defaults/jwt_signing_key"
|
jwt_signing_key: "./jwt_signing_key"
|
||||||
kafka_url: "localhost:9092"
|
kafka_url: "localhost:9092"
|
||||||
kafka_topic: "backend_events"
|
kafka_topic: "backend_events"
|
||||||
71
cmd/backend/server/handlers/user_verify_handler.go
Normal file
71
cmd/backend/server/handlers/user_verify_handler.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"backend/internal/core/services"
|
||||||
|
"backend/pkg/logger"
|
||||||
|
|
||||||
|
"html/template"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HtmlTemplate struct {
|
||||||
|
TabTitle string
|
||||||
|
Title string
|
||||||
|
Text string
|
||||||
|
Link string
|
||||||
|
LinkText string
|
||||||
|
}
|
||||||
|
|
||||||
|
const htmlTemplate = `
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{.TabTitle}}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{if .Title}}
|
||||||
|
<h1>{{.Title}}</h1>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<h3>{{.Text}}</h3>
|
||||||
|
|
||||||
|
{{if .Link}}
|
||||||
|
<a href="{{.Link}}">{{.LinkText}}</a>
|
||||||
|
{{end}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
|
func NewUserVerifyEmailHandler(log logger.Logger, userService services.UserService) gin.HandlerFunc {
|
||||||
|
template, err := template.New("verify-email").Parse(htmlTemplate)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Error parsing template")
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
tmp := HtmlTemplate{
|
||||||
|
TabTitle: "Verify Email",
|
||||||
|
Text: "Error verifying email",
|
||||||
|
}
|
||||||
|
|
||||||
|
token, ok := c.GetQuery("token")
|
||||||
|
if !ok || token == "" {
|
||||||
|
log.Error().Err(err).Msg("No token in query param")
|
||||||
|
template.Execute(c.Writer, tmp)
|
||||||
|
c.Status(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := userService.VerifyEmail(c, token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error verifying email")
|
||||||
|
template.Execute(c.Writer, tmp)
|
||||||
|
c.Status(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp.Text = "Email successfully verified"
|
||||||
|
template.Execute(c.Writer, tmp)
|
||||||
|
c.Status(200)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,7 +15,7 @@ func NewAuthMiddleware(userService services.UserService) gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := userService.ValidateToken(ctx, token)
|
user, err := userService.ValidateAuthToken(ctx, token)
|
||||||
if err == services.ErrUserWrongToken || err == services.ErrUserNotExists {
|
if err == services.ErrUserWrongToken || err == services.ErrUserNotExists {
|
||||||
ctx.AbortWithError(403, err)
|
ctx.AbortWithError(403, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -39,21 +39,25 @@ func NewServer(opts NewServerOpts) *httpserver.Server {
|
|||||||
r.Use(httpserver.NewRequestLogMiddleware(opts.Logger, opts.Tracer, prometheus))
|
r.Use(httpserver.NewRequestLogMiddleware(opts.Logger, opts.Tracer, prometheus))
|
||||||
r.Use(httpserver.NewTracingMiddleware(opts.Tracer))
|
r.Use(httpserver.NewTracingMiddleware(opts.Tracer))
|
||||||
|
|
||||||
v1 := r.Group("/v1")
|
r.GET("/verify-user", handlers.NewUserVerifyEmailHandler(opts.Logger, opts.UserService))
|
||||||
|
|
||||||
|
api := r.Group("/api")
|
||||||
|
|
||||||
|
v1 := api.Group("/v1")
|
||||||
userGroup := v1.Group("/user")
|
userGroup := v1.Group("/user")
|
||||||
{
|
{
|
||||||
userGroup.POST("/create", handlers.NewUserCreateHandler(opts.Logger, opts.UserService))
|
userGroup.POST("/create", handlers.NewUserCreateHandler(opts.Logger, opts.UserService))
|
||||||
userGroup.POST("/login", handlers.NewUserLoginHandler(opts.Logger, opts.UserService))
|
userGroup.POST("/login", handlers.NewUserLoginHandler(opts.Logger, opts.UserService))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dummyGroup := v1.Group("/dummy")
|
dummyGroup := v1.Group("/dummy")
|
||||||
{
|
|
||||||
dummyGroup.Use(middleware.NewAuthMiddleware(opts.UserService))
|
dummyGroup.Use(middleware.NewAuthMiddleware(opts.UserService))
|
||||||
|
{
|
||||||
dummyGroup.GET("", handlers.NewDummyHandler())
|
dummyGroup.GET("", handlers.NewDummyHandler())
|
||||||
dummyGroup.POST("/forgot-password", func(c *gin.Context) {
|
dummyGroup.POST("/forgot-password", func(c *gin.Context) {
|
||||||
user := utils.GetUserFromRequest(c)
|
user := utils.GetUserFromRequest(c)
|
||||||
opts.UserService.ForgotPassword(c, user.Id)
|
opts.UserService.SendEmailForgotPassword(c, user.Id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,10 @@ import "time"
|
|||||||
type ActionTokenTarget int
|
type ActionTokenTarget int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ActionTokenTargetForgotPassword ActionTokenTarget = iota
|
_ ActionTokenTarget = iota
|
||||||
|
ActionTokenTargetForgotPassword
|
||||||
ActionTokenTargetLogin2FA
|
ActionTokenTargetLogin2FA
|
||||||
|
ActionTokenVerifyEmail
|
||||||
)
|
)
|
||||||
|
|
||||||
type ActionTokenDTO struct {
|
type ActionTokenDTO struct {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package models
|
|||||||
type UserDTO struct {
|
type UserDTO struct {
|
||||||
Id string
|
Id string
|
||||||
Email string
|
Email string
|
||||||
|
EmailVerified bool
|
||||||
Secret string
|
Secret string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,8 @@ import (
|
|||||||
|
|
||||||
type ActionTokenRepo interface {
|
type ActionTokenRepo interface {
|
||||||
CreateActionToken(ctx context.Context, dto models.ActionTokenDTO) (*models.ActionTokenDTO, error)
|
CreateActionToken(ctx context.Context, dto models.ActionTokenDTO) (*models.ActionTokenDTO, error)
|
||||||
PopActionToken(ctx context.Context, userId, value string, target models.ActionTokenTarget) (*models.ActionTokenDTO, error)
|
GetActionToken(ctx context.Context, value string, target models.ActionTokenTarget) (*models.ActionTokenDTO, error)
|
||||||
|
DeleteActionToken(ctx context.Context, id string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewActionTokenRepo(db integrations.SqlDB) ActionTokenRepo {
|
func NewActionTokenRepo(db integrations.SqlDB) ActionTokenRepo {
|
||||||
@ -43,18 +44,17 @@ func (a *actionTokenRepo) CreateActionToken(ctx context.Context, dto models.Acti
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *actionTokenRepo) PopActionToken(ctx context.Context, userId, value string, target models.ActionTokenTarget) (*models.ActionTokenDTO, error) {
|
func (a *actionTokenRepo) GetActionToken(ctx context.Context, value string, target models.ActionTokenTarget) (*models.ActionTokenDTO, error) {
|
||||||
query := `
|
dto := &models.ActionTokenDTO{Value: value, Target: target}
|
||||||
delete
|
|
||||||
from action_tokens
|
|
||||||
where
|
|
||||||
user_id=$1 and value=$2 and target=$3
|
|
||||||
and CURRENT_TIMESTAMP < expiration
|
|
||||||
returning id;`
|
|
||||||
row := a.db.QueryRowContext(ctx, query, userId, value, target)
|
|
||||||
|
|
||||||
id := ""
|
query := `
|
||||||
err := row.Scan(&id)
|
select id, user_id from action_tokens
|
||||||
|
where
|
||||||
|
value=$1 and target=$2
|
||||||
|
and CURRENT_TIMESTAMP < expiration;`
|
||||||
|
row := a.db.QueryRowContext(ctx, query, value, target)
|
||||||
|
|
||||||
|
err := row.Scan(&dto.Id, &dto.UserId)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -62,10 +62,13 @@ func (a *actionTokenRepo) PopActionToken(ctx context.Context, userId, value stri
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &models.ActionTokenDTO{
|
return dto, nil
|
||||||
Id: id,
|
}
|
||||||
UserId: userId,
|
|
||||||
Value: value,
|
func (a *actionTokenRepo) DeleteActionToken(ctx context.Context, id string) error {
|
||||||
Target: target,
|
query := `delete from action_tokens where id=$1;`
|
||||||
}, nil
|
if _, err := a.db.ExecContext(ctx, query, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ type EventRepo struct {
|
|||||||
kafka *integrations.Kafka
|
kafka *integrations.Kafka
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EventRepo) SendEmailForgotPassword(ctx context.Context, email, actionToken string) error {
|
func (e *EventRepo) sendEmail(ctx context.Context, email, actionToken, eventType string) error {
|
||||||
value := struct {
|
value := struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
@ -29,5 +29,13 @@ func (e *EventRepo) SendEmailForgotPassword(ctx context.Context, email, actionTo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.kafka.SendMessage(ctx, "email_forgot_password", valueBytes)
|
return e.kafka.SendMessage(ctx, eventType, valueBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventRepo) SendEmailForgotPassword(ctx context.Context, email, actionToken string) error {
|
||||||
|
return e.sendEmail(ctx, email, actionToken, "email_forgot_password")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventRepo) SendEmailVerifyEmail(ctx context.Context, email, actionToken string) error {
|
||||||
|
return e.sendEmail(ctx, email, actionToken, "email_verify_email")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import (
|
|||||||
type UserRepo interface {
|
type UserRepo interface {
|
||||||
CreateUser(ctx context.Context, dto models.UserDTO) (*models.UserDTO, error)
|
CreateUser(ctx context.Context, dto models.UserDTO) (*models.UserDTO, error)
|
||||||
UpdateUser(ctx context.Context, userId string, dto models.UserUpdateDTO) error
|
UpdateUser(ctx context.Context, userId string, dto models.UserUpdateDTO) error
|
||||||
|
SetUserEmailVerified(ctx context.Context, userId string) error
|
||||||
GetUserById(ctx context.Context, id string) (*models.UserDTO, error)
|
GetUserById(ctx context.Context, id string) (*models.UserDTO, error)
|
||||||
GetUserByEmail(ctx context.Context, login string) (*models.UserDTO, error)
|
GetUserByEmail(ctx context.Context, login string) (*models.UserDTO, error)
|
||||||
}
|
}
|
||||||
@ -66,15 +67,28 @@ func (u *userRepo) UpdateUser(ctx context.Context, userId string, dto models.Use
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *userRepo) SetUserEmailVerified(ctx context.Context, userId string) error {
|
||||||
|
_, span := u.tracer.Start(ctx, "postgres::SetUserEmailVerified")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
query := `update users set email_verified=true where id = $1;`
|
||||||
|
_, err := u.db.ExecContext(ctx, query, userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u *userRepo) GetUserById(ctx context.Context, id string) (*models.UserDTO, error) {
|
func (u *userRepo) GetUserById(ctx context.Context, id string) (*models.UserDTO, error) {
|
||||||
_, span := u.tracer.Start(ctx, "postgres::GetUserById")
|
_, span := u.tracer.Start(ctx, "postgres::GetUserById")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
query := `select id, email, secret, name from users where id = $1;`
|
query := `select id, email, secret, name, email_verified from users where id = $1;`
|
||||||
row := u.db.QueryRowContext(ctx, query, id)
|
row := u.db.QueryRowContext(ctx, query, id)
|
||||||
|
|
||||||
dto := &models.UserDTO{}
|
dto := &models.UserDTO{}
|
||||||
err := row.Scan(&dto.Id, &dto.Email, &dto.Secret, &dto.Name)
|
err := row.Scan(&dto.Id, &dto.Email, &dto.Secret, &dto.Name, &dto.EmailVerified)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return dto, nil
|
return dto, nil
|
||||||
}
|
}
|
||||||
@ -89,11 +103,11 @@ func (u *userRepo) GetUserByEmail(ctx context.Context, login string) (*models.Us
|
|||||||
_, span := u.tracer.Start(ctx, "postgres::GetUserByEmail")
|
_, span := u.tracer.Start(ctx, "postgres::GetUserByEmail")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
query := `select id, email, secret, name from users where email = $1;`
|
query := `select id, email, secret, name, email_verified from users where email = $1;`
|
||||||
row := u.db.QueryRowContext(ctx, query, login)
|
row := u.db.QueryRowContext(ctx, query, login)
|
||||||
|
|
||||||
dto := &models.UserDTO{}
|
dto := &models.UserDTO{}
|
||||||
err := row.Scan(&dto.Id, &dto.Email, &dto.Secret, &dto.Name)
|
err := row.Scan(&dto.Id, &dto.Email, &dto.Secret, &dto.Name, &dto.EmailVerified)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return dto, nil
|
return dto, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ var (
|
|||||||
ErrUserWrongPassword = fmt.Errorf("wrong password")
|
ErrUserWrongPassword = fmt.Errorf("wrong password")
|
||||||
ErrUserWrongToken = fmt.Errorf("bad user token")
|
ErrUserWrongToken = fmt.Errorf("bad user token")
|
||||||
ErrUserBadPassword = fmt.Errorf("password must contain at least 8 characters")
|
ErrUserBadPassword = fmt.Errorf("password must contain at least 8 characters")
|
||||||
|
ErrUserEmailUnverified = fmt.Errorf("user has not verified email yet")
|
||||||
// ErrUserInternal = fmt.Errorf("unexpected error. contact tech support")
|
// ErrUserInternal = fmt.Errorf("unexpected error. contact tech support")
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,9 +29,12 @@ const (
|
|||||||
type UserService interface {
|
type UserService interface {
|
||||||
CreateUser(ctx context.Context, params UserCreateParams) (*models.UserDTO, error)
|
CreateUser(ctx context.Context, params UserCreateParams) (*models.UserDTO, error)
|
||||||
AuthenticateUser(ctx context.Context, login, password string) (string, error)
|
AuthenticateUser(ctx context.Context, login, password string) (string, error)
|
||||||
ValidateToken(ctx context.Context, tokenStr string) (*models.UserDTO, error)
|
ValidateAuthToken(ctx context.Context, tokenStr string) (*models.UserDTO, error)
|
||||||
|
VerifyEmail(ctx context.Context, actionToken string) error
|
||||||
|
|
||||||
|
SendEmailForgotPassword(ctx context.Context, userId string) error
|
||||||
|
SendEmailVerifyEmail(ctx context.Context, userId string) error
|
||||||
|
|
||||||
ForgotPassword(ctx context.Context, userId string) error
|
|
||||||
ChangePassword(ctx context.Context, userId, oldPassword, newPassword string) error
|
ChangePassword(ctx context.Context, userId, oldPassword, newPassword string) error
|
||||||
ChangePasswordWithToken(ctx context.Context, userId, actionToken, newPassword string) error
|
ChangePasswordWithToken(ctx context.Context, userId, actionToken, newPassword string) error
|
||||||
}
|
}
|
||||||
@ -87,6 +91,7 @@ func (u *userService) CreateUser(ctx context.Context, params UserCreateParams) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
u.sendEmailVerifyEmail(ctx, result.Id, user.Email)
|
||||||
|
|
||||||
u.deps.UserCache.Set(result.Id, *result, cache.Expiration{Ttl: userCacheTtl})
|
u.deps.UserCache.Set(result.Id, *result, cache.Expiration{Ttl: userCacheTtl})
|
||||||
|
|
||||||
@ -106,6 +111,10 @@ func (u *userService) AuthenticateUser(ctx context.Context, email, password stri
|
|||||||
return "", ErrUserWrongPassword
|
return "", ErrUserWrongPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !user.EmailVerified {
|
||||||
|
return "", ErrUserEmailUnverified
|
||||||
|
}
|
||||||
|
|
||||||
payload := utils.JwtPayload{UserId: user.Id}
|
payload := utils.JwtPayload{UserId: user.Id}
|
||||||
jwt, err := u.deps.Jwt.Create(payload)
|
jwt, err := u.deps.Jwt.Create(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -117,8 +126,27 @@ func (u *userService) AuthenticateUser(ctx context.Context, email, password stri
|
|||||||
return jwt, nil
|
return jwt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *userService) ForgotPassword(ctx context.Context, userId string) error {
|
func (u *userService) VerifyEmail(ctx context.Context, actionToken string) error {
|
||||||
user, err := u.getUserById(ctx, userId)
|
token, err := u.deps.ActionTokenRepo.GetActionToken(ctx, actionToken, models.ActionTokenVerifyEmail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if token == nil {
|
||||||
|
return fmt.Errorf("wrong action token")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := u.deps.UserRepo.SetUserEmailVerified(ctx, token.UserId); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: log warnings somehow
|
||||||
|
u.deps.ActionTokenRepo.DeleteActionToken(ctx, token.Id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userService) SendEmailForgotPassword(ctx context.Context, email string) error {
|
||||||
|
// user, err := u.getUserById(ctx, userId)
|
||||||
|
user, err := u.deps.UserRepo.GetUserByEmail(ctx, email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -139,21 +167,54 @@ func (u *userService) ForgotPassword(ctx context.Context, userId string) error {
|
|||||||
return u.deps.EventRepo.SendEmailForgotPassword(ctx, user.Email, actionToken.Value)
|
return u.deps.EventRepo.SendEmailForgotPassword(ctx, user.Email, actionToken.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *userService) sendEmailVerifyEmail(ctx context.Context, userId, email string) error {
|
||||||
|
actionToken, err := u.deps.ActionTokenRepo.CreateActionToken(
|
||||||
|
ctx,
|
||||||
|
models.ActionTokenDTO{
|
||||||
|
UserId: userId,
|
||||||
|
Value: uuid.New().String(),
|
||||||
|
Target: models.ActionTokenVerifyEmail,
|
||||||
|
Expiration: time.Now().Add(1 * time.Hour),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.deps.EventRepo.SendEmailVerifyEmail(ctx, email, actionToken.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userService) SendEmailVerifyEmail(ctx context.Context, email string) error {
|
||||||
|
//user, err := u.getUserById(ctx, userId)
|
||||||
|
user, err := u.deps.UserRepo.GetUserByEmail(ctx, email)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.sendEmailVerifyEmail(ctx, user.Id, user.Email)
|
||||||
|
}
|
||||||
|
|
||||||
func (u *userService) ChangePasswordWithToken(ctx context.Context, userId, actionToken, newPassword string) error {
|
func (u *userService) ChangePasswordWithToken(ctx context.Context, userId, actionToken, newPassword string) error {
|
||||||
user, err := u.getUserById(ctx, userId)
|
user, err := u.getUserById(ctx, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
code, err := u.deps.ActionTokenRepo.PopActionToken(ctx, userId, actionToken, models.ActionTokenTargetForgotPassword)
|
token, err := u.deps.ActionTokenRepo.GetActionToken(ctx, actionToken, models.ActionTokenTargetForgotPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if code == nil {
|
if token == nil {
|
||||||
return fmt.Errorf("wrong user access code")
|
return fmt.Errorf("wrong action token")
|
||||||
}
|
}
|
||||||
|
|
||||||
return u.updatePassword(ctx, *user, newPassword)
|
if err := u.updatePassword(ctx, *user, newPassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: log warnings somehow
|
||||||
|
u.deps.ActionTokenRepo.DeleteActionToken(ctx, token.Id)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *userService) ChangePassword(ctx context.Context, userId, oldPassword, newPassword string) error {
|
func (u *userService) ChangePassword(ctx context.Context, userId, oldPassword, newPassword string) error {
|
||||||
@ -205,7 +266,7 @@ func (u *userService) getUserById(ctx context.Context, userId string) (*models.U
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *userService) ValidateToken(ctx context.Context, tokenStr string) (*models.UserDTO, error) {
|
func (u *userService) ValidateAuthToken(ctx context.Context, tokenStr string) (*models.UserDTO, error) {
|
||||||
if userId, ok := u.deps.JwtCache.Get(tokenStr); ok {
|
if userId, ok := u.deps.JwtCache.Get(tokenStr); ok {
|
||||||
return u.getUserById(ctx, userId)
|
return u.getUserById(ctx, userId)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ create table if not exists users (
|
|||||||
email text unique not null,
|
email text unique not null,
|
||||||
secret text not null,
|
secret text not null,
|
||||||
name text not null,
|
name text not null,
|
||||||
|
email_verified boolean not null default false,
|
||||||
|
|
||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user