Skip to content

Commit

Permalink
fix(realtime): prevent sending expired tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
grdsdev committed Dec 3, 2024
1 parent 7e52aec commit ec2dbf6
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 32 deletions.
2 changes: 1 addition & 1 deletion Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ public final class AuthClient: Sendable {
var hasExpired = true
var session: Session

let jwt = try decode(jwt: accessToken)
let jwt = JWT.decodePayload(accessToken)
if let exp = jwt?["exp"] as? TimeInterval {
expiresAt = Date(timeIntervalSince1970: exp)
hasExpired = expiresAt <= now
Expand Down
2 changes: 1 addition & 1 deletion Sources/Auth/AuthMFA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public struct AuthMFA: Sendable {
public func getAuthenticatorAssuranceLevel() async throws -> AuthMFAGetAuthenticatorAssuranceLevelResponse {
do {
let session = try await sessionManager.session()
let payload = try decode(jwt: session.accessToken)
let payload = JWT.decodePayload(session.accessToken)

var currentLevel: AuthenticatorAssuranceLevels?

Expand Down
30 changes: 0 additions & 30 deletions Sources/Auth/Internal/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,33 +39,3 @@ private func extractParams(from fragment: String) -> [URLQueryItem] {
: nil
}
}

func decode(jwt: String) throws -> [String: Any]? {
let parts = jwt.split(separator: ".")
guard parts.count == 3 else {
return nil
}

let payload = String(parts[1])
guard let data = base64URLDecode(payload) else {
return nil
}
let json = try JSONSerialization.jsonObject(with: data, options: [])
guard let decodedPayload = json as? [String: Any] else {
return nil
}
return decodedPayload
}

private func base64URLDecode(_ value: String) -> Data? {
var base64 = value.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let length = Double(base64.lengthOfBytes(using: .utf8))
let requiredLength = 4 * ceil(length / 4.0)
let paddingLength = requiredLength - length
if paddingLength > 0 {
let padding = "".padding(toLength: Int(paddingLength), withPad: "=", startingAt: 0)
base64 = base64 + padding
}
return Data(base64Encoded: base64, options: .ignoreUnknownCharacters)
}
40 changes: 40 additions & 0 deletions Sources/Helpers/JWT.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// JWT.swift
// Supabase
//
// Created by Guilherme Souza on 28/11/24.
//

import Foundation

package enum JWT {
package static func decodePayload(_ jwt: String) -> [String: Any]? {
let parts = jwt.split(separator: ".")
guard parts.count == 3 else {
return nil
}

let payload = String(parts[1])
guard let data = base64URLDecode(payload) else {
return nil
}
let json = try? JSONSerialization.jsonObject(with: data, options: [])
guard let decodedPayload = json as? [String: Any] else {
return nil
}
return decodedPayload
}

private static func base64URLDecode(_ value: String) -> Data? {
var base64 = value.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let length = Double(base64.lengthOfBytes(using: .utf8))
let requiredLength = 4 * ceil(length / 4.0)
let paddingLength = requiredLength - length
if paddingLength > 0 {
let padding = "".padding(toLength: Int(paddingLength), withPad: "=", startingAt: 0)
base64 = base64 + padding
}
return Data(base64Encoded: base64, options: .ignoreUnknownCharacters)
}
}
8 changes: 8 additions & 0 deletions Sources/Realtime/V2/RealtimeClientV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,14 @@ public final class RealtimeClientV2: Sendable {
/// Sets the JWT access token used for channel subscription authorization and Realtime RLS.
/// - Parameter token: A JWT string.
public func setAuth(_ token: String?) async {
if let token, let payload = JWT.decodePayload(token),
let exp = payload["exp"] as? TimeInterval, exp < Date().timeIntervalSince1970
{
options.logger?.warning(
"InvalidJWTToken: Invalid value for JWT claim \"exp\" with value \(exp)")
return
}

mutableState.withValue {
$0.accessToken = token
}
Expand Down

0 comments on commit ec2dbf6

Please sign in to comment.