Files
IDP-Backend/internal/api/auth/service/auth_service.go

168 lines
4.4 KiB
Go

package service
import (
"context"
"strconv"
"git.logidex.ru/fakz9/logidex-id/internal/api/auth/domain"
userDomain "git.logidex.ru/fakz9/logidex-id/internal/api/user/domain"
"git.logidex.ru/fakz9/logidex-id/internal/phoneutil"
hydraApi "github.com/ory/hydra-client-go"
)
type AuthService interface {
OtpRequest(ctx context.Context, phoneNumber string) error
OtpVerify(ctx context.Context, phoneNumber, code string, loggingChallenge string) (string, error)
AcceptConsent(ctx context.Context, phoneNumber string, challenge string) (string, error)
}
type authService struct {
repo domain.AuthRepository
userRepo userDomain.UserRepository
hydraClient *hydraApi.APIClient
}
func (a authService) AcceptConsent(ctx context.Context, phoneNumber string, challenge string) (string, error) {
phoneNumber, err := phoneutil.ParseAndFormatPhoneNumber(phoneNumber)
if err != nil {
return "", domain.ErrInvalidPhoneNumber{
PhoneNumber: phoneNumber,
Err: err,
}
}
user, err := a.userRepo.GetUserByPhoneNumber(ctx, phoneNumber)
if err != nil {
return "", err
}
if user == nil {
return "", domain.ErrUserNotFound{PhoneNumber: phoneNumber}
}
request := hydraApi.AcceptConsentRequest{}
request.SetGrantScope([]string{"openid"})
request.SetRemember(true)
request.SetRememberFor(3600)
rsp, rawRsp, err := a.hydraClient.AdminApi.
AcceptConsentRequest(ctx).
ConsentChallenge(challenge).
AcceptConsentRequest(request).
Execute()
if err != nil {
return "", err
}
if rawRsp.StatusCode != 200 {
return "", domain.ErrInvalidHydraAccept{
Message: "Hydra response is nil: " + strconv.Itoa(rawRsp.StatusCode),
Uuid: "",
}
}
redirectTo, ok := rsp.GetRedirectToOk()
if !ok || redirectTo == nil {
return "", domain.ErrInvalidHydraAccept{
Message: "Hydra redirectTo is nil",
Uuid: "",
}
}
// TODO: Verify user in the database
_, err = a.userRepo.VerifyUser(ctx, user.Uuid.String())
if err != nil {
return "", err
}
return *redirectTo, nil
}
func (a authService) OtpRequest(ctx context.Context, phoneNumber string) error {
phoneNumber, err := phoneutil.ParseAndFormatPhoneNumber(phoneNumber)
if err != nil {
return domain.ErrInvalidPhoneNumber{
PhoneNumber: phoneNumber,
Err: err,
}
}
user, err := a.userRepo.GetUserByPhoneNumber(ctx, phoneNumber)
//if err != nil {
// return err
//}
if user == nil {
// Create a new user if it does not exist
user, err = a.userRepo.CreateUser(ctx, phoneNumber)
if err != nil {
return err
}
}
code := "123456"
err = a.repo.SaveOtpRequest(ctx, user.Uuid.String(), code)
if err != nil {
return err
}
// TODO implement sending OTP code via SMS
return nil
}
func (a authService) OtpVerify(ctx context.Context, phoneNumber string, code string, loggingChallenge string) (string, error) {
phoneNumber, err := phoneutil.ParseAndFormatPhoneNumber(phoneNumber)
if err != nil {
return "", domain.ErrInvalidPhoneNumber{
PhoneNumber: phoneNumber,
Err: err,
}
}
user, err := a.userRepo.GetUserByPhoneNumber(ctx, phoneNumber)
if err != nil {
return "", err
}
if user == nil {
return "", domain.ErrUserNotFound{PhoneNumber: phoneNumber}
}
otp, err := a.repo.GetOtpRequest(ctx, user.Uuid.String())
if err != nil {
return "", err
}
if otp == nil {
return "", domain.ErrOtpNotFound{Uuid: user.Uuid.String()}
}
if *otp != code {
return "", domain.ErrOtpInvalid{Uuid: user.Uuid.String(), Code: code}
}
request := hydraApi.AcceptLoginRequest{}
request.SetSubject(user.Uuid.String())
request.SetRemember(true)
request.SetRememberFor(3600) // 1 hour
rsp, rawRsp, err := a.hydraClient.AdminApi.
AcceptLoginRequest(ctx).
LoginChallenge(loggingChallenge).
AcceptLoginRequest(request).
Execute()
if err != nil {
return "", err
}
if rawRsp.StatusCode != 200 {
return "", domain.ErrInvalidHydraAccept{
Message: "Hydra response is nil: " + strconv.Itoa(rawRsp.StatusCode),
Uuid: user.Uuid.String(),
}
}
redirectTo, ok := rsp.GetRedirectToOk()
if !ok || redirectTo == nil {
return "", domain.ErrInvalidHydraAccept{
Message: "Hydra redirectTo is nil",
Uuid: user.Uuid.String(),
}
}
return *redirectTo, nil
}
func NewAuthService(repo domain.AuthRepository, userRepo userDomain.UserRepository, hydraClient *hydraApi.APIClient) AuthService {
return &authService{
repo: repo,
userRepo: userRepo,
hydraClient: hydraClient,
}
}