2024-03-04 18:14:38 -05:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2024-03-07 18:45:59 -05:00
|
|
|
"log/slog"
|
2024-03-04 18:14:38 -05:00
|
|
|
"net/http"
|
2024-03-07 18:44:01 -05:00
|
|
|
"strings"
|
2024-03-04 18:14:38 -05:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/google/go-querystring/query"
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Token struct {
|
|
|
|
AccessToken string `json:"access_token"`
|
|
|
|
ExpiresIn int `json:"expires_in"`
|
|
|
|
Expiry time.Time `json:"-"`
|
|
|
|
|
|
|
|
// Only present when using OIDC.
|
|
|
|
IdToken *string `json:"id_token"`
|
|
|
|
|
|
|
|
RefreshToken string `json:"refresh_token"`
|
|
|
|
Scope []string `json:"scope"`
|
|
|
|
TokenType string `json:"token_type"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Token) Valid() bool {
|
|
|
|
return t.AccessToken != "" && t.Expiry.After(time.Now())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Token) Underlying() *oauth2.Token {
|
|
|
|
return &oauth2.Token{
|
|
|
|
AccessToken: t.AccessToken,
|
|
|
|
TokenType: t.TokenType,
|
|
|
|
RefreshToken: t.RefreshToken,
|
|
|
|
Expiry: t.Expiry,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const TokenUrl = "https://id.twitch.tv/oauth2/token"
|
|
|
|
|
|
|
|
type GetTokenParams struct {
|
|
|
|
ClientId string `url:"client_id"`
|
|
|
|
ClientSecret string `url:"client_secret"`
|
|
|
|
Code string `url:"code"`
|
|
|
|
GrantType string `url:"grant_type"`
|
|
|
|
RedirectUri string `url:"redirect_uri"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetToken(params *GetTokenParams) (*Token, error) {
|
|
|
|
v, err := query.Values(params)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-03-07 18:44:01 -05:00
|
|
|
res, err := http.Post(TokenUrl, "application/x-www-form-urlencoded", strings.NewReader(v.Encode()))
|
2024-03-04 18:14:38 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
|
|
|
2024-03-07 18:45:59 -05:00
|
|
|
body := make([]byte, res.ContentLength)
|
|
|
|
if _, err := res.Body.Read(body); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-03-04 18:14:38 -05:00
|
|
|
var token Token
|
2024-03-07 18:45:59 -05:00
|
|
|
if err := json.Unmarshal(body, &token); err != nil {
|
|
|
|
slog.Debug("failed to decode token", slog.Any("error", err), slog.String("body", string(body)))
|
2024-03-04 18:14:38 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
token.Expiry = time.Now().Add(time.Duration(token.ExpiresIn) * time.Second)
|
|
|
|
|
|
|
|
return &token, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type TokenHandler interface {
|
|
|
|
Handle(state string, token string)
|
|
|
|
}
|
|
|
|
|
|
|
|
type TokenHandlerFunc func(state string, token string)
|
|
|
|
|
|
|
|
var _ TokenHandler = (*TokenHandlerFunc)(nil)
|
|
|
|
|
|
|
|
func (f TokenHandlerFunc) Handle(state string, token string) {
|
|
|
|
f(state, token)
|
|
|
|
}
|