1package spotify_proxyclient 2 3import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "net/http" 9 "net/url" 10 "time" 11 12 "github.com/ambientsound/visp/spotify/proxyserver" 13 "golang.org/x/oauth2" 14) 15 16const ( 17 SubtractTTL = time.Minute * 2 18) 19 20// How much time an oauth2 token has to live before it should be refreshed. 21func TokenTTL(token *oauth2.Token) time.Duration { 22 return token.Expiry.Sub(time.Now()) - SubtractTTL 23} 24 25// Decode a base64+JSON encoded oauth2 token into a struct. 26func DecodeTokenString(tokenstr string) (*oauth2.Token, error) { 27 data, err := base64.RawURLEncoding.DecodeString(tokenstr) 28 if err != nil { 29 return nil, fmt.Errorf("decode base64 string: %w", err) 30 } 31 32 tok := &oauth2.Token{} 33 err = json.Unmarshal(data, tok) 34 if err != nil { 35 return nil, fmt.Errorf("token error: %w", err) 36 } 37 38 return tok, nil 39} 40 41// Refresh a Spotify oauth2 token using the visp Spotify authorization proxy server. 42func RefreshToken(server string, client *http.Client, token *oauth2.Token) (*oauth2.Token, error) { 43 // Construct endpoint url 44 u, err := url.Parse(server) 45 if err != nil { 46 return nil, fmt.Errorf("parse Spotify auth url: %s", err) 47 } 48 u.Path = spotify_proxyserver.RefreshURL 49 50 // Encode request JSON 51 payload, err := json.Marshal(token) 52 if err != nil { 53 return nil, fmt.Errorf("encode token: %s", err) 54 } 55 56 // Make the request 57 resp, err := client.Post(u.String(), "application/json", bytes.NewReader(payload)) 58 if err != nil { 59 return nil, fmt.Errorf("authorization proxy error: %s", err) 60 } 61 62 // Read response JSON 63 response := &spotify_proxyserver.Response{} 64 err = json.NewDecoder(resp.Body).Decode(response) 65 if err != nil { 66 return nil, fmt.Errorf("decode data from authorization proxy: %s", err) 67 } 68 if resp.StatusCode != http.StatusOK { 69 return nil, fmt.Errorf("error from authorization proxy: %s: %s", resp.Status, response.Error) 70 } 71 72 return DecodeTokenString(response.Token) 73} 74