Giter VIP home page Giter VIP logo

helix's Introduction

helix

A Twitch Helix API client written in Go (Golang).

Tests and Coverage Coverage Status Go Reference

Twitch is always expanding and improving the available endpoints and features for the Helix API. The maintainers of this package will make a best effort approach to implementing new changes as they are released by the Twitch team.

See here for a list of endpoints and features this package supports.

Documentation & Examples

All documentation and usage examples for this package can be found here. If you are looking for the Twitch API docs, see the Twitch Developer website.

Support

Have a question? Need some assistance? Check out our dedicated channel in the Twitch API Discord.

Supported Go Versions

Our support of Go versions is aligned with Go's version release policy. So we will support a major version of Go until there are two newer major releases. We no longer support building this package with unsupported Go versions, as these contain security vulnerabilities which will not be fixed.

Contributions

PRs are very much welcome. Where possible, please include unit tests for any code that is introduced by your PRs. It's also helpful if you can include usage examples in the docs directory.

License

This package is distributed under the terms of the MIT license.

helix's People

Contributors

airforce270 avatar ashkeel avatar chronophylos avatar damoun avatar diphantxm avatar flipkick avatar gempir avatar hennedo avatar intel352 avatar iprodigy avatar jackmcguire1 avatar jbpratt avatar joeyak avatar jupjohn avatar kivutar avatar kloyan avatar mahcks avatar maniarr avatar nicklaw5 avatar pajlada avatar saltlands avatar satont avatar saxypandabear avatar scorfly avatar sputnikplop avatar tylersouthmayd avatar u5surf avatar uranoxyd avatar xjl0 avatar zneix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

helix's Issues

Oauth Enforced

We should update the library to automatically grab oauth tokens. Just ran into this

Error:Unauthorized ErrorStatus:401 ErrorMessage:OAuth token is missing

missing api endpoints

there are multiple new api endpoints
for example moderation ban/unban user
or
moderation add/remove blocked term

also get banned users missing some fields in response (reason)

Both GameAnalytic and gameAnalyticsParams struct are missing fields according to the Twitch docs

According to the docs the GetGameAnalytics method doesn't support a number of optional parameters (eg. after, type, etc.). It also doesn't return a number of response fields (eg. date_range, type).

This comment also suggest that the URL is valid for 1 minute, but according to the docs it should be 5 mins.

I assume these changes were introduced after I implemented that endpoint in this library, as I can't remember them being there 🤔.

Segfault on any request

When I try to use GetClips with a specified start time and end time, I get a segfault:

  startTime := time.Now().Add(time.Duration(-1) * time.Hour)
  endTime := time.Now()

  resp, err := twitchClient.GetClips(&helix.ClipsParams {
     BroadcasterID: accountID,
     First: 5,
     StartedAt: helix.Time{ Time: startTime },
     EndedAt: helix.Time{ Time: endTime },
  })

The same code works if I remove StartedAt and EndedAt.

Here's the error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x98 pc=0x67753f]

goroutine 1 [running]:
github.com/nicklaw5/helix.(*Client).getBaseURL(0x94f500, 0x7061c4, 0x6, 0x6bf460, 0xc000169d90)
        /home/fish/go/pkg/mod/github.com/nicklaw5/[email protected]/helix.go:307 +0xff
github.com/nicklaw5/helix.(*Client).newRequest(0x94f500, 0x705c51, 0x3, 0x7061c4, 0x6, 0x695bc0, 0xc000102e70, 0x40e100, 0x20, 0x6bf460, ...)
        /home/fish/go/pkg/mod/github.com/nicklaw5/[email protected]/helix.go:253 +0x46
github.com/nicklaw5/helix.(*Client).sendRequest(0x94f500, 0x705c51, 0x3, 0x7061c4, 0x6, 0x695a80, 0xc0001103a0, 0x695bc0, 0xc000102e70, 0x40e100,
 ...)
        /home/fish/go/pkg/mod/github.com/nicklaw5/[email protected]/helix.go:158 +0xca
github.com/nicklaw5/helix.(*Client).get(...)
        /home/fish/go/pkg/mod/github.com/nicklaw5/[email protected]/helix.go:133
