diff --git a/configEditor_general.go b/configEditor_general.go
index 3a3d5d2..b98fc99 100644
--- a/configEditor_general.go
+++ b/configEditor_general.go
@@ -17,10 +17,11 @@ import (
type (
configEditorGeneralConfig struct {
- BotEditors []string `json:"bot_editors"`
- BotName *string `json:"bot_name,omitempty"`
- Channels []string `json:"channels"`
- ChannelScopes map[string][]string `json:"channel_scopes"`
+ BotEditors []string `json:"bot_editors"`
+ BotName *string `json:"bot_name,omitempty"`
+ Channels []string `json:"channels"`
+ ChannelScopes map[string][]string `json:"channel_scopes"`
+ ChannelHasToken map[string]bool `json:"channel_has_token"`
}
)
@@ -186,7 +187,12 @@ func configEditorHandleGeneralDeleteAuthToken(w http.ResponseWriter, r *http.Req
}
func configEditorHandleGeneralGet(w http.ResponseWriter, _ *http.Request) {
- channelScopes := make(map[string][]string)
+ resp := configEditorGeneralConfig{
+ BotEditors: config.BotEditors,
+ Channels: config.Channels,
+ ChannelHasToken: make(map[string]bool),
+ ChannelScopes: make(map[string][]string),
+ }
channels, err := accessService.ListPermittedChannels()
if err != nil {
@@ -195,7 +201,12 @@ func configEditorHandleGeneralGet(w http.ResponseWriter, _ *http.Request) {
}
for _, ch := range channels {
- if channelScopes[ch], err = accessService.GetChannelPermissions(ch); err != nil {
+ if resp.ChannelScopes[ch], err = accessService.GetChannelPermissions(ch); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ if resp.ChannelHasToken[ch], err = accessService.HasTokensForChannel(ch); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -206,13 +217,9 @@ func configEditorHandleGeneralGet(w http.ResponseWriter, _ *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
+ resp.BotName = &uName
- if err = json.NewEncoder(w).Encode(configEditorGeneralConfig{
- BotEditors: config.BotEditors,
- BotName: &uName,
- Channels: config.Channels,
- ChannelScopes: channelScopes,
- }); err != nil {
+ if err = json.NewEncoder(w).Encode(resp); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
diff --git a/internal/service/access/access.go b/internal/service/access/access.go
index dfd9560..a92f6c1 100644
--- a/internal/service/access/access.go
+++ b/internal/service/access/access.go
@@ -212,6 +212,40 @@ func (s Service) HasPermissionsForChannel(channel string, scopes ...string) (boo
return true, nil
}
+// HasTokensForChannel retrieves and decrypts stored access- and
+// refresh-tokens to evaluate whether tokens are available. Those
+// tokens are NOT validated in this request, it's just checked whether
+// they are present
+func (s Service) HasTokensForChannel(channel string) (bool, error) {
+ var (
+ err error
+ perm extendedPermission
+ )
+
+ if err = helpers.Retry(func() error {
+ err = s.db.DB().First(&perm, "channel = ?", strings.TrimLeft(channel, "#")).Error
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return backoff.NewErrCannotRetry(ErrChannelNotAuthorized)
+ }
+ return errors.Wrap(err, "getting twitch credential from database")
+ }); err != nil {
+ if errors.Is(err, ErrChannelNotAuthorized) {
+ return false, nil
+ }
+ return false, err
+ }
+
+ if perm.AccessToken, err = s.db.DecryptField(perm.AccessToken); err != nil {
+ return false, errors.Wrap(err, "decrypting access token")
+ }
+
+ if perm.RefreshToken, err = s.db.DecryptField(perm.RefreshToken); err != nil {
+ return false, errors.Wrap(err, "decrypting refresh token")
+ }
+
+ return perm.AccessToken != "" && perm.RefreshToken != "", nil
+}
+
func (s Service) ListPermittedChannels() (out []string, err error) {
var perms []extendedPermission
if err = helpers.Retry(func() error {
diff --git a/src/generalConfig.vue b/src/generalConfig.vue
index fe21e36..65c6998 100644
--- a/src/generalConfig.vue
+++ b/src/generalConfig.vue
@@ -25,7 +25,14 @@
{{ channel }}
+
- Channel is missing {{ missingExtendedScopes(channel).length }} extended permissions.
- Click pencil to change granted permissions.
+
+ Bot is not authorized to access Twitch on behalf of this channels owner (tokens are missing).
+ Click pencil to grant permissions.
+
+
+ Channel is missing {{ missingExtendedScopes(channel).length }} extended permissions.
+ Click pencil to change granted permissions.
+