package migration import ( "context" "encoding/json" "fmt" "strings" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" ) var googleDWDScopes = []string{ "https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/drive.readonly", "https://www.googleapis.com/auth/calendar.readonly", "https://www.googleapis.com/auth/contacts.readonly", } // GoogleDWD mints access tokens via a service account with domain-wide delegation. type GoogleDWD struct { jwtConfig *jwt.Config } func NewGoogleDWD(jsonKey string) (*GoogleDWD, error) { jsonKey = strings.TrimSpace(jsonKey) if jsonKey == "" { return nil, nil } conf, err := google.JWTConfigFromJSON([]byte(jsonKey), googleDWDScopes...) if err != nil { return nil, fmt.Errorf("parse google service account: %w", err) } return &GoogleDWD{jwtConfig: conf}, nil } func (g *GoogleDWD) Enabled() bool { return g != nil && g.jwtConfig != nil } func (g *GoogleDWD) AccessToken(ctx context.Context, subjectEmail string) (string, error) { if !g.Enabled() { return "", fmt.Errorf("google domain-wide delegation not configured") } subjectEmail = strings.ToLower(strings.TrimSpace(subjectEmail)) if subjectEmail == "" { return "", fmt.Errorf("subject email required for domain-wide delegation") } conf := *g.jwtConfig conf.Subject = subjectEmail token, err := conf.TokenSource(ctx).Token() if err != nil { return "", fmt.Errorf("google dwd token: %w", err) } if token.AccessToken == "" { return "", fmt.Errorf("google dwd token empty") } return token.AccessToken, nil } func validateServiceAccountJSON(raw string) error { raw = strings.TrimSpace(raw) if raw == "" { return nil } var probe struct { ClientEmail string `json:"client_email"` PrivateKey string `json:"private_key"` } if err := json.Unmarshal([]byte(raw), &probe); err != nil { return fmt.Errorf("invalid service account json: %w", err) } if probe.ClientEmail == "" || probe.PrivateKey == "" { return fmt.Errorf("service account json missing client_email or private_key") } return nil }