Skip to content

Commit

Permalink
Fix expired token being treated as invalid token
Browse files Browse the repository at this point in the history
  • Loading branch information
nuno-vieira committed Jul 18, 2024
1 parent f0b21d1 commit f635d07
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 11 deletions.
4 changes: 2 additions & 2 deletions Sources/StreamChat/APIClient/RequestDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ struct DefaultRequestDecoder: RequestDecoder {
throw ClientError.Unknown("Unknown error. Server response: \(httpResponse).")
}

if serverError.isInvalidTokenError {
log.info("Request failed because of an experied token.", subsystems: .httpRequests)
if serverError.isExpiredTokenError {
log.info("Request failed because of an expired token.", subsystems: .httpRequests)
throw ClientError.ExpiredToken()
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/StreamChat/Errors/ErrorPayload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ extension ErrorPayload {

extension ClosedRange where Bound == Int {
/// The error codes for token-related errors. Typically, a refreshed token is required to recover.
static let tokenInvalidErrorCodes: Self = StreamErrorCode.expiredToken...StreamErrorCode.invalidTokenSignature
static let tokenInvalidErrorCodes: Self = StreamErrorCode.notYetValidToken...StreamErrorCode.invalidTokenSignature

/// The range of HTTP request status codes for client errors.
static let clientErrorCodes: Self = 400...499
Expand Down
6 changes: 2 additions & 4 deletions Sources/StreamChat/Repositories/ConnectionRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,8 @@ class ConnectionRepository {
case let .connected(connectionId: id):
shouldNotifyConnectionIdWaiters = true
connectionId = id
case let .disconnected(source) where source.serverError?.isInvalidTokenError == true:
if source.serverError?.isExpiredTokenError == true {
onExpiredToken()
}
case let .disconnected(source) where source.serverError?.isExpiredTokenError == true:
onExpiredToken()
shouldNotifyConnectionIdWaiters = false
connectionId = nil
case .disconnected:
Expand Down
5 changes: 3 additions & 2 deletions Sources/StreamChat/WebSocketClient/ConnectionStatus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ enum WebSocketConnectionState: Equatable {
return false
}

if serverInitiatedError.isClientError {
// Don't reconnect on client side errors
if serverInitiatedError.isClientError && !serverInitiatedError.isExpiredTokenError {
// Don't reconnect on client side errors unless it is an expired token
// Expired tokens return 401, so it is considered client error.
return false
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,13 @@ final class ConnectionRepository_Tests: XCTestCase {

func test_handleConnectionUpdate_shouldNotifyWaitersWhenNeeded() {
let invalidTokenError = ClientError(with: ErrorPayload(
code: .random(in: ClosedRange.tokenInvalidErrorCodes),
code: StreamErrorCode.accessKeyInvalid,
message: .unique,
statusCode: .unique
))

let expiredTokenError = ClientError(with: ErrorPayload(
code: StreamErrorCode.expiredToken,
message: .unique,
statusCode: .unique
))
Expand All @@ -301,7 +307,8 @@ final class ConnectionRepository_Tests: XCTestCase {
(.disconnecting(source: .noPongReceived), false),
(.disconnected(source: .userInitiated), true),
(.disconnected(source: .systemInitiated), true),
(.disconnected(source: .serverInitiated(error: invalidTokenError)), false)
(.disconnected(source: .serverInitiated(error: invalidTokenError)), true),
(.disconnected(source: .serverInitiated(error: expiredTokenError)), false)
]

for (webSocketState, shouldNotify) in pairs {
Expand Down
17 changes: 17 additions & 0 deletions Tests/StreamChatTests/WebSocketClient/ConnectionStatus_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,23 @@ final class WebSocketConnectionState_Tests: XCTestCase {
XCTAssertFalse(state.isAutomaticReconnectionEnabled)
}

func test_isAutomaticReconnectionEnabled_whenDisconnectedByServerWithExpiredToken_returnsTrue() {
// Create expired token error
let expiredTokenError = ErrorPayload(
code: StreamErrorCode.expiredToken,
message: .unique,
statusCode: .unique
)

// Create disconnected state intiated by the server with invalid token error
let state: WebSocketConnectionState = .disconnected(
source: .serverInitiated(error: ClientError(with: expiredTokenError))
)

// Assert `isAutomaticReconnectionEnabled` returns true
XCTAssertTrue(state.isAutomaticReconnectionEnabled)
}

func test_isAutomaticReconnectionEnabled_whenDisconnectedByServerWithClientError_returnsFalse() {
// Create client error
let clientError = ErrorPayload(
Expand Down

0 comments on commit f635d07

Please sign in to comment.