twitch-bot/internal/actors/spotify/http.go
2024-04-07 13:37:13 +02:00

89 lines
2.4 KiB
Go

package spotify
import (
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/gofrs/uuid"
"github.com/gorilla/mux"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/oauth2"
)
const (
spotifyRequestTimeout = 2 * time.Second
pkcePBKDFIter = 210000
pkcePBKDFLen = 64
)
var instanceSalt = uuid.Must(uuid.NewV4()).String()
func handleStartAuth(w http.ResponseWriter, r *http.Request) {
channel := mux.Vars(r)["channel"]
pkceVerifier := hex.EncodeToString(pbkdf2.Key([]byte(channel), []byte(instanceSalt), pkcePBKDFIter, pkcePBKDFLen, sha512.New))
redirURL := baseURL.ResolveReference(&url.URL{Path: r.URL.Path})
conf, err := oauthConfig(channel, strings.Split(redirURL.String(), "?")[0])
if err != nil {
logrus.WithError(err).Error("getting Spotify oauth config")
http.Error(w, "unable to get Spotify config for this channel", http.StatusInternalServerError)
return
}
code := r.URL.Query().Get("code")
if code == "" {
http.Redirect(
w, r,
conf.AuthCodeURL(
fmt.Sprintf("%x", sha256.Sum256(append([]byte(conf.ClientID), []byte(channel)...))),
oauth2.S256ChallengeOption(pkceVerifier),
),
http.StatusFound,
)
return
}
token, err := conf.Exchange(
r.Context(),
r.URL.Query().Get("code"),
oauth2.VerifierOption(pkceVerifier),
)
if err != nil {
logrus.WithError(err).Error("getting Spotify oauth token")
http.Error(w, "unable to get Spotify auth token", http.StatusInternalServerError)
return
}
if err = db.StoreEncryptedCoreMeta(strings.Join([]string{"spotify-auth", channel}, ":"), token); err != nil {
logrus.WithError(err).Error("storing Spotify oauth token")
http.Error(w, "unable to store Spotify auth token", http.StatusInternalServerError)
return
}
fmt.Fprintln(w, "Spotify is now authorized for this channel, you can close this page")
}
func oauthConfig(channel, redirectURL string) (conf *oauth2.Config, err error) {
clientID, err := getModuleConfig(actorName, channel).String("clientId")
if err != nil {
return nil, fmt.Errorf("getting clientId for channel: %w", err)
}
return &oauth2.Config{
ClientID: clientID,
Endpoint: oauth2.Endpoint{
AuthURL: "https://accounts.spotify.com/authorize",
TokenURL: "https://accounts.spotify.com/api/token",
},
RedirectURL: redirectURL,
Scopes: []string{"user-read-currently-playing"},
}, nil
}