Skip to content

Commit c229630

Browse files
Convert SessionToken to a String typealias (#590)
Co-authored-by: Jordan Haven <117691317+jhaven-stytch@users.noreply.github.com>
1 parent c2ff94b commit c229630

File tree

10 files changed

+59
-127
lines changed

10 files changed

+59
-127
lines changed

Sources/StytchCore/EncryptedUserDefaultsClient/EncryptedUserDefaultsItem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ extension EncryptedUserDefaultsItem {
1818
extension EncryptedUserDefaultsItem {
1919
static let biometricKeyRegistration: Self = .init(name: "stytch_biometric_key_registration")
2020

21-
static let sessionToken: Self = .init(name: SessionToken.Kind.opaque.name)
22-
static let sessionJwt: Self = .init(name: SessionToken.Kind.jwt.name)
21+
static let sessionToken: Self = .init(name: "stytch_session")
22+
static let sessionJwt: Self = .init(name: "stytch_session_jwt")
2323
static let intermediateSessionToken: Self = .init(name: "stytch_intermediate_session_token")
2424

2525
static let codeVerifierPKCE: Self = .init(name: "stytch_code_verifier_pkce")

Sources/StytchCore/KeychainClient/KeychainItem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ extension KeychainItem {
122122

123123
// The following key types are deprecated, but exist for backwards compatibility with migrations
124124
static let biometricKeyRegistration: Self = .init(kind: .deprecated, name: "stytch_biometric_key_registration")
125-
static let sessionToken: Self = .init(kind: .deprecated, name: SessionToken.Kind.opaque.name)
126-
static let sessionJwt: Self = .init(kind: .deprecated, name: SessionToken.Kind.jwt.name)
125+
static let sessionToken: Self = .init(kind: .deprecated, name: "stytch_session")
126+
static let sessionJwt: Self = .init(kind: .deprecated, name: "stytch_session_jwt")
127127
static let intermediateSessionToken: Self = .init(kind: .deprecated, name: "stytch_intermediate_session_token")
128128
static let codeVerifierPKCE: Self = .init(kind: .deprecated, name: "stytch_code_verifier_pkce")
129129
static let codeChallengePKCE: Self = .init(kind: .deprecated, name: "stytch_code_challenge_pkce")

Sources/StytchCore/Networking/NetworkingClient.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ extension NetworkingClient {
1818
let publicToken = configuration.publicToken
1919

2020
let authToken: String
21-
if let sessionToken = Current.sessionManager.sessionToken?.value, sessionToken.isEmpty == false {
21+
if let sessionToken = Current.sessionManager.sessionToken, sessionToken.isEmpty == false {
2222
authToken = "\(publicToken):\(sessionToken)".base64Encoded()
2323
} else {
2424
authToken = "\(publicToken):\(publicToken)".base64Encoded()

Sources/StytchCore/Networking/NetworkingRouter.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ extension NetworkingRouter {
215215

216216
sessionManager.updateSession(
217217
sessionType: .user(sessionResponse.session),
218-
tokens: SessionTokens(jwt: .jwt(sessionResponse.sessionJwt), opaque: .opaque(sessionResponse.sessionToken))
218+
tokens: SessionTokens(jwt: sessionResponse.sessionJwt, opaque: sessionResponse.sessionToken)
219219
)
220220

221221
#if !os(tvOS) && !os(watchOS)
@@ -231,7 +231,7 @@ extension NetworkingRouter {
231231

232232
sessionManager.updateSession(
233233
sessionType: .member(sessionResponse.memberSession),
234-
tokens: SessionTokens(jwt: .jwt(sessionResponse.sessionJwt), opaque: .opaque(sessionResponse.sessionToken))
234+
tokens: SessionTokens(jwt: sessionResponse.sessionJwt, opaque: sessionResponse.sessionToken)
235235
)
236236
} else if let sessionResponse = dataContainer.data as? B2BMFAAuthenticateResponseType {
237237
// Update the member and organization so that all values are current when the session publisher fires
@@ -241,7 +241,7 @@ extension NetworkingRouter {
241241
if let memberSession = sessionResponse.memberSession {
242242
sessionManager.updateSession(
243243
sessionType: .member(memberSession),
244-
tokens: SessionTokens(jwt: .jwt(sessionResponse.sessionJwt), opaque: .opaque(sessionResponse.sessionToken))
244+
tokens: SessionTokens(jwt: sessionResponse.sessionJwt, opaque: sessionResponse.sessionToken)
245245
)
246246
} else {
247247
sessionManager.updateSession(

Sources/StytchCore/SessionManager.swift

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ class SessionManager {
2828
@Dependency(\.keychainClient) private var keychainClient
2929

3030
var hasValidSessionToken: Bool {
31-
sessionToken != nil && sessionToken?.value.isEmpty == false
31+
sessionToken != nil && sessionToken?.isEmpty == false
3232
}
3333

3434
var hasValidSessionJwt: Bool {
35-
sessionJwt != nil && sessionJwt?.value.isEmpty == false
35+
sessionJwt != nil && sessionJwt?.isEmpty == false
3636
}
3737

3838
var hasValidIntermediateSessionToken: Bool {
@@ -95,10 +95,10 @@ class SessionManager {
9595

9696
func clearEmptyTokens() {
9797
// Clear any successfully cached empty string on startup
98-
if let value = sessionToken?.value, value.isEmpty == true {
98+
if let value = sessionToken, value.isEmpty == true {
9999
sessionToken = nil
100100
}
101-
if let value = sessionJwt?.value, value.isEmpty == true {
101+
if let value = sessionJwt, value.isEmpty == true {
102102
sessionJwt = nil
103103
}
104104
if let value = intermediateSessionToken, value.isEmpty == true {
@@ -115,30 +115,18 @@ class SessionManager {
115115
resetSession()
116116
}
117117
}
118-
119-
@objc func cookiesDidUpdate(notification: Notification) {
120-
let storage = (notification.object as? HTTPCookieStorage) ?? .shared
121-
122-
if let jwtCookieValue = storage.cookieValue(cookieName: SessionToken.Kind.jwt.name, date: date()) {
123-
sessionJwt = .jwt(jwtCookieValue)
124-
}
125-
126-
if let opaqueCookieValue = storage.cookieValue(cookieName: SessionToken.Kind.opaque.name, date: date()) {
127-
sessionToken = .opaque(opaqueCookieValue)
128-
}
129-
}
130118
}
131119

132120
// Session Tokens
133121
extension SessionManager {
134122
private(set) var sessionToken: SessionToken? {
135123
get {
136-
try? userDefaultsClient.getStringValue(.sessionToken).map(SessionToken.opaque)
124+
try? userDefaultsClient.getStringValue(.sessionToken)
137125
}
138126
set {
139127
let userDefaultsItem: EncryptedUserDefaultsItem = .sessionToken
140128
if let newValue = newValue {
141-
try? userDefaultsClient.setStringValue(newValue.value, for: userDefaultsItem)
129+
try? userDefaultsClient.setStringValue(newValue, for: userDefaultsItem)
142130
} else {
143131
try? userDefaultsClient.removeItem(item: userDefaultsItem)
144132
}
@@ -147,12 +135,12 @@ extension SessionManager {
147135

148136
private(set) var sessionJwt: SessionToken? {
149137
get {
150-
try? userDefaultsClient.getStringValue(.sessionJwt).map(SessionToken.jwt)
138+
try? userDefaultsClient.getStringValue(.sessionJwt)
151139
}
152140
set {
153141
let userDefaultsItem: EncryptedUserDefaultsItem = .sessionJwt
154142
if let newValue = newValue {
155-
try? userDefaultsClient.setStringValue(newValue.value, for: userDefaultsItem)
143+
try? userDefaultsClient.setStringValue(newValue, for: userDefaultsItem)
156144
} else {
157145
try? userDefaultsClient.removeItem(item: userDefaultsItem)
158146
}

Sources/StytchCore/SharedModels/SessionToken.swift

Lines changed: 3 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,17 @@
11
import Foundation
22

3-
// TODO: include optional expiration here
4-
/// Represents one of two kinds of tokens used to represent a session (see ``SessionToken/Kind-swift.enum``, for more info.) These tokens are used to authenticate the current user/member.
5-
public struct SessionToken: Equatable, Sendable {
6-
/// A type representing the different kinds of session tokens available.
7-
public enum Kind: CaseIterable, Sendable {
8-
/// An token which is an opaque string, simply representing the session.
9-
case opaque
10-
/// A JWT representing the session, which contains signed and serialized information about the session.
11-
case jwt
12-
13-
var name: String {
14-
switch self {
15-
case .opaque:
16-
return "stytch_session"
17-
case .jwt:
18-
return "stytch_session_jwt"
19-
}
20-
}
21-
}
22-
23-
/// The kind of session token.
24-
public let kind: Kind
25-
26-
/// The string value of the session token.
27-
public let value: String
28-
29-
var name: String { kind.name }
30-
31-
private init(kind: Kind, value: String) {
32-
self.kind = kind
33-
self.value = value
34-
}
35-
36-
/// Initializes a new token and marks it as a JWT.
37-
public static func jwt(_ value: String) -> Self {
38-
.init(kind: .jwt, value: value)
39-
}
40-
41-
/// Initializes a new token and marks it as an opaque token.
42-
public static func opaque(_ value: String) -> Self {
43-
.init(kind: .opaque, value: value)
44-
}
45-
}
3+
public typealias SessionToken = String
464

475
/// A public interface to require the caller to explicitly pass one of each type of non nil token in order to update a session.
486
public struct SessionTokens: Sendable {
497
internal let jwt: SessionToken?
508
internal let opaque: SessionToken
519

52-
/// A nullable initializer that requires the caller to pass at least one non-nil instance of each token type.
10+
/// An initializer that requires the caller to pass a non nil opaque SessionToken and an optional jwt.
5311
/// - Parameters:
5412
/// - jwt: An instance of `SessionToken` with a `type` of `.jwt`
5513
/// - opaque: An instance of `SessionToken` with a `type` of `.opaque`
56-
public init?(jwt: SessionToken?, opaque: SessionToken) {
57-
if let jwt = jwt, jwt.kind != .jwt, jwt.value.isEmpty == true {
58-
return nil
59-
}
60-
61-
if opaque.kind != .opaque, opaque.value.isEmpty == true {
62-
return nil
63-
}
64-
14+
public init(jwt: SessionToken? = nil, opaque: SessionToken) {
6515
self.jwt = jwt
6616
self.opaque = opaque
6717
}

Tests/StytchCoreTests/B2BSessionsTestCase.swift

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ final class B2BSessionsTestCase: BaseTestCase {
1818

1919
Current.sessionManager.updateSession(
2020
sessionType: .member(.mock),
21-
tokens: SessionTokens(jwt: .jwt("i'm_jwt"), opaque: .opaque("opaque_all_day"))
21+
tokens: SessionTokens(jwt: "i'm_jwt", opaque: "opaque_all_day")
2222
)
2323

2424
_ = try await StytchB2BClient.sessions.authenticate(parameters: parameters)
@@ -29,8 +29,8 @@ final class B2BSessionsTestCase: BaseTestCase {
2929
method: .post(["session_duration_minutes": 15])
3030
)
3131

32-
XCTAssertEqual(StytchB2BClient.sessions.sessionJwt, .jwt("i'mvalidjson"))
33-
XCTAssertEqual(StytchB2BClient.sessions.sessionToken, .opaque("xyzasdf"))
32+
XCTAssertEqual(StytchB2BClient.sessions.sessionJwt, "i'mvalidjson")
33+
XCTAssertEqual(StytchB2BClient.sessions.sessionToken, "xyzasdf")
3434
XCTAssertNotNil(StytchB2BClient.sessions.memberSession)
3535
}
3636

@@ -63,8 +63,8 @@ final class B2BSessionsTestCase: BaseTestCase {
6363
])
6464
)
6565

66-
XCTAssertEqual(StytchB2BClient.sessions.sessionJwt, .jwt("i'mvalidjson"))
67-
XCTAssertEqual(StytchB2BClient.sessions.sessionToken, .opaque("xyzasdf"))
66+
XCTAssertEqual(StytchB2BClient.sessions.sessionJwt, "i'mvalidjson")
67+
XCTAssertEqual(StytchB2BClient.sessions.sessionToken, "xyzasdf")
6868
XCTAssertNotNil(StytchB2BClient.sessions.memberSession)
6969
}
7070

@@ -74,11 +74,11 @@ final class B2BSessionsTestCase: BaseTestCase {
7474

7575
Current.sessionManager.updateSession(
7676
sessionType: .member(.mock),
77-
tokens: SessionTokens(jwt: .jwt("i'm_jwt"), opaque: .opaque("opaque_all_day"))
77+
tokens: SessionTokens(jwt: "i'm_jwt", opaque: "opaque_all_day")
7878
)
7979

80-
XCTAssertEqual(StytchB2BClient.sessions.sessionToken, .opaque("opaque_all_day"))
81-
XCTAssertEqual(StytchB2BClient.sessions.sessionJwt, .jwt("i'm_jwt"))
80+
XCTAssertEqual(StytchB2BClient.sessions.sessionToken, "opaque_all_day")
81+
XCTAssertEqual(StytchB2BClient.sessions.sessionJwt, "i'm_jwt")
8282

8383
_ = try await StytchB2BClient.sessions.revoke()
8484

@@ -94,13 +94,10 @@ final class B2BSessionsTestCase: BaseTestCase {
9494
XCTAssertNil(StytchB2BClient.sessions.sessionToken)
9595
XCTAssertNil(StytchB2BClient.sessions.sessionJwt)
9696

97-
if let tokens = SessionTokens(jwt: .jwt("jwt"), opaque: .opaque("token")) {
98-
StytchB2BClient.sessions.update(sessionTokens: tokens)
99-
XCTAssertEqual(StytchB2BClient.sessions.sessionToken, .opaque("token"))
100-
XCTAssertEqual(StytchB2BClient.sessions.sessionJwt, .jwt("jwt"))
101-
} else {
102-
XCTFail("SessionTokens should not be nil")
103-
}
97+
let tokens = SessionTokens(jwt: "jwt", opaque: "token")
98+
StytchB2BClient.sessions.update(sessionTokens: tokens)
99+
XCTAssertEqual(StytchB2BClient.sessions.sessionToken, "token")
100+
XCTAssertEqual(StytchB2BClient.sessions.sessionJwt, "jwt")
104101
}
105102

106103
func testSessionExchange() async throws {
@@ -144,7 +141,7 @@ final class B2BSessionsTestCase: BaseTestCase {
144141
Current.timer = { _, _, _ in Self.mockTimer }
145142
Current.sessionManager.updateSession(
146143
sessionType: .member(.mock),
147-
tokens: SessionTokens(jwt: .jwt("i'm_jwt"), opaque: .opaque("opaque_all_day"))
144+
tokens: SessionTokens(jwt: "i'm_jwt", opaque: "opaque_all_day")
148145
)
149146

150147
wait(for: [expectation], timeout: 1.0)
@@ -167,7 +164,7 @@ final class B2BSessionsTestCase: BaseTestCase {
167164
Current.timer = { _, _, _ in Self.mockTimer }
168165
Current.sessionManager.updateSession(
169166
sessionType: nil,
170-
tokens: SessionTokens(jwt: .jwt("i'm_jwt"), opaque: .opaque("opaque_all_day"))
167+
tokens: SessionTokens(jwt: "i'm_jwt", opaque: "opaque_all_day")
171168
)
172169

173170
wait(for: [expectation], timeout: 1.0)
@@ -178,7 +175,7 @@ final class B2BSessionsTestCase: BaseTestCase {
178175
Current.timer = { _, _, _ in Self.mockTimer }
179176
Current.sessionManager.updateSession(
180177
sessionType: .member(.mockWithExpiredMemberSession),
181-
tokens: SessionTokens(jwt: .jwt("i'm_jwt"), opaque: .opaque("opaque_all_day"))
178+
tokens: SessionTokens(jwt: "i'm_jwt", opaque: "opaque_all_day")
182179
)
183180

184181
XCTAssertNil(StytchB2BClient.sessions.memberSession)

Tests/StytchCoreTests/CryptoWalletsTestCase.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ final class CryptoWalletsTestCase: BaseTestCase {
5555

5656
_ = try await StytchClient.cryptoWallets.authenticate(parameters: .init(cryptoWalletType: .solana, cryptoWalletAddress: "mock-crypto-address", signature: "mock-signature"))
5757

58-
XCTAssertEqual(StytchClient.sessions.sessionToken, .opaque("hello_session"))
59-
XCTAssertEqual(StytchClient.sessions.sessionJwt, .jwt("jwt_for_me"))
58+
XCTAssertEqual(StytchClient.sessions.sessionToken, "hello_session")
59+
XCTAssertEqual(StytchClient.sessions.sessionJwt, "jwt_for_me")
6060

6161
try XCTAssertRequest(
6262
networkInterceptor.requests[0],

Tests/StytchCoreTests/OTPTestCase.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ final class OTPTestCase: BaseTestCase {
158158

159159
_ = try await StytchClient.otps.authenticate(parameters: parameters)
160160

161-
XCTAssertEqual(StytchClient.sessions.sessionToken, .opaque("hello_session"))
162-
XCTAssertEqual(StytchClient.sessions.sessionJwt, .jwt("jwt_for_me"))
161+
XCTAssertEqual(StytchClient.sessions.sessionToken, "hello_session")
162+
XCTAssertEqual(StytchClient.sessions.sessionJwt, "jwt_for_me")
163163

164164
try XCTAssertRequest(
165165
networkInterceptor.requests[0],

0 commit comments

Comments
 (0)