chatdesk-ui/auth_v2.169.0/internal/api/provider/twitch.go

155 lines
3.6 KiB
Go

package provider
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/supabase/auth/internal/conf"
"github.com/supabase/auth/internal/utilities"
"golang.org/x/oauth2"
)
// Twitch
const (
defaultTwitchAuthBase = "id.twitch.tv"
defaultTwitchAPIBase = "api.twitch.tv"
)
type twitchProvider struct {
*oauth2.Config
APIHost string
}
type twitchUsers struct {
Data []struct {
ID string `json:"id"`
Login string `json:"login"`
DisplayName string `json:"display_name"`
Type string `json:"type"`
BroadcasterType string `json:"broadcaster_type"`
Description string `json:"description"`
ProfileImageURL string `json:"profile_image_url"`
OfflineImageURL string `json:"offline_image_url"`
ViewCount int `json:"view_count"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
} `json:"data"`
}
// NewTwitchProvider creates a Twitch account provider.
func NewTwitchProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) {
if err := ext.ValidateOAuth(); err != nil {
return nil, err
}
apiHost := chooseHost(ext.URL, defaultTwitchAPIBase)
authHost := chooseHost(ext.URL, defaultTwitchAuthBase)
oauthScopes := []string{
"user:read:email",
}
if scopes != "" {
oauthScopes = append(oauthScopes, strings.Split(scopes, ",")...)
}
return &twitchProvider{
Config: &oauth2.Config{
ClientID: ext.ClientID[0],
ClientSecret: ext.Secret,
Endpoint: oauth2.Endpoint{
AuthURL: authHost + "/oauth2/authorize",
TokenURL: authHost + "/oauth2/token",
},
RedirectURL: ext.RedirectURI,
Scopes: oauthScopes,
},
APIHost: apiHost,
}, nil
}
func (t twitchProvider) GetOAuthToken(code string) (*oauth2.Token, error) {
return t.Exchange(context.Background(), code)
}
func (t twitchProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) {
var u twitchUsers
// Perform http request, because we neeed to set the Client-Id header
req, err := http.NewRequest("GET", t.APIHost+"/helix/users", nil)
if err != nil {
return nil, err
}
// set headers
req.Header.Set("Client-Id", t.Config.ClientID)
req.Header.Set("Authorization", "Bearer "+tok.AccessToken)
client := &http.Client{Timeout: defaultTimeout}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer utilities.SafeClose(resp.Body)
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
return nil, fmt.Errorf("a %v error occurred with retrieving user from twitch", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(body, &u)
if err != nil {
return nil, err
}
if len(u.Data) == 0 {
return nil, errors.New("unable to find user with twitch provider")
}
user := u.Data[0]
data := &UserProvidedData{}
if user.Email != "" {
data.Emails = []Email{{
Email: user.Email,
Verified: true,
Primary: true,
}}
}
data.Metadata = &Claims{
Issuer: t.APIHost,
Subject: user.ID,
Picture: user.ProfileImageURL,
Name: user.Login,
NickName: user.DisplayName,
CustomClaims: map[string]interface{}{
"broadcaster_type": user.BroadcasterType,
"description": user.Description,
"type": user.Type,
"offline_image_url": user.OfflineImageURL,
"view_count": user.ViewCount,
},
// To be deprecated
Slug: user.DisplayName,
AvatarURL: user.ProfileImageURL,
FullName: user.Login,
ProviderId: user.ID,
}
return data, nil
}