Skip to content

Commit dcada1a

Browse files
authored
feat(auth): add isExpired variable to session type (#399)
1 parent a0ecb70 commit dcada1a

File tree

8 files changed

+55
-43
lines changed

8 files changed

+55
-43
lines changed

Sources/Auth/AuthClient.swift

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -599,17 +599,17 @@ public final class AuthClient: Sendable {
599599
/// Gets the session data from a OAuth2 callback URL.
600600
@discardableResult
601601
public func session(from url: URL) async throws -> Session {
602-
if configuration.flowType == .implicit, !isImplicitGrantFlow(url: url) {
602+
let params = extractParams(from: url)
603+
604+
if configuration.flowType == .implicit, !isImplicitGrantFlow(params: params) {
603605
throw AuthError.invalidImplicitGrantFlowURL
604606
}
605607

606-
if configuration.flowType == .pkce, !isPKCEFlow(url: url) {
608+
if configuration.flowType == .pkce, !isPKCEFlow(params: params) {
607609
throw AuthError.pkce(.invalidPKCEFlowURL)
608610
}
609611

610-
let params = extractParams(from: url)
611-
612-
if isPKCEFlow(url: url) {
612+
if isPKCEFlow(params: params) {
613613
guard let code = params["code"] else {
614614
throw AuthError.pkce(.codeVerifierNotFound)
615615
}
@@ -1120,15 +1120,13 @@ public final class AuthClient: Sendable {
11201120
return (codeChallenge, codeChallengeMethod)
11211121
}
11221122

1123-
private func isImplicitGrantFlow(url: URL) -> Bool {
1124-
let fragments = extractParams(from: url)
1125-
return fragments["access_token"] != nil || fragments["error_description"] != nil
1123+
private func isImplicitGrantFlow(params: [String: String]) -> Bool {
1124+
params["access_token"] != nil || params["error_description"] != nil
11261125
}
11271126

1128-
private func isPKCEFlow(url: URL) -> Bool {
1129-
let fragments = extractParams(from: url)
1127+
private func isPKCEFlow(params: [String: String]) -> Bool {
11301128
let currentCodeVerifier = codeVerifierStorage.get()
1131-
return fragments["code"] != nil && currentCodeVerifier != nil
1129+
return params["code"] != nil && currentCodeVerifier != nil
11321130
}
11331131

11341132
private func getURLForProvider(

Sources/Auth/AuthError.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ public enum AuthError: LocalizedError, Sendable, Equatable {
55
case malformedJWT
66
case sessionNotFound
77
case api(APIError)
8+
9+
/// Error thrown during PKCE flow.
810
case pkce(PKCEFailureReason)
11+
912
case invalidImplicitGrantFlowURL
1013
case missingURL
1114
case invalidRedirectScheme
1215

16+
/// An error returned by the API.
1317
public struct APIError: Error, Decodable, Sendable, Equatable {
1418
/// A basic message describing the problem with the request. Usually missing if
1519
/// ``AuthError/APIError/error`` is present.
@@ -39,21 +43,32 @@ public enum AuthError: LocalizedError, Sendable, Equatable {
3943
}
4044

4145
public enum PKCEFailureReason: Sendable {
46+
/// Code verifier not found in the URL.
4247
case codeVerifierNotFound
48+
49+
/// Not a valid PKCE flow URL.
4350
case invalidPKCEFlowURL
4451
}
4552

4653
public var errorDescription: String? {
4754
switch self {
4855
case let .api(error): error.errorDescription ?? error.msg ?? error.error
49-
case .missingExpClaim: "Missing expiration claim on access token."
56+
case .missingExpClaim: "Missing expiration claim in the access token."
5057
case .malformedJWT: "A malformed JWT received."
5158
case .sessionNotFound: "Unable to get a valid session."
52-
case .pkce(.codeVerifierNotFound): "A code verifier wasn't found in PKCE flow."
53-
case .pkce(.invalidPKCEFlowURL): "Not a valid PKCE flow url."
59+
case let .pkce(reason): reason.errorDescription
5460
case .invalidImplicitGrantFlowURL: "Not a valid implicit grant flow url."
5561
case .missingURL: "Missing URL."
5662
case .invalidRedirectScheme: "Invalid redirect scheme."
5763
}
5864
}
5965
}
66+
67+
extension AuthError.PKCEFailureReason {
68+
var errorDescription: String {
69+
switch self {
70+
case .codeVerifierNotFound: "A code verifier wasn't found in PKCE flow."
71+
case .invalidPKCEFlowURL: "Not a valid PKCE flow url."
72+
}
73+
}
74+
}

Sources/Auth/Internal/Contants.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// Contants.swift
3+
//
4+
//
5+
// Created by Guilherme Souza on 22/05/24.
6+
//
7+
8+
import Foundation
9+
10+
let EXPIRY_MARGIN: TimeInterval = 30

Sources/Auth/Internal/SessionManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private actor LiveSessionManager {
3636
throw AuthError.sessionNotFound
3737
}
3838

39-
if currentSession.isValid {
39+
if !currentSession.isExpired {
4040
scheduleNextTokenRefresh(currentSession)
4141

4242
return currentSession

Sources/Auth/Internal/SessionStorage.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ struct StoredSession: Codable {
1919
}
2020
}
2121

22-
extension Session {
23-
var isValid: Bool {
24-
expiresAt - Date().timeIntervalSince1970 > 60
25-
}
26-
}
27-
2822
extension AuthLocalStorage {
2923
func getSession() throws -> Session? {
3024
try retrieve(key: "supabase.session").flatMap {

Sources/Auth/Types.swift

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ public enum AuthChangeEvent: String, Sendable {
1515
case mfaChallengeVerified = "MFA_CHALLENGE_VERIFIED"
1616
}
1717

18+
@available(
19+
*,
20+
deprecated,
21+
message: "Access to UserCredentials will be removed on the next major release."
22+
)
1823
public struct UserCredentials: Codable, Hashable, Sendable {
1924
public var email: String?
2025
public var password: String?
@@ -104,21 +109,13 @@ public struct Session: Codable, Hashable, Sendable {
104109
self.user = user
105110
}
106111

107-
static let empty = Session(
108-
accessToken: "",
109-
tokenType: "",
110-
expiresIn: 0,
111-
expiresAt: 0,
112-
refreshToken: "",
113-
user: User(
114-
id: UUID(),
115-
appMetadata: [:],
116-
userMetadata: [:],
117-
aud: "",
118-
createdAt: Date(),
119-
updatedAt: Date()
120-
)
121-
)
112+
/// Returns `true` if the token is expired or will expire in the next 30 seconds.
113+
///
114+
/// The 30 second buffer is to account for latency issues.
115+
public var isExpired: Bool {
116+
let expiresAt = Date(timeIntervalSince1970: expiresAt)
117+
return expiresAt.timeIntervalSinceNow < EXPIRY_MARGIN
118+
}
122119
}
123120

124121
public struct User: Codable, Hashable, Identifiable, Sendable {
@@ -513,7 +510,7 @@ public struct Factor: Identifiable, Codable, Hashable, Sendable {
513510
public let friendlyName: String?
514511

515512
/// Type of factor. Only `totp` supported with this version but may change in future versions.
516-
public let factorType: String
513+
public let factorType: FactorType
517514

518515
/// Factor's status.
519516
public let status: FactorStatus
@@ -718,8 +715,7 @@ public struct ResendMobileResponse: Decodable, Hashable, Sendable {
718715
}
719716

720717
public struct WeakPassword: Codable, Hashable, Sendable {
721-
/// List of reasons the password is too weak, could be any of `length`, `characters`, or
722-
/// `pwned`.
718+
/// List of reasons the password is too weak, could be any of `length`, `characters`, or `pwned`.
723719
public let reasons: [String]
724720
}
725721

Sources/_Helpers/AnyJSON/AnyJSON.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ public enum AnyJSON: Sendable, Codable, Hashable {
2222

2323
/// Returns the underlying Swift value corresponding to the `AnyJSON` instance.
2424
///
25-
/// - Note: For `.object` and `.array` cases, the returned value contains recursively transformed
26-
/// `AnyJSON` instances.
25+
/// - Note: For `.object` and `.array` cases, the returned value contains recursively transformed `AnyJSON` instances.
2726
public var value: Any {
2827
switch self {
2928
case .null: NSNull()

Tests/AuthTests/Mocks/Mocks.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ extension Session {
2828
static let expiredSession = Session(
2929
accessToken: "accesstoken",
3030
tokenType: "bearer",
31-
expiresIn: 60,
32-
expiresAt: Date().addingTimeInterval(60).timeIntervalSince1970,
31+
expiresIn: 30,
32+
expiresAt: Date().addingTimeInterval(30).timeIntervalSince1970,
3333
refreshToken: "refreshtoken",
3434
user: User(fromMockNamed: "user")
3535
)

0 commit comments

Comments
 (0)