59 lines
1.4 KiB
Go
59 lines
1.4 KiB
Go
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
|
|
}
|