Skip to content

Commit

Permalink
Add support for authenticated flows (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
itaihanski authored Feb 3, 2024
1 parent ae31331 commit e425857
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 3 deletions.
7 changes: 7 additions & 0 deletions src/internal/http/DescopeClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ class DescopeClient: HTTPClient {
])
}

func flowPrime(codeChallenge: String, flowId: String, refreshJwt: String) async throws {
try await post("flow/prime", headers: authorization(with: refreshJwt), body: [
"codeChallenge": codeChallenge,
"flowId": flowId,
])
}

// MARK: - Others

func me(refreshJwt: String) async throws -> UserResponse {
Expand Down
10 changes: 7 additions & 3 deletions src/internal/routes/Flow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ class Flow: Route, DescopeFlow {
func start(runner: DescopeFlowRunner) async throws -> AuthenticationResponse {
// adds some required query parameters to the flow URL to facilitate PKCE and
// redirection at the end of the flow
let (initialURL, codeVerifier) = try prepareInitialRequest(for: runner)
let (initialURL, codeVerifier, codeChallenge) = try prepareInitialRequest(for: runner)
// prime the flow if the runner has flow authentication info
if let flowAuthentication = runner.flowAuthentication {
try await client.flowPrime(codeChallenge: codeChallenge, flowId: flowAuthentication.flowId, refreshJwt: flowAuthentication.refreshJwt)
}
log(.info, "Starting flow authentication", initialURL)

// sets the flow we're about to present as the current flow
Expand Down Expand Up @@ -188,7 +192,7 @@ private extension Data {
}
}

private func prepareInitialRequest(for runner: DescopeFlowRunner) throws -> (url: URL, codeVerifier: String) {
private func prepareInitialRequest(for runner: DescopeFlowRunner) throws -> (url: URL, codeVerifier: String, codeChallenge: String) {
guard let randomBytes = Data(randomBytesCount: 32) else { throw DescopeError.flowFailed.with(message: "Error generating random bytes") }
let hashedBytes = Data(SHA256.hash(data: randomBytes))

Expand All @@ -208,7 +212,7 @@ private func prepareInitialRequest(for runner: DescopeFlowRunner) throws -> (url

guard let initialURL = components.url else { throw DescopeError.flowFailed.with(message: "Failed to create flow URL") }

return (initialURL, codeVerifier)
return (initialURL, codeVerifier, codeChallenge)
}

private func prepareRedirectRequest(for runner: DescopeFlowRunner, redirectURL: URL) -> URL? {
Expand Down
22 changes: 22 additions & 0 deletions src/types/Flows.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,31 @@ import AuthenticationServices
/// email. See the documentation for ``resume(with:)`` for more details.
@MainActor
public class DescopeFlowRunner {
/// Provide authentication info if the flow is being run by an already
/// authenticated user.
public struct Authentication {
/// The flow ID about to be run
public var flowId: String
/// The refresh JWT from and active descope session
public var refreshJwt: String

/// Creates a new ``DescopeFlowRunner.Authentication`` object that encapsulates the
/// information required to run a flow for an authenticated user.
///
/// - Parameter flowId: The flow ID about to be run.
/// - Parameter refreshJwt: The refresh JWT from and active descope session
public init(flowId: String, refreshJwt: String) {
self.flowId = flowId
self.refreshJwt = refreshJwt
}
}

/// The URL where the flow is hosted.
public let flowURL: String

/// Optional authentication info to allow running flows for authenticated users
public var flowAuthentication: Authentication?

/// Whether the authentication view is allowed to access shared user data.
///
/// Setting this to `true` allows the sandboxed browser in the authentication view
Expand Down

0 comments on commit e425857

Please sign in to comment.