1- package globalping
1+ package api
22
33import (
4+ "context"
45 "crypto/rand"
56 "crypto/sha256"
67 "encoding/base64"
@@ -11,9 +12,9 @@ import (
1112 "strconv"
1213 "strings"
1314 "time"
14- )
1515
16- var timeNow = time .Now
16+ "github.com/jsdelivr/globalping-cli/storage"
17+ )
1718
1819var (
1920 ErrTypeExchangeFailed = "exchange_failed"
2425 ErrTypeNotAuthorized = "not_authorized"
2526)
2627
27- type Token struct {
28- AccessToken string `json:"access_token"`
29- TokenType string `json:"token_type,omitempty"`
30- RefreshToken string `json:"refresh_token,omitempty"`
31- ExpiresIn int64 `json:"expires_in,omitempty"`
32- Expiry time.Time `json:"expiry,omitempty"`
33- }
34-
3528type AuthorizeError struct {
3629 Code int `json:"-"`
3730 ErrorType string `json:"error"`
@@ -47,23 +40,28 @@ type AuthorizeResponse struct {
4740 CallbackURL string
4841}
4942
50- func (c * client ) Authorize (callback func (error )) (* AuthorizeResponse , error ) {
43+ func (c * client ) Authorize (ctx context. Context , callback func (error )) (* AuthorizeResponse , error ) {
5144 verifier := generateVerifier ()
5245 mux := http .NewServeMux ()
5346 server := & http.Server {
5447 Handler : mux ,
5548 }
5649 callbackURL := ""
5750 mux .HandleFunc ("/callback" , func (w http.ResponseWriter , req * http.Request ) {
58- req .ParseForm ()
59- token , err := c .exchange (req .Form , verifier , callbackURL )
51+ err := req .ParseForm ()
52+ if err != nil {
53+ http .Error (w , "Bad Request" , http .StatusBadRequest )
54+ callback (& AuthorizeError {ErrorType : "failed to parse form" , Description : err .Error ()})
55+ return
56+ }
57+ token , err := c .exchange (ctx , req .Form , verifier , callbackURL )
6058 if err != nil {
6159 http .Redirect (w , req , c .dashboardURL + "/authorize/error" , http .StatusFound )
6260 } else {
6361 http .Redirect (w , req , c .dashboardURL + "/authorize/success" , http .StatusFound )
6462 }
6563 go func () {
66- server .Shutdown (req . Context ())
64+ server .Shutdown (context . Background ())
6765 if err == nil {
6866 c .updateToken (token )
6967 }
@@ -105,9 +103,9 @@ func (c *client) Authorize(callback func(error)) (*AuthorizeResponse, error) {
105103 }, nil
106104}
107105
108- func (c * client ) TokenIntrospection (token string ) (* IntrospectionResponse , error ) {
106+ func (c * client ) TokenIntrospection (ctx context. Context , token string ) (* IntrospectionResponse , error ) {
109107 if token == "" {
110- t , err := c .getToken ()
108+ t , err := c .getToken (ctx )
111109 if err != nil {
112110 return nil , & AuthorizeError {
113111 ErrorType : ErrTypeNotAuthorized ,
@@ -124,25 +122,25 @@ func (c *client) TokenIntrospection(token string) (*IntrospectionResponse, error
124122 Description : "client is not authorized" ,
125123 }
126124 }
127- return c .introspection (token )
125+ return c .introspection (ctx , token )
128126}
129127
130- func (c * client ) Logout () error {
128+ func (c * client ) Logout (ctx context. Context ) error {
131129 c .mu .RLock ()
132130 t := c .token
133131 c .mu .RUnlock ()
134132 if t == nil {
135133 return nil
136134 }
137- err := c .RevokeToken (t .RefreshToken )
135+ err := c .RevokeToken (ctx , t .RefreshToken )
138136 if err != nil {
139137 return err
140138 }
141139 c .updateToken (nil )
142140 return nil
143141}
144142
145- func (c * client ) exchange (form url.Values , verifier string , redirect string ) (* Token , error ) {
143+ func (c * client ) exchange (ctx context. Context , form url.Values , verifier string , redirect string ) (* storage. Token , error ) {
146144 if form .Get ("error" ) != "" {
147145 return nil , & AuthorizeError {
148146 ErrorType : form .Get ("error" ),
@@ -163,7 +161,7 @@ func (c *client) exchange(form url.Values, verifier string, redirect string) (*T
163161 q .Set ("code_verifier" , verifier )
164162 q .Set ("grant_type" , "authorization_code" )
165163 q .Set ("redirect_uri" , redirect )
166- req , err := http .NewRequest ( "POST" , c .authURL + "/oauth/token" , strings .NewReader (q .Encode ()))
164+ req , err := http .NewRequestWithContext ( ctx , "POST" , c .authURL + "/oauth/token" , strings .NewReader (q .Encode ()))
167165 if err != nil {
168166 return nil , & AuthorizeError {
169167 ErrorType : ErrTypeExchangeFailed ,
@@ -188,7 +186,7 @@ func (c *client) exchange(form url.Values, verifier string, redirect string) (*T
188186 json .NewDecoder (resp .Body ).Decode (err )
189187 return nil , err
190188 }
191- t := & Token {}
189+ t := & storage. Token {}
192190 err = json .NewDecoder (resp .Body ).Decode (t )
193191 if err != nil {
194192 return nil , & AuthorizeError {
@@ -200,18 +198,18 @@ func (c *client) exchange(form url.Values, verifier string, redirect string) (*T
200198 t .TokenType = "Bearer"
201199 }
202200 if t .ExpiresIn != 0 {
203- t .Expiry = timeNow ().Add (time .Duration (t .ExpiresIn ) * time .Second )
201+ t .Expiry = c . utils . Now ().Add (time .Duration (t .ExpiresIn ) * time .Second )
204202 }
205203 return t , nil
206204}
207205
208- func (c * client ) getToken () (* Token , error ) {
209- c .mu .RLock ()
210- defer c .mu .RUnlock ()
206+ func (c * client ) getToken (ctx context. Context ) (* storage. Token , error ) {
207+ c .mu .Lock ()
208+ defer c .mu .Unlock ()
211209 if c .token == nil {
212210 return nil , nil
213211 }
214- if ! c .token .Expiry .Before (timeNow ()) {
212+ if ! c .token .Expiry .Before (c . utils . Now ()) {
215213 return c .token , nil
216214 }
217215 if c .token .RefreshToken == "" {
@@ -220,47 +218,47 @@ func (c *client) getToken() (*Token, error) {
220218 Description : "empty refresh token" ,
221219 }
222220 }
223- t , err := c .refreshToken (c .token .RefreshToken )
221+ t , err := c .refreshToken (ctx , c .token .RefreshToken )
224222 if err != nil {
225223 e , ok := err .(* AuthorizeError )
226- if ok && e .ErrorType == ErrTypeInvalidGrant && c . onTokenRefresh != nil {
227- c .onTokenRefresh (nil )
224+ if ok && e .ErrorType == ErrTypeInvalidGrant {
225+ c .saveToken (nil )
228226 }
229227 return nil , err
230228 }
229+
231230 c .token = t
232- if c .onTokenRefresh != nil {
233- c .onTokenRefresh (& Token {
234- AccessToken : t .AccessToken ,
235- TokenType : t .TokenType ,
236- RefreshToken : t .RefreshToken ,
237- ExpiresIn : t .ExpiresIn ,
238- Expiry : t .Expiry ,
239- })
240- }
231+ c .saveToken (& storage.Token {
232+ AccessToken : t .AccessToken ,
233+ TokenType : t .TokenType ,
234+ RefreshToken : t .RefreshToken ,
235+ ExpiresIn : t .ExpiresIn ,
236+ Expiry : t .Expiry ,
237+ })
238+
241239 return t , nil
242240}
243241
244- func (c * client ) updateToken (t * Token ) {
242+ func (c * client ) updateToken (t * storage. Token ) {
245243 c .mu .Lock ()
246244 defer c .mu .Unlock ()
245+
247246 c .token = t
248- if c .onTokenRefresh != nil {
249- if t == nil {
250- c .onTokenRefresh (nil )
251- } else {
252- c .onTokenRefresh (& Token {
253- AccessToken : t .AccessToken ,
254- TokenType : t .TokenType ,
255- RefreshToken : t .RefreshToken ,
256- ExpiresIn : t .ExpiresIn ,
257- Expiry : t .Expiry ,
258- })
259- }
247+ if t == nil {
248+ c .saveToken (nil )
249+ return
260250 }
251+
252+ c .saveToken (& storage.Token {
253+ AccessToken : t .AccessToken ,
254+ TokenType : t .TokenType ,
255+ RefreshToken : t .RefreshToken ,
256+ ExpiresIn : t .ExpiresIn ,
257+ Expiry : t .Expiry ,
258+ })
261259}
262260
263- func (c * client ) tryToRefreshToken (refreshToken string ) bool {
261+ func (c * client ) tryToRefreshToken (ctx context. Context , refreshToken string ) bool {
264262 c .mu .Lock ()
265263 defer c .mu .Unlock ()
266264 if c .token == nil {
@@ -270,38 +268,37 @@ func (c *client) tryToRefreshToken(refreshToken string) bool {
270268 if c .token .RefreshToken != refreshToken {
271269 return false
272270 }
273- token , err := c .refreshToken (c .token .RefreshToken )
271+
272+ token , err := c .refreshToken (ctx , c .token .RefreshToken )
274273 if err != nil {
275274 e , ok := err .(* AuthorizeError )
276275 // If the refresh token is invalid, clear the token
277- if ok && e .ErrorType == ErrTypeInvalidGrant && c . onTokenRefresh != nil {
276+ if ok && e .ErrorType == ErrTypeInvalidGrant {
278277 c .token = nil
279- if c .onTokenRefresh != nil {
280- c .onTokenRefresh (nil )
281- }
278+ c .saveToken (nil )
282279 }
283280 return false
284281 }
282+
285283 c .token = token
286- if c .onTokenRefresh != nil {
287- c .onTokenRefresh (& Token {
288- AccessToken : token .AccessToken ,
289- TokenType : token .TokenType ,
290- RefreshToken : token .RefreshToken ,
291- ExpiresIn : token .ExpiresIn ,
292- Expiry : token .Expiry ,
293- })
294- }
284+ c .saveToken (& storage.Token {
285+ AccessToken : token .AccessToken ,
286+ TokenType : token .TokenType ,
287+ RefreshToken : token .RefreshToken ,
288+ ExpiresIn : token .ExpiresIn ,
289+ Expiry : token .Expiry ,
290+ })
291+
295292 return true
296293}
297294
298- func (c * client ) refreshToken (token string ) (* Token , error ) {
295+ func (c * client ) refreshToken (ctx context. Context , token string ) (* storage. Token , error ) {
299296 q := url.Values {}
300297 q .Set ("client_id" , c .authClientId )
301298 q .Set ("client_secret" , c .authClientSecret )
302299 q .Set ("refresh_token" , token )
303300 q .Set ("grant_type" , "refresh_token" )
304- req , err := http .NewRequest ( "POST" , c .authURL + "/oauth/token" , strings .NewReader (q .Encode ()))
301+ req , err := http .NewRequestWithContext ( ctx , "POST" , c .authURL + "/oauth/token" , strings .NewReader (q .Encode ()))
305302 if err != nil {
306303 return nil , & AuthorizeError {
307304 ErrorType : ErrTypeRefreshFailed ,
@@ -326,7 +323,7 @@ func (c *client) refreshToken(token string) (*Token, error) {
326323 json .NewDecoder (resp .Body ).Decode (err )
327324 return nil , err
328325 }
329- t := & Token {}
326+ t := & storage. Token {}
330327 err = json .NewDecoder (resp .Body ).Decode (t )
331328 if err != nil {
332329 return nil , & AuthorizeError {
@@ -338,11 +335,19 @@ func (c *client) refreshToken(token string) (*Token, error) {
338335 t .TokenType = "Bearer"
339336 }
340337 if t .ExpiresIn != 0 {
341- t .Expiry = timeNow ().Add (time .Duration (t .ExpiresIn ) * time .Second )
338+ t .Expiry = c . utils . Now ().Add (time .Duration (t .ExpiresIn ) * time .Second )
342339 }
343340 return t , nil
344341}
345342
343+ func (c * client ) saveToken (token * storage.Token ) {
344+ c .storage .GetProfile ().Token = token
345+ err := c .storage .SaveConfig ()
346+ if err != nil {
347+ c .printer .ErrPrintf ("Error: Token was refreshed but failed to save to storage: %v\n " , err )
348+ }
349+ }
350+
346351// https://datatracker.ietf.org/doc/html/rfc7662#section-2.1
347352type IntrospectionResponse struct {
348353 // Required fields
@@ -362,9 +367,9 @@ type IntrospectionResponse struct {
362367 Jti string `json:"jti"` // JWT ID
363368}
364369
365- func (c * client ) introspection (token string ) (* IntrospectionResponse , error ) {
370+ func (c * client ) introspection (ctx context. Context , token string ) (* IntrospectionResponse , error ) {
366371 form := url.Values {"token" : {token }}.Encode ()
367- req , err := http .NewRequest ( "POST" , c .authURL + "/oauth/token/introspect" , strings .NewReader (form ))
372+ req , err := http .NewRequestWithContext ( ctx , "POST" , c .authURL + "/oauth/token/introspect" , strings .NewReader (form ))
368373 if err != nil {
369374 return nil , & AuthorizeError {
370375 ErrorType : ErrTypeIntrospectionFailed ,
@@ -400,12 +405,12 @@ func (c *client) introspection(token string) (*IntrospectionResponse, error) {
400405 return ires , nil
401406}
402407
403- func (c * client ) RevokeToken (token string ) error {
408+ func (c * client ) RevokeToken (ctx context. Context , token string ) error {
404409 if token == "" {
405410 return nil
406411 }
407412 form := url.Values {"token" : {token }}.Encode ()
408- req , err := http .NewRequest ( "POST" , c .authURL + "/oauth/token/revoke" , strings .NewReader (form ))
413+ req , err := http .NewRequestWithContext ( ctx , "POST" , c .authURL + "/oauth/token/revoke" , strings .NewReader (form ))
409414 if err != nil {
410415 return & AuthorizeError {
411416 ErrorType : ErrTypeRevokeFailed ,
0 commit comments