Skip to content

Commit 00b9234

Browse files
committed
don't throw error if an access token can't be parsed as JWT
it's not technically required that an access token is a JWT
1 parent 8f1dd9a commit 00b9234

File tree

1 file changed

+46
-8
lines changed

1 file changed

+46
-8
lines changed

lib/minisky/requests.rb

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -304,12 +304,13 @@ def fetch_all(method, params = nil, auth: default_auth_mode,
304304
# - `:logged_in` if a login using a password was performed
305305
# - `:refreshed` if the access token was expired and was refreshed
306306
# - `:ok` if no refresh was needed
307+
# - `:unknown` if the token is not a valid JWT (e.g. an opaque blob)
307308
#
308309
# @raise [BadResponse] if login or refresh returns an error status code
309310
# @raise [AuthError]
311+
# - if the client doesn't include user config at all
310312
# - if logging in is required, but login or password isn't provided
311313
# - if token refresh is needed, but refresh token is missing
312-
# - if a token has invalid format
313314

314315
def check_access
315316
if !user
@@ -318,8 +319,16 @@ def check_access
318319
raise AuthError, "User id or password is missing"
319320
elsif !user.logged_in?
320321
log_in
321-
:logged_in
322-
elsif access_token_expired?
322+
return :logged_in
323+
end
324+
325+
begin
326+
expired = access_token_expired?
327+
rescue AuthError
328+
return :unknown
329+
end
330+
331+
if expired
323332
perform_token_refresh
324333
:refreshed
325334
else
@@ -390,29 +399,58 @@ def perform_token_refresh
390399
json
391400
end
392401

402+
# Attempts to parse a given token as JWT and extract the expiration date from the payload.
403+
# An access token technically isn't required to be a (valid) JWT, so if the parsing fails
404+
# for whatever reason, nil is returned.
405+
#
406+
# @return [Time, nil] parsed expiration time, or nil if token is not a valid JWT
407+
393408
def token_expiration_date(token)
409+
return nil unless token.valid_encoding?
410+
394411
parts = token.split('.')
395-
raise AuthError, "Invalid access token format" unless parts.length == 3
412+
return nil unless parts.length == 3
396413

397414
begin
398415
payload = JSON.parse(Base64.decode64(parts[1]))
399416
rescue JSON::ParserError
400-
raise AuthError, "Couldn't decode payload from access token"
417+
return nil
401418
end
402419

403420
exp = payload['exp']
404-
raise AuthError, "Invalid token expiry data" unless exp.is_a?(Numeric) && exp > 0
421+
return nil unless exp.is_a?(Numeric) && exp > 0
405422

406-
Time.at(exp)
423+
time = Time.at(exp)
424+
return nil if time.year < 2026 || time.year > 2100
425+
426+
time
407427
end
408428

429+
# Attempts to parse the user's access token as JWT, extract the expiration date from the
430+
# payload, and check if the token hasn't expired yet.
431+
#
432+
# @return [Boolean] true if the token's expiration time is more than a minute away
433+
# @raise [AuthError] if the token is not a valid JWT, or user is not logged in
434+
409435
def access_token_expired?
410-
token_expiration_date(user.access_token) < Time.now + 60
436+
if user&.access_token.nil?
437+
raise AuthError, "No access token (user is not logged in)"
438+
end
439+
440+
exp_date = token_expiration_date(user.access_token)
441+
442+
if exp_date
443+
exp_date < Time.now + 60
444+
else
445+
raise AuthError, "Token expiration date cannot be decoded"
446+
end
411447
end
412448

413449
#
414450
# Clear stored access and refresh tokens, effectively logging out the user.
415451
#
452+
# @raise [AuthError] if the client doesn't have a user config
453+
#
416454

417455
def reset_tokens
418456
if !user

0 commit comments

Comments
 (0)