feat: impl bearer auth middleware

This commit is contained in:
2025-11-26 01:09:42 +00:00
parent 81e3f7af75
commit 389fe35a0a
9 changed files with 161 additions and 25 deletions

View File

@@ -5,11 +5,13 @@ import (
"encoding/hex"
"errors"
"github.com/get-drexa/drexa/internal/user"
"github.com/google/uuid"
"github.com/uptrace/bun"
)
type LoginResult struct {
User User
User *user.User
AccessToken string
RefreshToken string
}
@@ -19,6 +21,7 @@ var ErrUserExists = errors.New("user already exists")
type Service struct {
db *bun.DB
userService *user.Service
tokenConfig TokenConfig
}
@@ -28,32 +31,33 @@ type registerOptions struct {
password string
}
func NewService(db *bun.DB, tokenConfig TokenConfig) *Service {
func NewService(db *bun.DB, userService *user.Service, tokenConfig TokenConfig) *Service {
return &Service{
db: db,
userService: userService,
tokenConfig: tokenConfig,
}
}
func (s *Service) LoginWithEmailAndPassword(ctx context.Context, email, password string) (*LoginResult, error) {
var user User
var u user.User
err := s.db.NewSelect().Model(&user).Where("email = ?", email).Scan(ctx)
err := s.db.NewSelect().Model(&u).Where("email = ?", email).Scan(ctx)
if err != nil {
return nil, err
}
ok, err := VerifyPassword(password, user.Password)
ok, err := VerifyPassword(password, u.Password)
if err != nil || !ok {
return nil, ErrInvalidCredentials
}
at, err := GenerateAccessToken(&user, &s.tokenConfig)
at, err := GenerateAccessToken(&u, &s.tokenConfig)
if err != nil {
return nil, err
}
rt, err := GenerateRefreshToken(&user, &s.tokenConfig)
rt, err := GenerateRefreshToken(&u, &s.tokenConfig)
if err != nil {
return nil, err
}
@@ -64,19 +68,19 @@ func (s *Service) LoginWithEmailAndPassword(ctx context.Context, email, password
}
return &LoginResult{
User: user,
User: &u,
AccessToken: at,
RefreshToken: hex.EncodeToString(rt.Token),
}, nil
}
func (s *Service) Register(ctx context.Context, opts registerOptions) (*LoginResult, error) {
user := User{
u := user.User{
Email: opts.email,
DisplayName: opts.displayName,
}
exists, err := s.db.NewSelect().Model(&user).Where("email = ?", opts.email).Exists(ctx)
exists, err := s.db.NewSelect().Model(&u).Where("email = ?", opts.email).Exists(ctx)
if err != nil {
return nil, err
}
@@ -84,29 +88,43 @@ func (s *Service) Register(ctx context.Context, opts registerOptions) (*LoginRes
return nil, ErrUserExists
}
user.Password, err = HashPassword(opts.password)
u.Password, err = HashPassword(opts.password)
if err != nil {
return nil, err
}
_, err = s.db.NewInsert().Model(&user).Exec(ctx)
_, err = s.db.NewInsert().Model(&u).Exec(ctx)
if err != nil {
return nil, err
}
at, err := GenerateAccessToken(&user, &s.tokenConfig)
at, err := GenerateAccessToken(&u, &s.tokenConfig)
if err != nil {
return nil, err
}
rt, err := GenerateRefreshToken(&user, &s.tokenConfig)
rt, err := GenerateRefreshToken(&u, &s.tokenConfig)
if err != nil {
return nil, err
}
return &LoginResult{
User: user,
User: &u,
AccessToken: at,
RefreshToken: hex.EncodeToString(rt.Token),
}, nil
}
func (s *Service) AuthenticateWithAccessToken(ctx context.Context, token string) (*user.User, error) {
claims, err := ParseAccessToken(token, &s.tokenConfig)
if err != nil {
return nil, err
}
id, err := uuid.Parse(claims.Subject)
if err != nil {
return nil, newInvalidAccessTokenError(err)
}
return s.userService.UserByID(ctx, id)
}