feat: add oauth session impl in auth service

This commit is contained in:
Stavros
2026-03-21 12:55:22 +02:00
parent 1a1712eaeb
commit 2491d453cf

View File

@@ -17,8 +17,17 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"golang.org/x/oauth2"
) )
type OAuthPendingSession struct {
State string
Verifier string
Token *oauth2.Token
Service *OAuthServiceImpl
ExpiresAt time.Time
}
type LdapGroupsCache struct { type LdapGroupsCache struct {
Groups []string Groups []string
Expires time.Time Expires time.Time
@@ -45,17 +54,20 @@ type AuthServiceConfig struct {
} }
type AuthService struct { type AuthService struct {
config AuthServiceConfig config AuthServiceConfig
docker *DockerService docker *DockerService
loginAttempts map[string]*LoginAttempt loginAttempts map[string]*LoginAttempt
ldapGroupsCache map[string]*LdapGroupsCache ldapGroupsCache map[string]*LdapGroupsCache
loginMutex sync.RWMutex oauthPendingSessions map[string]*OAuthPendingSession
ldapGroupsMutex sync.RWMutex oauthMutex sync.RWMutex
ldap *LdapService loginMutex sync.RWMutex
queries *repository.Queries ldapGroupsMutex sync.RWMutex
ldap *LdapService
queries *repository.Queries
oauthBroker *OAuthBrokerService
} }
func NewAuthService(config AuthServiceConfig, docker *DockerService, ldap *LdapService, queries *repository.Queries) *AuthService { func NewAuthService(config AuthServiceConfig, docker *DockerService, ldap *LdapService, queries *repository.Queries, oauthBroker *OAuthBrokerService) *AuthService {
return &AuthService{ return &AuthService{
config: config, config: config,
docker: docker, docker: docker,
@@ -63,10 +75,12 @@ func NewAuthService(config AuthServiceConfig, docker *DockerService, ldap *LdapS
ldapGroupsCache: make(map[string]*LdapGroupsCache), ldapGroupsCache: make(map[string]*LdapGroupsCache),
ldap: ldap, ldap: ldap,
queries: queries, queries: queries,
oauthBroker: oauthBroker,
} }
} }
func (auth *AuthService) Init() error { func (auth *AuthService) Init() error {
go auth.CleanupOAuthSessionsRoutine()
return nil return nil
} }
@@ -553,3 +567,135 @@ func (auth *AuthService) IsBypassedIP(acls config.AppIP, ip string) bool {
tlog.App.Debug().Str("ip", ip).Msg("IP not in bypass list, continuing with authentication") tlog.App.Debug().Str("ip", ip).Msg("IP not in bypass list, continuing with authentication")
return false return false
} }
func (auth *AuthService) NewOAuthSession(serviceName string) (string, error) {
service, ok := auth.oauthBroker.GetService(serviceName)
if !ok {
return "", fmt.Errorf("oauth service not found: %s", serviceName)
}
sessionId, err := uuid.NewRandom()
if err != nil {
return "", fmt.Errorf("failed to generate session ID: %w", err)
}
state := uuid.New().String()
verifier := uuid.New().String()
auth.oauthMutex.Lock()
auth.oauthPendingSessions[sessionId.String()] = &OAuthPendingSession{
State: state,
Verifier: verifier,
Service: &service,
ExpiresAt: time.Now().Add(1 * time.Hour),
}
auth.oauthMutex.Unlock()
return sessionId.String(), nil
}
func (auth *AuthService) GetOAuthURL(sessionId string) (string, error) {
auth.oauthMutex.RLock()
defer auth.oauthMutex.RUnlock()
session, exists := auth.oauthPendingSessions[sessionId]
if !exists {
return "", fmt.Errorf("oauth session not found: %s", sessionId)
}
if time.Now().After(session.ExpiresAt) {
delete(auth.oauthPendingSessions, sessionId)
return "", fmt.Errorf("oauth session expired: %s", sessionId)
}
return (*session.Service).GetAuthURL(session.State, session.Verifier), nil
}
func (auth *AuthService) GetOAuthToken(sessionId string, code string) (*oauth2.Token, error) {
auth.oauthMutex.RLock()
session, exists := auth.oauthPendingSessions[sessionId]
auth.oauthMutex.RUnlock()
if !exists {
return nil, fmt.Errorf("oauth session not found: %s", sessionId)
}
if time.Now().After(session.ExpiresAt) {
auth.oauthMutex.Lock()
delete(auth.oauthPendingSessions, sessionId)
auth.oauthMutex.Unlock()
return nil, fmt.Errorf("oauth session expired: %s", sessionId)
}
token, err := (*session.Service).GetToken(code, session.Verifier)
if err != nil {
return nil, fmt.Errorf("failed to exchange code for token: %w", err)
}
auth.oauthMutex.Lock()
session.Token = token
auth.oauthMutex.Unlock()
return token, nil
}
func (auth *AuthService) GetOAuthUserinfo(sessionId string) (config.Claims, error) {
auth.oauthMutex.RLock()
session, exists := auth.oauthPendingSessions[sessionId]
auth.oauthMutex.RUnlock()
if !exists {
return config.Claims{}, fmt.Errorf("oauth session not found: %s", sessionId)
}
if time.Now().After(session.ExpiresAt) {
auth.oauthMutex.Lock()
delete(auth.oauthPendingSessions, sessionId)
auth.oauthMutex.Unlock()
return config.Claims{}, fmt.Errorf("oauth session expired: %s", sessionId)
}
if session.Token == nil {
return config.Claims{}, fmt.Errorf("oauth token not found for session: %s", sessionId)
}
userinfo, err := (*session.Service).GetUserinfo(session.Token)
if err != nil {
return config.Claims{}, fmt.Errorf("failed to get userinfo: %w", err)
}
auth.oauthMutex.Lock()
delete(auth.oauthPendingSessions, sessionId)
auth.oauthMutex.Unlock()
return userinfo, nil
}
func (auth *AuthService) EndOAuthSession(sessionId string) {
auth.oauthMutex.Lock()
delete(auth.oauthPendingSessions, sessionId)
auth.oauthMutex.Unlock()
}
func (auth *AuthService) CleanupOAuthSessionsRoutine() {
ticker := time.NewTicker(30 * time.Minute)
defer ticker.Stop()
for range ticker.C {
auth.oauthMutex.Lock()
defer auth.oauthMutex.Unlock()
now := time.Now()
for sessionId, session := range auth.oauthPendingSessions {
if now.After(session.ExpiresAt) {
delete(auth.oauthPendingSessions, sessionId)
}
}
}
}