Files
go-ohif-proxy/internal/api/service/auth_service.go

218 lines
5.9 KiB
Go

package service
import (
"errors"
"time"
"golang.org/x/crypto/bcrypt"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/auth"
)
var (
ErrInvalidCredentials = errors.New("invalid credentials")
ErrUserNotFound = errors.New("user not found")
)
// AuthService handles authentication operations
type AuthService struct {
jwtManager *auth.JWTManager
// When you implement database connection, add a db client here
// db *sqlx.DB
}
// NewAuthService creates a new authentication service
func NewAuthService(jwtManager *auth.JWTManager) *AuthService {
return &AuthService{
jwtManager: jwtManager,
}
}
// Login authenticates a user and generates tokens
func (s *AuthService) Login(email, password string) (*models.LoginResponse, error) {
// For now, use hardcoded credentials
// TODO: In a real implementation, you would query the database
if email == "admin" && password == "admin" {
// Create a dummy user
user := &models.User{
ID: "1",
Email: "admin",
Role: "expertise_doctor",
Name: "Admin User",
CreatedAt: time.Now().Format(time.RFC3339),
UpdatedAt: time.Now().Format(time.RFC3339),
}
// Generate tokens
accessToken, err := s.jwtManager.GenerateAccessToken(user.ID, user.Email, user.Role)
if err != nil {
return nil, err
}
refreshToken, err := s.jwtManager.GenerateRefreshToken(user.ID, user.Email, user.Role)
if err != nil {
return nil, err
}
// TODO: In a real implementation, you would store the refresh token in the database
// For example:
// s.storeRefreshToken(user.ID, refreshToken)
// Determine redirect URL based on role
redirectURL := "/viewer"
if user.Role == "ref_doctor" || user.Role == "expertise_doctor" {
redirectURL = "/studylist"
}
return &models.LoginResponse{
AccessToken: accessToken,
RefreshToken: refreshToken,
User: user,
RedirectURL: redirectURL,
}, nil
} else if email == "patient" && password == "patient" {
// Create a patient user
user := &models.User{
ID: "2",
Email: "patient",
Role: "patient",
Name: "Patient User",
CreatedAt: time.Now().Format(time.RFC3339),
UpdatedAt: time.Now().Format(time.RFC3339),
}
// Generate tokens with patient-specific claims
accessToken, err := s.jwtManager.GenerateAccessToken(user.ID, user.Email, user.Role)
if err != nil {
return nil, err
}
refreshToken, err := s.jwtManager.GenerateRefreshToken(user.ID, user.Email, user.Role)
if err != nil {
return nil, err
}
return &models.LoginResponse{
AccessToken: accessToken,
RefreshToken: refreshToken,
User: user,
RedirectURL: "/viewer",
}, nil
} else if email == "doctor" && password == "doctor" {
// Create a referring doctor user
user := &models.User{
ID: "3",
Email: "doctor",
Role: "ref_doctor",
Name: "Doctor User",
CreatedAt: time.Now().Format(time.RFC3339),
UpdatedAt: time.Now().Format(time.RFC3339),
}
// Generate tokens
accessToken, err := s.jwtManager.GenerateAccessToken(user.ID, user.Email, user.Role)
if err != nil {
return nil, err
}
refreshToken, err := s.jwtManager.GenerateRefreshToken(user.ID, user.Email, user.Role)
if err != nil {
return nil, err
}
return &models.LoginResponse{
AccessToken: accessToken,
RefreshToken: refreshToken,
User: user,
RedirectURL: "/studylist",
}, nil
}
return nil, ErrInvalidCredentials
}
// RefreshToken generates a new access token using a refresh token
func (s *AuthService) RefreshToken(refreshToken string) (string, error) {
// Validate the refresh token
claims, err := s.jwtManager.ValidateToken(refreshToken)
if err != nil {
return "", err
}
// Check if token is a refresh token
if claims.TokenType != "refresh" {
return "", errors.New("invalid token type")
}
// TODO: In a real implementation, you would check if the token is in the database and not revoked
// Here we just generate a new access token
accessToken, err := s.jwtManager.GenerateAccessToken(claims.UserID, claims.Email, claims.Role)
if err != nil {
return "", err
}
return accessToken, nil
}
// ValidateToken validates a token and returns the claims
func (s *AuthService) ValidateToken(token string) (*auth.CustomClaims, error) {
return s.jwtManager.ValidateToken(token)
}
// HashPassword hashes a password using bcrypt
func HashPassword(password string) (string, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashedPassword), nil
}
// CheckPassword compares a password with a hash
func CheckPassword(password, hash string) error {
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
}
// Below functions would be implemented when connecting to a real database
// storeRefreshToken stores a refresh token in the database
func (s *AuthService) storeRefreshToken(userID, token string) error {
// TODO: In a real implementation, this would insert a record in the database
// For example:
/*
refreshToken := &models.RefreshToken{
ID: uuid.New().String(),
UserID: userID,
Token: token,
ExpiresAt: time.Now().Add(7 * 24 * time.Hour).Format(time.RFC3339),
IsRevoked: false,
CreatedAt: time.Now().Format(time.RFC3339),
}
_, err := s.db.NamedExec(
`INSERT INTO refresh_tokens (id, user_id, token, expires_at, is_revoked, created_at)
VALUES (:id, :user_id, :token, :expires_at, :is_revoked, :created_at)`,
refreshToken,
)
return err
*/
return nil
}
// revokeRefreshToken marks a refresh token as revoked
func (s *AuthService) revokeRefreshToken(token string) error {
// TODO: In a real implementation, this would update a record in the database
// For example:
/*
_, err := s.db.Exec(
"UPDATE refresh_tokens SET is_revoked = true WHERE token = ?",
token,
)
return err
*/
return nil
}