Files
b0esche_cloud/go_cloud/internal/auth/auth.go
Leon Bösche 5cb99815a0 idle
2026-01-08 13:07:07 +01:00

97 lines
2.5 KiB
Go

package auth
import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"time"
"go.b0esche.cloud/backend/internal/config"
"go.b0esche.cloud/backend/internal/database"
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"
)
type OIDCService struct {
provider *oidc.Provider
oauth2Config oauth2.Config
db *database.DB // Assume we have a DB wrapper
}
func NewOIDCService(cfg *config.Config, db *database.DB) (*OIDCService, error) {
ctx := context.Background()
provider, err := oidc.NewProvider(ctx, cfg.OIDCIssuerURL)
if err != nil {
return nil, fmt.Errorf("failed to get OIDC provider: %w", err)
}
oauth2Config := oauth2.Config{
ClientID: cfg.OIDCClientID,
ClientSecret: cfg.OIDCClientSecret,
RedirectURL: cfg.OIDCRedirectURL, // Add to config
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
return &OIDCService{
provider: provider,
oauth2Config: oauth2Config,
db: db,
}, nil
}
func (s *OIDCService) LoginURL(state string) string {
return s.oauth2Config.AuthCodeURL(state)
}
func (s *OIDCService) HandleCallback(ctx context.Context, code, state string) (*database.User, *database.Session, error) {
oauth2Token, err := s.oauth2Config.Exchange(ctx, code)
if err != nil {
return nil, nil, fmt.Errorf("failed to exchange code: %w", err)
}
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok {
return nil, nil, fmt.Errorf("no id_token in token response")
}
idToken, err := s.provider.Verifier(&oidc.Config{ClientID: s.oauth2Config.ClientID}).Verify(ctx, rawIDToken)
if err != nil {
return nil, nil, fmt.Errorf("failed to verify ID token: %w", err)
}
var claims struct {
Sub string `json:"sub"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Name string `json:"name"`
}
if err := idToken.Claims(&claims); err != nil {
return nil, nil, fmt.Errorf("failed to parse claims: %w", err)
}
user, err := s.db.GetOrCreateUser(ctx, claims.Sub, claims.Email, claims.Name)
if err != nil {
return nil, nil, err
}
session, err := s.db.CreateSession(ctx, user.ID, time.Now().Add(15*time.Minute))
if err != nil {
return nil, nil, err
}
return user, session, nil
}
// GenerateState generates a secure random state string
func GenerateState() (string, error) {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(b), nil
}