Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Generated using Sourcery 2.0.2 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
import Combine
import Foundation

public extension StytchB2BClient.Sessions {
/// Exchange an auth token issued by a trusted identity provider for a Stytch session.
/// You must first register a Trusted Auth Token profile in the Stytch dashboard (https://stytch.com/dashboard/trusted-auth-tokens).
/// If a session token or session JWT is provided, it will add the trusted auth token as an authentication factor to the existing session.
func attest(parameters: AttestParameters, completion: @escaping Completion<B2BAuthenticateResponse>) {
Task {
do {
completion(.success(try await attest(parameters: parameters)))
} catch {
completion(.failure(error))
}
}
}

/// Exchange an auth token issued by a trusted identity provider for a Stytch session.
/// You must first register a Trusted Auth Token profile in the Stytch dashboard (https://stytch.com/dashboard/trusted-auth-tokens).
/// If a session token or session JWT is provided, it will add the trusted auth token as an authentication factor to the existing session.
func attest(parameters: AttestParameters) -> AnyPublisher<B2BAuthenticateResponse, Error> {
return Deferred {
Future({ promise in
Task {
do {
promise(.success(try await attest(parameters: parameters)))
} catch {
promise(.failure(error))
}
}
})
}
.eraseToAnyPublisher()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Generated using Sourcery 2.0.2 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
import Combine
import Foundation

public extension StytchClient.Sessions {
/// Exchange an auth token issued by a trusted identity provider for a Stytch session.
/// You must first register a Trusted Auth Token profile in the Stytch dashboard (https://stytch.com/dashboard/trusted-auth-tokens).
/// If a session token or session JWT is provided, it will add the trusted auth token as an authentication factor to the existing session.
func attest(parameters: AttestParameters, completion: @escaping Completion<AuthenticateResponse>) {
Task {
do {
completion(.success(try await attest(parameters: parameters)))
} catch {
completion(.failure(error))
}
}
}

/// Exchange an auth token issued by a trusted identity provider for a Stytch session.
/// You must first register a Trusted Auth Token profile in the Stytch dashboard (https://stytch.com/dashboard/trusted-auth-tokens).
/// If a session token or session JWT is provided, it will add the trusted auth token as an authentication factor to the existing session.
func attest(parameters: AttestParameters) -> AnyPublisher<AuthenticateResponse, Error> {
return Deferred {
Future({ promise in
Task {
do {
promise(.success(try await attest(parameters: parameters)))
} catch {
promise(.failure(error))
}
}
})
}
.eraseToAnyPublisher()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ extension StytchB2BClient {
case authenticate
case revoke
case exchange
case attest

var path: Path {
.init(rawValue: rawValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ public extension StytchB2BClient {
}
}

// sourcery: AsyncVariants, (NOTE: - must use /// doc comment styling)
/// Exchange an auth token issued by a trusted identity provider for a Stytch session.
/// You must first register a Trusted Auth Token profile in the Stytch dashboard (https://stytch.com/dashboard/trusted-auth-tokens).
/// If a session token or session JWT is provided, it will add the trusted auth token as an authentication factor to the existing session.
public func attest(parameters: AttestParameters) async throws -> B2BAuthenticateResponse {
try await router.performSessionRequest(to: .attest, parameters: parameters)
}

/// If your app has cookies disabled or simply receives updated session tokens from your backend via means other than
/// `Set-Cookie` headers, you must call this method after receiving the updated tokens to ensure the `StytchClient`
/// and persistent storage are kept up-to-date. You are required to include both the opaque token and the jwt.
Expand Down Expand Up @@ -116,3 +124,34 @@ public extension StytchB2BClient.Sessions {
}
}
}

public extension StytchB2BClient.Sessions {
/// The dedicated parameters type for sessions `attest` calls.
struct AttestParameters: Codable, Sendable {
let profileId: String
let token: String
let organizationId: String?
let sessionJwt: String?
let sessionToken: String?

/// - Parameters:
/// - profileId: The member profile identifier to attest.
/// - token: The attestation token issued by the platform.
/// - organizationId: Optional organization ID associated with the session.
/// - sessionJwt: Optional current session JWT.
/// - sessionToken: Optional current session token.
public init(
profileId: String,
token: String,
organizationId: String? = nil,
sessionJwt: String? = nil,
sessionToken: String? = nil
) {
self.profileId = profileId
self.token = token
self.organizationId = organizationId
self.sessionJwt = sessionJwt
self.sessionToken = sessionToken
}
}
}
40 changes: 40 additions & 0 deletions Sources/StytchCore/StytchClient/StytchClient+Sessions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public extension StytchClient {
enum SessionsRoute: String, RouteType {
case authenticate
case revoke
case attest

var path: Path {
.init(rawValue: rawValue)
Expand Down Expand Up @@ -59,6 +60,14 @@ public extension StytchClient {
}
}

// sourcery: AsyncVariants, (NOTE: - must use /// doc comment styling)
/// Exchange an auth token issued by a trusted identity provider for a Stytch session.
/// You must first register a Trusted Auth Token profile in the Stytch dashboard (https://stytch.com/dashboard/trusted-auth-tokens).
/// If a session token or session JWT is provided, it will add the trusted auth token as an authentication factor to the existing session.
public func attest(parameters: AttestParameters) async throws -> AuthenticateResponse {
try await router.performSessionRequest(to: .attest, parameters: parameters)
}

/// If your app has cookies disabled or simply receives updated session tokens from your backend via means other than
/// `Set-Cookie` headers, you must call this method after receiving the updated tokens to ensure the `StytchClient`
/// and persistent storage are kept up-to-date. You are required to include both the opaque token and the jwt.
Expand Down Expand Up @@ -104,3 +113,34 @@ public extension StytchClient.Sessions {
}
}
}

public extension StytchClient.Sessions {
/// The dedicated parameters type for sessions `attest` calls.
struct AttestParameters: Codable, Sendable {
let profileId: String
let token: String
let sessionDurationMinutes: Minutes?
let sessionJwt: String?
let sessionToken: String?

/// - Parameters:
/// - profileId: The profile identifier to attest.
/// - token: The attestation token issued by the platform.
/// - sessionDurationMinutes: Optional override for the requested session duration in minutes.
/// - sessionJwt: Optional current session JWT.
/// - sessionToken: Optional current session token.
public init(
profileId: String,
token: String,
sessionDurationMinutes: Minutes? = nil,
sessionJwt: String? = nil,
sessionToken: String? = nil
) {
self.profileId = profileId
self.token = token
self.sessionDurationMinutes = sessionDurationMinutes
self.sessionJwt = sessionJwt
self.sessionToken = sessionToken
}
}
}
34 changes: 34 additions & 0 deletions Tests/StytchCoreTests/B2BSessionsTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,40 @@ final class B2BSessionsTestCase: BaseTestCase {
XCTAssertNotNil(StytchB2BClient.sessions.memberSession)
}

func testSessionsAttest() async throws {
networkInterceptor.responses { B2BAuthenticateResponse.mock }

let parameters = StytchB2BClient.Sessions.AttestParameters(
profileId: "profile_123",
token: "attestation_token",
organizationId: "org_123",
sessionJwt: "existing_jwt",
sessionToken: "existing_token"
)

Current.timer = { _, _, _ in .init() }

XCTAssertNil(StytchB2BClient.sessions.memberSession)

_ = try await StytchB2BClient.sessions.attest(parameters: parameters)

try XCTAssertRequest(
networkInterceptor.requests[0],
urlString: "https://api.stytch.com/sdk/v1/b2b/sessions/attest",
method: .post([
"profile_id": "profile_123",
"token": "attestation_token",
"organization_id": "org_123",
"session_jwt": "existing_jwt",
"session_token": "existing_token",
])
)

XCTAssertEqual(StytchB2BClient.sessions.sessionJwt, .jwt("i'mvalidjson"))
XCTAssertEqual(StytchB2BClient.sessions.sessionToken, .opaque("xyzasdf"))
XCTAssertNotNil(StytchB2BClient.sessions.memberSession)
}

func testSessionsRevoke() async throws {
networkInterceptor.responses { BasicResponse(requestId: "request_id", statusCode: 200) }
Current.timer = { _, _, _ in .init() }
Expand Down
33 changes: 33 additions & 0 deletions Tests/StytchCoreTests/SessionManagerTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,39 @@ final class SessionManagerTestCase: BaseTestCase {
XCTAssertNotNil(StytchClient.sessions.session)
}

func testSessionsAttest() async throws {
networkInterceptor.responses { AuthenticateResponse.mock }
let parameters = StytchClient.Sessions.AttestParameters(
profileId: "profile_123",
token: "attestation_token",
sessionDurationMinutes: 30,
sessionJwt: "existing_jwt",
sessionToken: "existing_token"
)

Current.timer = { _, _, _ in .init() }

XCTAssertNil(StytchClient.sessions.session)

_ = try await StytchClient.sessions.attest(parameters: parameters)

try XCTAssertRequest(
networkInterceptor.requests[0],
urlString: "https://api.stytch.com/sdk/v1/sessions/attest",
method: .post([
"profile_id": "profile_123",
"token": "attestation_token",
"session_duration_minutes": 30,
"session_jwt": "existing_jwt",
"session_token": "existing_token",
])
)

XCTAssertEqual(StytchClient.sessions.sessionJwt, .jwt("jwt_for_me"))
XCTAssertEqual(StytchClient.sessions.sessionToken, .opaque("hello_session"))
XCTAssertNotNil(StytchClient.sessions.session)
}

func testSessionsRevoke() async throws {
networkInterceptor.responses { BasicResponse(requestId: "request_id", statusCode: 200) }
Current.timer = { _, _, _ in .init() }
Expand Down