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 }