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, } }