218 lines
5.9 KiB
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
|
|
}
|