github.com/nicklaw5/helix.(*Client).GetUsers(0x94f500, 0xc000102e70, 0x2a, 0xc000169ed8, 0x40e198)
        /home/fish/go/pkg/mod/github.com/nicklaw5/[email protected]/users.go:42 +0xa0
main.get_user_id_from_username(0x706a7a, 0x8, 0x7083f0, 0xe)
        /home/fish/twitch-discord-bot/main.go:17 +0xa9
main.main()
        /home/fish/twitch-discord-bot/main.go:38 +0x145
exit status 2

Error nil

Hey Hey,
is it a Bug, that you return everytime a nil as error. It will be a fatal NullPointer exception and the catch of errors will be triggert.

Greeting
Marcel

helix/helix.go

Line 110 in a74e7ff

return client, nil

Add webhook docs

Webhook support was added in #22, however no documentation on how to use these features was introduced.

GetBannedUsers() returns nothing

this does return no data with no error

&{{200 map[Content-Length:[27] Content-Type:[application/json; charset=utf-8] Date:[Mon, 19 Feb 2024 10:07:10 GMT] Ratelimit-Limit:[800] Ratelimit-Remaining:[799] Ratelimit-Reset:[1708337231] Strict-Transport-Security:[max-age=300] Timing-Allow-Origin:[https://www.twitch.tv] Vary:[Accept-Encoding, Origin] X-Cache:[MISS, MISS] X-Cache-Hits:[0, 0] X-Served-By:[cache-bfi-kbfi7400061-BFI, cache-mxp6943-MXP] X-Timer:[S1708337230.992733,VS0,VS0,VE167]]  0 } {[] {}}}

code to test
just check if you get the correct scopes

package main

import (
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/nicklaw5/helix/v2"
)

const (
	clientid  string = ""
	usertoken string = ""
)

func main() {
	api, err := helix.NewClient(&helix.Options{
		ClientID:        clientid,
		UserAccessToken: usertoken,
		RateLimitFunc: func(r *helix.Response) error {
			if r.GetRateLimitRemaining() > 0 {
				return nil
			}
			var reset int64 = int64(r.GetRateLimitReset())
			var now int64 = time.Now().Unix()
			if now < reset {
				var sleep time.Duration = time.Duration(reset - now)
				if sleep > 0 {
					time.Sleep(sleep * time.Second)
				}
			}
			return nil
		},
	})
	if err != nil {
		panic(err)
	}

	valid, self, err := api.ValidateToken(api.GetUserAccessToken())
	if err != nil {
		panic(err)
	} else if !valid {
		panic("user access token not valid")
	}

	fmt.Println(self.Data.Scopes)

	bans, err := api.GetBannedUsers(&helix.BannedUsersParams{
		BroadcasterID: self.Data.UserID,
		UserID:        self.Data.UserID,
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf("%+v\n", bans)

	body, err := httpGet(fmt.Sprintf("https://api.twitch.tv/helix/moderation/banned?first=%d&broadcaster_id=%s", 100, self.Data.UserID), map[string]string{
		"Client-Id":     self.Data.ClientID,
		"Authorization": "Bearer " + api.GetUserAccessToken(),
	})
	if err != nil {
		panic(err)
	}

	fmt.Println(string(body))
}

func httpGet(link string, header map[string]string) ([]byte, error) {
	client := &http.Client{}
	req, err := http.NewRequest(http.MethodGet, link, nil)
	if err != nil {
		return nil, err
	}

	for k, v := range header {
		req.Header.Add(k, v)
	}

	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	body, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	return body, nil
}

Proposal: Add option to pass in state for each API request

First of all, thank you for your amazing work on this library, it's great!

The library in general is written with single-user use cases in mind and doesn't work well in a multi-tenant system, i.e. a microservice which handles API calls for multiple users, bot id's, etc. This is because the state of all authentication mechanisms is stored within the instantiated object it self.

One option would be to create a new object per user, but this creates overhead, eliminates the possibility of some design patterns (i.e. singleton instantiation), and would require a tighter coupling of the driver to the authentication source.

Ideally, all authentication would be stateless and be passed in per call. At the very minimum, user and application tokens being passed in per API call would provide sufficient abstraction insofar that multiple users can be used on the same bot, concurrently, without having to create a new object for every user.

I'm curious to hear what you think @nicklaw5 about this proposal.

Thanks!

Add missing API endpoints

The following API endpoints have been added to Twitch's Helix API docs, but have yet to be implemented in this package:

Any help implementing these endpoints is very much appreciated. All PRs should be accompanied by tests.

GetBlockedTerms() does not use provided data

if r, err := api.GetBlockedTerms(&helix.BlockedTermsParams{
	BroadcasterID: channelID,
	ModeratorID:   selfID,
	First:         10,
}); err != nil {
	panic(err)
} else {
	fmt.Printf("%+v\n", r)
}
&{ResponseCommon:{StatusCode:400 Header:map[Content-Length:[92] Content-Type:[application/json; charset=utf-8] Date:[Sat, 30 Dec 2023 13:22:47 GMT] Strict-Transport-Security:[max-age=300] Timing-Allow-Origin:[https://www.twitch.tv] Vary:[Accept-Encoding, Origin] X-Cache:[MISS, MISS] X-Cache-Hits:[0, 0] X-Served-By:[cache-bfi-kbfi7400051-BFI, cache-mxp6971-MXP] X-Timer:[S1703942568.737446,VS0,VS0,VE174]] Error:Bad Request ErrorStatus:400 ErrorMessage:Missing required parameter "moderator_id"} Data:{Terms:[] Pagination:{Cursor:}}}

How can I use current access token with ValidateToken?

I have an app that runs while I'm streaming, using this library for Twitch integration (thanks!). I'd like to check if the access token is valid before making API calls, since it's quite likely that my token may expire so I can use the refresh token to update it in that case.

How can I get my current access token from the client in order to check if it is valid? Or is there a way to ask the method itself to use the current token rather than a provided one? Is this a feature that would be useful to others?

Parse response "WWW-Authenticate" header

It was brought to my attention by @jscoys via email communication that we don't provide access to the
WWW-Authenticate response header. This response header is sent when an access token has expired and needs to be refreshed. See the Twitch docs for information.

Rather than limiting what headers are providing with each response from the client, it may be a better idea to provide all headers that are returned in a response as a slice.

For example:

type Response struct {
	ResponseCommon
	Data interface{}
}

type ResponseCommon struct {
	StatusCode   int
	Error        string
	ErrorStatus  int
	ErrorMessage string
        Headers      map[string]string
}

OAuth AppAccessToken seemingly not getting automatically added to request headers.

(Reposting because may be urgent). Hey, this doesn't seem to work as you say. I have this code:

if time.Now().After(authExpiry) {
	authExpiry = time.Now()
	res, err := twitch.GetAppAccessToken()
	exitIfError(err)
	authExpiry = authExpiry.Add(time.Duration(res.Data.ExpiresIn) * time.Second)
	log.Insta <- fmt.Sprintf("< | token generated: %s (for %ds)", res.Data.AccessToken, res.Data.ExpiresIn)
}
res, err := twitch.GetStreams(&getStreamsParams)
if err == nil && res.StatusCode != 200 {         // reinterpret HTTP error as actual error
	err = fmt.Errorf("HTTP %d: %s", res.StatusCode, res.ErrorMessage)
}
if err != nil {
	log.Insta <- fmt.Sprintf("x | < : %s", err)
}

and my program output

< | token generated: pfzn88vavmt9hlgy6c33aonf2imako (for 5444223s)
x | < : HTTP 401: OAuth token is missing

during the migration test window that just ended (is back to normal now). Any ideas?

Originally posted by @pyorot in #42 (comment)

Client does not use AppAccessToken on calls that must use AppAccessToken

When creating a client with both an AppAccessToken and a UserAccessToken, the client favors the UserAccessToken over the AppAccessToken. However, certain Helix API calls require the use of an AppAccessToken, i.e. CreateEventSubSubscription.

The client should automatically use the AppAccessToken field for calls that require AppAccessToken, or otherwise allow AppAccessToken to be passed in per call as in #128.

Get Emote Sets missing OwnerID value

The Get Emote Sets function is missing the OwnerID value returned from the API.

The simple solution is to add OwnerID to the Emote type, but that'd mean an empty value in the other calls using the type (i.e. Get Channel Emotes and Get Global Emotes).
The complex solution is to return a different type in the response of Get Emote Sets.

I'm happy to make the change with a PR but I'd like to hear your preferred solution first.

Auto Refreshing Access Tokens

I got my application to work with refreshing tokens on a 401 like twitch suggests in https://dev.twitch.tv/docs/authentication/refresh-tokens/. I originally tried making it work by adding to the library, but I had no idea if it's needed or wanted, so instead I created an HTTPClient that I pass in instead of a token. It is similar to how the spotify library I use does with oauth2 tokens.

If this is helpful for you adding auto token refreshing, feel free to use it. If not feel free to close the issue.

TokenHandler is an wrapper of token storage (used to be database now a json file). handler.GetTwitchToken gets the twitch token from memory, and handler.write saves the file.

old NewClient method

token, err := c.tokens.GetTwitchToken()
if err != nil {
	return fmt.Errorf("could not get twitch token: %w", err)
}

client, err := helix.NewClientWithContext(ctx, &helix.Options{
	ClientID: c.settings.ClientID(),
	UserAccessToken: token.AccessToken,
})

new NewClient method

client, err := helix.NewClientWithContext(ctx, &helix.Options{
	ClientID: c.settings.ClientID(),
	HTTPClient: c.tokens.NewTwitchClient(ctx, c.settings),
})
func (handler *TokenHandler) NewTwitchClient(ctx context.Context, settings *SettingsHandler) *TokenHTTPClient {
	client := &TokenHTTPClient{
		ctx:      ctx,
		logger:   twitchClientTokenLogger,
		settings: settings,
		save:     handler.write,
		token:    handler.GetTwitchToken,
	}

	return client
}

type TokenHTTPClient struct {
	ctx      context.Context
	logger   *slog.Logger
	settings *SettingsHandler
	save     func() error
	token    func() (*Token, error)
}

func (client *TokenHTTPClient) Do(req *http.Request) (*http.Response, error) {
	err := client.replaceAuth(req)
	if err != nil {
		return nil, fmt.Errorf("could not replace auth header: %w", err)
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode == http.StatusUnauthorized {
		err := client.refreshToken(req)
		if err != nil {
			return nil, fmt.Errorf("got unauthorized and could not refresh token: %w", err)
		}

		// Don't defer the body close and do it here
		// because the caller will close the body,
		// but if we're getting a new response, then
		// this body must be closed
		resp.Body.Close()

		err = client.replaceAuth(req)
		if err != nil {
			return nil, fmt.Errorf("could not replace auth header: %w", err)
		}

		return http.DefaultClient.Do(req)
	}

	return resp, nil
}

func (client *TokenHTTPClient) replaceAuth(req *http.Request) error {
	token, err := client.token()
	if err != nil {
		return fmt.Errorf("could not get token: %w", err)
	}

	req.Header.Set("Authorization", fmt.Sprintf("%s %s", "Bearer", token.AccessToken))
	return nil
}

func (client *TokenHTTPClient) refreshToken(req *http.Request) error {
	client.logger.Info("refreshing tokens")

	refreshClient, err := helix.NewClientWithContext(client.ctx, &helix.Options{
		ClientID:     client.settings.ClientID(),
		ClientSecret: client.settings.ClientSecret(),
	})
	if err != nil {
		return fmt.Errorf("could not create client to refresh token: %w", err)
	}

	token, _ := client.token()

	isValid, validateResp, err := refreshClient.ValidateToken(token.RefreshToken)
	if err == nil && isValid {
		return nil
	}

	defer func() {
		err := client.save()
		if err != nil {
			client.logger.Error("could not save token change", err)
		}
	}()

	token.AccessToken = ""

	if err != nil {
		return fmt.Errorf("could not validate token: %w", err)
	}
	if validateResp.StatusCode != http.StatusUnauthorized {
		return fmt.Errorf("could not validate token: %s", validateResp.ErrorMessage)
	}

	resp, err := refreshClient.RefreshUserAccessToken(token.RefreshToken)
	if err != nil || resp.ErrorMessage != "" {
		token.RefreshToken = ""
		if err != nil {
			return fmt.Errorf("could not refresh token: %w", err)
		}
		return fmt.Errorf("could not refresh token: %s", resp.ErrorMessage)
	}

	token.AccessToken = resp.Data.AccessToken
	token.RefreshToken = resp.Data.RefreshToken
	token.TokenExpires = time.Now().UTC().Add(time.Second * time.Duration(resp.Data.ExpiresIn))

	return nil
}

Not able to use the app token with GetStreams endpoint

Hi,

Maybe I’m not using it correctly, but I’m getting an app token (I checked), then I pass it to my client and then I’m calling the GetStream endpoint the same way that in the documentation. The weird thing is that I’m getting a rate limit of 30requests per second instead of 120.

Allow querying multiple channels with `GetChannelInformation`

Twitch didn't document this properly in their docs, but it is possible to specify multiple broadcaster_id parameters (up to 100) and you will get back multiple objects for these, quite similarly to how it works in GetClips function.

I would need to fetch channel information for ~400 channels and doing that in bulks of 100 seems way more efficient than making separate requests for each of these, but I can't find a way to specify more than 1 BroadcasterID in GetChannelInformationParams.

I had an idea to change GetChannelInformationParams.BroadcasterID string to GetChannelInformationParams.BroadcasterIDs []string (also just like it's done in the GetClips function), but this would be a breaking change and existing applications would need to update, would you be okay with me PRing this change or should a different solution be considered instead?
Update to the above: I just decided to open a PR for that.

Endpoint errors but returned error is nil

I ran into an issue where I was calling GetUsers(...) and twitch was returning an error, but the helix library returned an empty error, so I didn't handle it right away.

In my program, I'm wrapping all error responses before I check for an error

func GetHelixError(err error, resp helix.ResponseCommon) error {
	if resp.ErrorStatus == 0 && err == nil {
		return nil
	}

	if err != nil {
		return err
	}

	return fmt.Errorf("error %s with status %d: %s", resp.Error, resp.ErrorStatus, resp.ErrorMessage)
}

// Example call
userResp, err := tokenClient.GetUsers(&helix.UsersParams{Logins: []string{strings.ToLower(botName)}})
err = service.GetHelixError(err, userResp.ResponseCommon)
if err != nil {
	return fmt.Errorf("could not get user info for bot %s: %w", botName, err)
}

Is the expected way to always check resp.ErrorStatus for it being set. This feels more a limitation of twitch not using proper status codes... but it's what we have to work with.

One thing that could be done, but it would require a lot of refactoring, is add a wrap function. Wrapping it on TwitchResponseError would allow a check with errors.Is(err, helix.TwitchResponseError)

var TwitchResponseError = errors.New("twitch returned an error")

func pullErrorFromResponse(resp ResponseCommon) error {
	if resp.ErrorStatus == 0 {
		return nil
	}

	return fmt.Errorf("error %s occured with status %d and message %s: %w", resp.Error, resp.ErrorStatus, resp.ErrorMessage, err)
}

// elsewhere
func (c *Client) GetStreams(params *StreamsParams) (*StreamsResponse, error) {
	resp, err := c.get("/streams", &ManyStreams{}, params)
	if err != nil {
		return nil, err
	}

	streams := &StreamsResponse{}
	resp.HydrateResponseCommon(&streams.ResponseCommon)
	streams.Data.Streams = resp.Data.(*ManyStreams).Streams
	streams.Data.Pagination = resp.Data.(*ManyStreams).Pagination

	// refactor is here \/
	return streams, pullErrorFromResponse(resp.ResponseCommon)
}

Review existing API endpoints

It seems that some of the API endpoint have changed since their original inception in this package. In most circumstances, either additional request parameters have been added, or additional response parameters have been added on Twitch's end.

For example, the Get Clips endpoint has the following request parameters: id, broadcaster_id, game_id, after, before, ended_at, started_at and first. Of which only id is catered for in this package as it currently stands.

We need to review all implemented endpoints to verify they are to current spec.

Add support for all endpoints

Add support for all Helix API endpoints as documented here.

  • GET /bits/leaderboard
  • GET /clips
  • POST /clips
  • POST /entitlements/upload
  • GET /games
  • GET /games/top
  • GET /analytics/games
  • GET /streams
  • GET /streams/metadata
  • GET /users
  • GET /users/follows
  • PUT /users
  • GET /videos

Option to change APIBaseURL

For one of my projects I would have to change the APIBaseURL. Unfortunately, this is not possible because it is a constant. And I haven't found any other way to achieve this behavior.

I would appreciate an option for this in the Options struct

Kind regards David

WebSub webhooks being deprecated

Twitch are deprecating their websub-based webhook functionality, in favour of their EventSub API.

  • No new twitch applications can utilize webhooks
  • Last date of use is September 16, 2021.
  • main branch contains WebSub webhooks code

need to work towards refactoring webhooks.go to support EventSub

Proposal: Add default rate limit function

Currently in the documentation, a proposed rate limit function is provided as such:

func rateLimitCallback(lastResponse *helix.Response) error {
    if lastResponse.GetRateLimitRemaining() > 0 {
        return nil
    }

    var reset64 int64
    reset64 = int64(lastResponse.GetRateLimitReset())

    currentTime := time.Now().Unix()

    if currentTime < reset64 {
        timeDiff := time.Duration(reset64 - currentTime)
        if timeDiff > 0 {
            fmt.Printf("Waiting on rate limit to pass before sending next request (%d seconds)\n", timeDiff)
            time.Sleep(timeDiff * time.Second)
        }
    }

    return nil
}

I propose adding this function, sans the print statement, to the core helix library. The user would still have to specify the use of this function, but it would simplify the usage of this library and reduce boiler plate for the end user. As such, a client might instantiate the client like so:

client, err := helix.NewClient(&helix.Options{
    ClientID:      "your-client-id",
    RateLimitFunc: helix.DefaultRateLimit,
})

where helix.DefaultRateLimit is the example function above.

Thoughts?

Missing response_type "code"

Hi !

The last release broke my project :

197ab07

I use url := AuthBaseURL + "/authorize?response_type=code" because it return an access_token and a refresh_token

with the response_type=token, it doesn't return an refresh_token

Did i miss something ?

I think it would be better to pass this as a param of the function

AddBlockedTerm() does not use provided data

r, err := api.AddBlockedTerm(&helix.AddBlockedTermParams{
	BroadcasterID: self.Data.UserID,
	ModeratorID:   self.Data.UserID,
	Text:          "stream-rise",
})
if err != nil {
	panic(err)
}
log.Printf("%+v\n", r)
&{ResponseCommon:{StatusCode:400 Header:map[Content-Length:[92] Content-Type:[application/json; charset=utf-8] Date:[Sat, 30 Dec 2023 13:32:14 GMT] Strict-Transport-Security:[max-age=300] Timing-Allow-Origin:[https://www.twitch.tv] Vary:[Accept-Encoding, Origin] X-Cache:[MISS, MISS] X-Cache-Hits:[0, 0] X-Served-By:[cache-bfi-kbfi7400075-BFI, cache-mxp6937-MXP] X-Timer:[S1703943134.032044,VS0,VS0,VE164]] Error:Bad Request ErrorStatus:400 ErrorMessage:Missing required parameter "moderator_id"} Data:{Terms:[]}}

When scope for endpoint is missing in the access token, it gets locked in a loop refreshing the access token

Steps to reproduce:

  • request access token with no scopes
  • Set OnUserAccessTokenRefreshed
  • call an endpoint that need a scope (ex. GetFollowedChannels)

What happens:
It gets into an endless loop calling the function passed to OnUserAccessTokenRefreshed.

What should happen:
It should be able to distiguish between unauthorized because of missing scopes and unauthorized because of an expired access token.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.