package auth import ( "context" "crypto/rand" "encoding/hex" "codeberg.org/nextgo/dbots/internal/db" "codeberg.org/nextgo/dbots/internal/discord" ) type Service struct { q *db.Queries client *discord.Client } func NewService(q *db.Queries, client *discord.Client) *Service { return &Service{q: q, client: client} } // Callback exchanges the OAuth code for a Discord access token, // fetches the Discord user, and upserts them in the database. // It returns the db.User and the raw Discord access token // (needed so the caller can store it in the session if desired). func (s *Service) Callback(ctx context.Context, code string) (*db.User, *discord.TokenResponse, error) { tok, err := s.client.ExchangeCode(ctx, code) if err != nil { return nil, nil, err } dUser, err := s.client.GetCurrentUser(ctx, tok.AccessToken) if err != nil { return nil, nil, err } user, err := s.q.GetUser(ctx, dUser.ID) if err != nil { // First login — create the user. user, err = s.q.CreateUser(ctx, db.CreateUserParams{ ID: dUser.ID, Username: dUser.Username, }) if err != nil { return nil, nil, err } } return user, tok, nil } // GenerateState returns a cryptographically random hex string for // the OAuth2 state parameter. func GenerateState() (string, error) { b := make([]byte, 16) if _, err := rand.Read(b); err != nil { return "", err } return hex.EncodeToString(b), nil }