Skip to content

Commit

Permalink
feat(passkeys): meet server requirements (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
mhamann committed Feb 27, 2023
1 parent c708bed commit 17a2861
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 9 deletions.
14 changes: 13 additions & 1 deletion Sources/Rownd/Models/Context/AppConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,12 @@ extension AppHubCustomStylesConfigState: Codable {

public struct SignInMethods: Hashable {
public var google: GoogleSignInMethodConfig?
public var passkeys: PasskeysSignInMethodConfig?
}

extension SignInMethods: Codable {
enum CodingKeys: String, CodingKey {
case google
case google, passkeys
}
}

Expand All @@ -145,6 +146,17 @@ extension GoogleSignInMethodConfig: Codable {
}
}

public struct PasskeysSignInMethodConfig: Hashable {
public var enabled: Bool?
public var domains: [String]?
}

extension PasskeysSignInMethodConfig: Codable {
enum CodingKeys: String, CodingKey {
case enabled, domains
}
}

struct SetAppConfig: Action {
var payload: AppConfigState
}
Expand Down
85 changes: 79 additions & 6 deletions Sources/Rownd/Models/PasskeyCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,21 @@ class PasskeyCoordinator: NSObject, ASAuthorizationControllerPresentationContext
let anchor: ASPresentationAnchor = (getWindowScene()?.windows.last?.rootViewController?.view.window)!
let hubViewController = getHubViewController()

guard let subdomain = store.state.appConfig.config?.subdomain else {
logger.trace("Please go to the Rownd dashboard https://app.rownd.io/applications and add a subdomain in mobile sign-in")
return
}

Task {
do {
let challengeResponse: PasskeyRegisterResponse = try await Rownd.apiClient.send(Get.Request(url: URL(string: "/hub/auth/passkeys/registration")!)).value
let challengeResponse: PasskeyRegisterResponse = try await Rownd.apiClient.send(
Get.Request(
url: URL(string: "/hub/auth/passkeys/registration")!,
headers: [
"origin": "https://\(subdomain + Rownd.config.subdomainExtension)"
]
)
).value

await hubViewController?.loadNewPage(targetPage: .connectPasskey, jsFnOptions: RowndConnectPasskeySignInOptions(status: Status.loading, biometricType: LAContext().biometricType.rawValue))

Expand Down Expand Up @@ -117,9 +129,22 @@ class PasskeyCoordinator: NSObject, ASAuthorizationControllerPresentationContext
//Use passkey to sign in as a Rownd user
method = PasskeyCoordinatorMethods.SignIn
let anchor: ASPresentationAnchor = (getWindowScene()?.windows.last?.rootViewController?.view.window)!

guard let subdomain = store.state.appConfig.config?.subdomain else {
logger.trace("Please go to the Rownd dashboard https://app.rownd.io/applications and add a subdomain in mobile sign-in")
return
}

Task {
do {
let challengeResponse: PasskeyAuthenticationResponse = try await Rownd.apiClient.send(Get.Request(url: URL(string: "/hub/auth/passkeys/authentication")!)).value
let challengeResponse: PasskeyAuthenticationResponse = try await Rownd.apiClient.send(
Get.Request(
url: URL(string: "/hub/auth/passkeys/authentication")!,
headers: [
"origin": "https://\(subdomain + Rownd.config.subdomainExtension)"
]
)
).value
signInWith(anchor: anchor, preferImmediatelyAvailableCredentials: false, challengeResponse: challengeResponse)
}
catch {
Expand Down Expand Up @@ -188,10 +213,23 @@ class PasskeyCoordinator: NSObject, ASAuthorizationControllerPresentationContext
let hubViewController = getHubViewController()

Task {
let body: PasskeyRegisterPayload = PasskeyRegisterPayload(response: PasskeyRegisterPayloadResponse(attestationObject: attestationObject?.base64URLEncodedString() ?? "", clientDataJSON: clientDataJSON.base64URLEncodedString()), id: credentialID, rawId: credentialID)
let body: PasskeyRegisterPayload = PasskeyRegisterPayload(
response: PasskeyRegisterPayloadResponse(
attestationObject: attestationObject?.base64URLEncodedString() ?? "",
clientDataJSON: clientDataJSON.base64URLEncodedString()),
id: credentialID,
rawId: credentialID
)

do {
let _ = try await Rownd.apiClient.send(Get.Request(url: URL(string: "/hub/auth/passkeys/registration")!, method: "post", body: body, headers: ["content-type":"application/json"] )).value
let _ = try await Rownd.apiClient.send(Get.Request(
url: URL(string: "/hub/auth/passkeys/registration")!,
method: "post",
body: body,
headers: [
"content-type":"application/json"
]
)).value
await hubViewController?.loadNewPage(targetPage: .connectPasskey, jsFnOptions: RowndConnectPasskeySignInOptions(status: Status.success, biometricType: LAContext().biometricType.rawValue))
} catch {
logger.error("Failed passkey POST registration: \(String(describing: error))")
Expand All @@ -205,18 +243,53 @@ class PasskeyCoordinator: NSObject, ASAuthorizationControllerPresentationContext
let credentialID = credentialAssertion.credentialID.base64URLEncodedString()
let authenticatorData = credentialAssertion.rawAuthenticatorData

let hubViewController = getHubViewController()

Task {
let body: PasskeyAuthenticationPayload = PasskeyAuthenticationPayload(response: PasskeyAuthenticationPayloadResponse(clientDataJSON: clientDataJSON.base64URLEncodedString(), signature: signature?.base64URLEncodedString() ?? "", userHandle: userId?.base64URLEncodedString() ?? "", authenticatorData: authenticatorData?.base64URLEncodedString() ?? ""), rawId: credentialID, id: credentialID)
let body: PasskeyAuthenticationPayload = PasskeyAuthenticationPayload(
response: PasskeyAuthenticationPayloadResponse(
clientDataJSON: clientDataJSON.base64URLEncodedString(),
signature: signature?.base64URLEncodedString() ?? "",
userHandle: userId?.base64URLEncodedString() ?? "",
authenticatorData: authenticatorData?.base64URLEncodedString() ?? ""
),
rawId: credentialID,
id: credentialID
)

do {
let challengeAuthenticationCompleteResponse: PasskeyAuthenticationCompleteResponse = try await Rownd.apiClient.send(Get.Request(url: URL(string: "/hub/auth/passkeys/authentication")!, method: "post", body: body, headers: ["content-type":"application/json"] )).value
let challengeAuthenticationCompleteResponse: PasskeyAuthenticationCompleteResponse = try await Rownd.apiClient.send(
Get.Request(
url: URL(string: "/hub/auth/passkeys/authentication")!,
method: "post",
body: body,
headers: ["content-type":"application/json"]
)
).value

DispatchQueue.main.async {
store.dispatch(SetAuthState(payload: AuthState(accessToken: challengeAuthenticationCompleteResponse.access_token, refreshToken: challengeAuthenticationCompleteResponse.refresh_token)))
store.dispatch(UserData.fetch())
}

await hubViewController?.loadNewPage(
targetPage: .signIn,
jsFnOptions: RowndSignInJsOptions(
loginStep: RowndSignInLoginStep.Success,
intent: .signIn,
userType: .ExistingUser
)
)
} catch {
logger.error("Failed passkey POST registration: \(String(describing: error))")
await hubViewController?.loadNewPage(
targetPage: .signIn,
jsFnOptions: RowndSignInJsOptions(
loginStep: RowndSignInLoginStep.Init,
intent: .signIn,
userType: .ExistingUser
)
)
}
}
default:
Expand Down
5 changes: 5 additions & 0 deletions Sources/Rownd/Models/RowndHubInteropMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum MessageType: String, Codable {
case triggerSignInWithApple = "trigger_sign_in_with_apple"
case triggerSignInWithGoogle = "trigger_sign_in_with_google"
case triggerSignUpWithPasskey = "trigger_sign_up_with_passkey"
case triggerSignInWithPasskey = "trigger_sign_in_with_passkey"
case userDataUpdate = "user_data_update"
case tryAgain = "try_again"
case hubLoaded = "hub_loaded"
Expand Down Expand Up @@ -78,6 +79,7 @@ enum MessagePayload: Decodable {
case triggerSignInWithGoogle(TriggerSignInWithGoogleMessage)
case triggerSignUpWithPasskey
case hubLoaded
case triggerSignInWithPasskey
case tryAgain

enum CodingKeys: String, CodingKey {
Expand Down Expand Up @@ -106,6 +108,9 @@ enum MessagePayload: Decodable {
case .triggerSignUpWithPasskey:
self = .triggerSignUpWithPasskey

case .triggerSignInWithPasskey:
self = .triggerSignInWithPasskey

case .authentication:
let payload = try objectContainer.decode(AuthenticationMessage.self)
self = .authentication(payload)
Expand Down
10 changes: 8 additions & 2 deletions Sources/Rownd/Rownd.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public class Rownd: NSObject {
}

public static func requestSignIn(with: RowndSignInHint, signInOptions: RowndSignInOptions?, completion: (() -> Void)? = nil) {
var signInOptions = determineSignInOptions(signInOptions)
let signInOptions = determineSignInOptions(signInOptions)
switch with {
case .appleId:
appleSignUpCoordinator?.signIn(signInOptions?.intent)
Expand Down Expand Up @@ -171,7 +171,13 @@ public class Rownd: NSObject {
store.dispatch(UserData.fetch())
store.dispatch(SetLastSignInMethod(payload: SignInMethodTypes.google))

requestSignIn(jsFnOptions: RowndSignInJsOptions(loginStep: RowndSignInLoginStep.Success,intent: signInOptions?.intent, userType: tokenResponse?.userType))
requestSignIn(
jsFnOptions: RowndSignInJsOptions(
loginStep: RowndSignInLoginStep.Success,
intent: signInOptions?.intent,
userType: tokenResponse?.userType
)
)
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/Rownd/Views/HubWebView/HubWebViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ extension HubWebViewController: WKScriptMessageHandler, WKNavigationDelegate {
Rownd.requestSignIn(with: .googleId, signInOptions: RowndSignInOptions(intent: signInWithGoogleMessage?.intent))
case .triggerSignUpWithPasskey:
HubWebViewController.passkeyCoordinator?.signUpWith()

case .triggerSignInWithPasskey:
Rownd.requestSignIn(with: .passkey)

case .signOut:
guard hubViewController?.targetPage == .signOut else { return }
Expand Down

0 comments on commit 17a2861

Please sign in to comment.