129 lines
4.5 KiB
Go
129 lines
4.5 KiB
Go
package provider
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/supabase/auth/internal/utilities"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
var defaultTimeout time.Duration = time.Second * 10
|
|
|
|
func init() {
|
|
timeoutStr := os.Getenv("GOTRUE_INTERNAL_HTTP_TIMEOUT")
|
|
if timeoutStr != "" {
|
|
if timeout, err := time.ParseDuration(timeoutStr); err != nil {
|
|
log.Fatalf("error loading GOTRUE_INTERNAL_HTTP_TIMEOUT: %v", err.Error())
|
|
} else if timeout != 0 {
|
|
defaultTimeout = timeout
|
|
}
|
|
}
|
|
}
|
|
|
|
type Claims struct {
|
|
// Reserved claims
|
|
Issuer string `json:"iss,omitempty" structs:"iss,omitempty"`
|
|
Subject string `json:"sub,omitempty" structs:"sub,omitempty"`
|
|
Aud string `json:"aud,omitempty" structs:"aud,omitempty"`
|
|
Iat float64 `json:"iat,omitempty" structs:"iat,omitempty"`
|
|
Exp float64 `json:"exp,omitempty" structs:"exp,omitempty"`
|
|
|
|
// Default profile claims
|
|
Name string `json:"name,omitempty" structs:"name,omitempty"`
|
|
FamilyName string `json:"family_name,omitempty" structs:"family_name,omitempty"`
|
|
GivenName string `json:"given_name,omitempty" structs:"given_name,omitempty"`
|
|
MiddleName string `json:"middle_name,omitempty" structs:"middle_name,omitempty"`
|
|
NickName string `json:"nickname,omitempty" structs:"nickname,omitempty"`
|
|
PreferredUsername string `json:"preferred_username,omitempty" structs:"preferred_username,omitempty"`
|
|
Profile string `json:"profile,omitempty" structs:"profile,omitempty"`
|
|
Picture string `json:"picture,omitempty" structs:"picture,omitempty"`
|
|
Website string `json:"website,omitempty" structs:"website,omitempty"`
|
|
Gender string `json:"gender,omitempty" structs:"gender,omitempty"`
|
|
Birthdate string `json:"birthdate,omitempty" structs:"birthdate,omitempty"`
|
|
ZoneInfo string `json:"zoneinfo,omitempty" structs:"zoneinfo,omitempty"`
|
|
Locale string `json:"locale,omitempty" structs:"locale,omitempty"`
|
|
UpdatedAt string `json:"updated_at,omitempty" structs:"updated_at,omitempty"`
|
|
Email string `json:"email,omitempty" structs:"email,omitempty"`
|
|
EmailVerified bool `json:"email_verified,omitempty" structs:"email_verified"`
|
|
Phone string `json:"phone,omitempty" structs:"phone,omitempty"`
|
|
PhoneVerified bool `json:"phone_verified,omitempty" structs:"phone_verified"`
|
|
|
|
// Custom profile claims that are provider specific
|
|
CustomClaims map[string]interface{} `json:"custom_claims,omitempty" structs:"custom_claims,omitempty"`
|
|
|
|
// TODO: Deprecate in next major release
|
|
FullName string `json:"full_name,omitempty" structs:"full_name,omitempty"`
|
|
AvatarURL string `json:"avatar_url,omitempty" structs:"avatar_url,omitempty"`
|
|
Slug string `json:"slug,omitempty" structs:"slug,omitempty"`
|
|
ProviderId string `json:"provider_id,omitempty" structs:"provider_id,omitempty"`
|
|
UserNameKey string `json:"user_name,omitempty" structs:"user_name,omitempty"`
|
|
}
|
|
|
|
// Email is a struct that provides information on whether an email is verified or is the primary email address
|
|
type Email struct {
|
|
Email string
|
|
Verified bool
|
|
Primary bool
|
|
}
|
|
|
|
// UserProvidedData is a struct that contains the user's data returned from the oauth provider
|
|
type UserProvidedData struct {
|
|
Emails []Email
|
|
Metadata *Claims
|
|
}
|
|
|
|
// Provider is an interface for interacting with external account providers
|
|
type Provider interface {
|
|
AuthCodeURL(string, ...oauth2.AuthCodeOption) string
|
|
}
|
|
|
|
// OAuthProvider specifies additional methods needed for providers using OAuth
|
|
type OAuthProvider interface {
|
|
AuthCodeURL(string, ...oauth2.AuthCodeOption) string
|
|
GetUserData(context.Context, *oauth2.Token) (*UserProvidedData, error)
|
|
GetOAuthToken(string) (*oauth2.Token, error)
|
|
}
|
|
|
|
func chooseHost(base, defaultHost string) string {
|
|
if base == "" {
|
|
return "https://" + defaultHost
|
|
}
|
|
|
|
baseLen := len(base)
|
|
if base[baseLen-1] == '/' {
|
|
return base[:baseLen-1]
|
|
}
|
|
|
|
return base
|
|
}
|
|
|
|
func makeRequest(ctx context.Context, tok *oauth2.Token, g *oauth2.Config, url string, dst interface{}) error {
|
|
client := g.Client(ctx, tok)
|
|
client.Timeout = defaultTimeout
|
|
res, err := client.Get(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer utilities.SafeClose(res.Body)
|
|
|
|
bodyBytes, _ := io.ReadAll(res.Body)
|
|
res.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
|
|
|
if res.StatusCode < http.StatusOK || res.StatusCode >= http.StatusMultipleChoices {
|
|
return httpError(res.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
if err := json.NewDecoder(res.Body).Decode(dst); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|