Skip to content

Commit 94c00c5

Browse files
authored
Added --validate flag to twitch token (#277)
Added --validate flag to twitch token
1 parent 103544e commit 94c00c5

3 files changed

Lines changed: 86 additions & 0 deletions

File tree

cmd/token.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ package cmd
55
import (
66
"fmt"
77
"strconv"
8+
"time"
89

10+
"github.com/fatih/color"
911
"github.com/twitchdev/twitch-cli/internal/login"
1012

1113
"github.com/spf13/cobra"
@@ -15,6 +17,7 @@ import (
1517
var isUserToken bool
1618
var userScopes string
1719
var revokeToken string
20+
var validateToken string
1821
var overrideClientId string
1922
var tokenServerPort int
2023
var tokenServerIP string
@@ -32,6 +35,7 @@ func init() {
3235
loginCmd.Flags().BoolVarP(&isUserToken, "user-token", "u", false, "Whether to login as a user or getting an app access token.")
3336
loginCmd.Flags().StringVarP(&userScopes, "scopes", "s", "", "Space separated list of scopes to request with your user token.")
3437
loginCmd.Flags().StringVarP(&revokeToken, "revoke", "r", "", "Instead of generating a new token, revoke the one passed to this parameter.")
38+
loginCmd.Flags().StringVarP(&validateToken, "validate", "v", "", "Instead of generating a new token, validate the one passed to this parameter.")
3539
loginCmd.Flags().StringVar(&overrideClientId, "client-id", "", "Override/manually set client ID for token actions. By default client ID from CLI config will be used.")
3640
loginCmd.Flags().StringVar(&tokenServerIP, "ip", "localhost", "Manually set the IP address to be binded to for the User Token web server.")
3741
loginCmd.Flags().IntVarP(&tokenServerPort, "port", "p", 3000, "Manually set the port to be used for the User Token web server.")
@@ -67,6 +71,40 @@ func loginCmdRun(cmd *cobra.Command, args []string) error {
6771
p.Token = revokeToken
6872
p.URL = login.RevokeTokenURL
6973
login.CredentialsLogout(p)
74+
} else if validateToken != "" {
75+
p.Token = validateToken
76+
p.URL = login.ValidateTokenURL
77+
r, err := login.ValidateCredentials(p)
78+
if err != nil {
79+
return fmt.Errorf("Failed to validate: %v", err.Error())
80+
}
81+
82+
tokenType := "App Access Token"
83+
if r.UserID != "" {
84+
tokenType = "User Access Token"
85+
}
86+
87+
expiresInTimestamp := time.Now().Add(time.Duration(r.ExpiresIn) * time.Second).UTC().Format(time.RFC1123)
88+
89+
lightYellow := color.New(color.FgHiYellow).PrintfFunc()
90+
white := color.New(color.FgWhite).SprintfFunc()
91+
92+
lightYellow("Client ID: %v\n", white(r.ClientID))
93+
lightYellow("Token Type: %v\n", white(tokenType))
94+
if r.UserID != "" {
95+
lightYellow("User ID: %v\n", white(r.UserID))
96+
lightYellow("User Login: %v\n", white(r.UserLogin))
97+
}
98+
lightYellow("Expires In: %v\n", white("%v (%v)", strconv.FormatInt(r.ExpiresIn, 10), expiresInTimestamp))
99+
100+
if len(r.Scopes) == 0 {
101+
lightYellow("User ID: %v\n", white("None"))
102+
} else {
103+
lightYellow("Scopes:\n")
104+
for _, s := range r.Scopes {
105+
fmt.Println(white("- %v\n", s))
106+
}
107+
}
70108
} else if isUserToken == true {
71109
p.URL = login.UserCredentialsURL
72110
login.UserCredentialsLogin(p, tokenServerIP, webserverPort)

internal/login/login.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ type LoginResponse struct {
5757
ExpiresAt time.Time
5858
}
5959

60+
type ValidateResponse struct {
61+
ClientID string `json:"client_id"`
62+
UserLogin string `json:"login"`
63+
UserID string `json:"user_id"`
64+
Scopes []string `json:"scopes"`
65+
ExpiresIn int64 `json:"expires_in"`
66+
}
67+
6068
const ClientCredentialsURL = "https://id.twitch.tv/oauth2/token?grant_type=client_credentials"
6169

6270
const UserCredentialsURL = "https://id.twitch.tv/oauth2/token?grant_type=authorization_code"
@@ -66,6 +74,8 @@ const RefreshTokenURL = "https://id.twitch.tv/oauth2/token?grant_type=refresh_to
6674

6775
const RevokeTokenURL = "https://id.twitch.tv/oauth2/revoke"
6876

77+
const ValidateTokenURL = "https://id.twitch.tv/oauth2/validate"
78+
6979
func ClientCredentialsLogin(p LoginParameters) (LoginResponse, error) {
7080
u, err := url.Parse(p.URL)
7181
if err != nil {
@@ -216,6 +226,31 @@ func RefreshUserToken(p RefreshParameters) (LoginResponse, error) {
216226
return r, nil
217227
}
218228

229+
func ValidateCredentials(p LoginParameters) (ValidateResponse, error) {
230+
u, err := url.Parse(p.URL)
231+
if err != nil {
232+
log.Fatal(err)
233+
}
234+
235+
resp, err := loginRequestWithHeaders(http.MethodGet, u.String(), nil, []loginHeader{
236+
loginHeader{
237+
Key: "Authorization",
238+
Value: "OAuth " + p.Token,
239+
},
240+
})
241+
if err != nil {
242+
return ValidateResponse{}, err
243+
}
244+
245+
// Handle validate response body
246+
var r ValidateResponse
247+
if err = json.Unmarshal(resp.Body, &r); err != nil {
248+
return ValidateResponse{}, err
249+
}
250+
251+
return r, nil
252+
}
253+
219254
func handleLoginResponse(body []byte) (LoginResponse, error) {
220255
var r AuthorizationResponse
221256
if err := json.Unmarshal(body, &r); err != nil {

internal/login/login_request.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,22 @@ type loginRequestResponse struct {
1616
Body []byte
1717
}
1818

19+
type loginHeader struct {
20+
Key string
21+
Value string
22+
}
23+
1924
func loginRequest(method string, url string, payload io.Reader) (loginRequestResponse, error) {
25+
return loginRequestWithHeaders(method, url, payload, []loginHeader{})
26+
}
27+
28+
func loginRequestWithHeaders(method string, url string, payload io.Reader, headers []loginHeader) (loginRequestResponse, error) {
2029
req, err := request.NewRequest(method, url, payload)
2130

31+
for _, header := range headers {
32+
req.Header.Add(header.Key, header.Value)
33+
}
34+
2235
client := &http.Client{
2336
Timeout: time.Second * 10,
2437
}

0 commit comments

Comments
 (0)