Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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,78 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift WebAuthn open source project
//
// Copyright (c) 2022 the Swift WebAuthn project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Foundation

/// A dictionary describing the Relying Party's requirements regarding authenticator attributes.
///
/// - SeeAlso: [WebAuthn Level 3 Working Draft §5.4.4. Authenticator Selection Criteria](https://www.w3.org/TR/webauthn-3/#dictionary-authenticatorSelection)
public struct AuthenticatorSelection: Codable, Sendable, Hashable {
/// If present, indicates the Relying Party's preference for authenticator attachment.
/// - SeeAlso: [WebAuthn Level 3 Working Draft §5.4.4. Authenticator Selection Criteria](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-authenticatorattachment)
public var authenticatorAttachment: AuthenticatorAttachment?

/// Describes the Relying Party's requirements regarding whether the authenticator should create a client-side-resident public key credential source.
/// - SeeAlso: [WebAuthn Level 3 Working Draft §5.4.4. Authenticator Selection Criteria](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-residentkey)
public var residentKey: ResidentKeyRequirement?

/// Describes the Relying Party's requirements regarding user verification.
/// - SeeAlso: [WebAuthn Level 3 Working Draft §5.4.4. Authenticator Selection Criteria](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-userverification)
public var userVerification: UserVerificationRequirement?

public init(
authenticatorAttachment: AuthenticatorAttachment? = nil,
residentKey: ResidentKeyRequirement? = nil,
userVerification: UserVerificationRequirement? = nil
) {
self.authenticatorAttachment = authenticatorAttachment
self.residentKey = residentKey
self.userVerification = userVerification
}
}

extension AuthenticatorSelection {
public init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

self.authenticatorAttachment = try container.decodeIfPresent(
AuthenticatorAttachment.self, forKey: .authenticatorAttachment)
self.residentKey = try container.decodeIfPresent(
ResidentKeyRequirement.self, forKey: .residentKey)
self.userVerification = try container.decodeIfPresent(
UserVerificationRequirement.self, forKey: .userVerification)

// requireResidentKey is ignored during decoding as it's derived from residentKey
// It's only included in encoding for backwards compatibility with WebAuthn Level 1
}

public func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

try container.encodeIfPresent(authenticatorAttachment, forKey: .authenticatorAttachment)
try container.encodeIfPresent(residentKey, forKey: .residentKey)
try container.encodeIfPresent(userVerification, forKey: .userVerification)

// requireResidentKey is included for backwards compatibility with WebAuthn Level 1
// It should be true if and only if residentKey is set to .required
let requireResidentKey = residentKey == .required
try container.encode(requireResidentKey, forKey: .requireResidentKey)
}

private enum CodingKeys: String, CodingKey {
case authenticatorAttachment
case residentKey
case userVerification
case requireResidentKey
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ public struct PublicKeyCredentialCreationOptions: Sendable {
/// Sets the Relying Party's preference for attestation conveyance. At the time of writing only ``AttestationConveyancePreference/none`` is supported.
public var attestation: AttestationConveyancePreference

/// A dictionary describing the Relying Party's requirements regarding authenticator attributes.
public var authenticatorSelection: AuthenticatorSelection?

/// Initialize a credential creation options dictionary directly.
///
/// - Warning: Manually initializing options dictionaries can easily lead to insecure implementations of the WebAuthn protocol. Whenever possible, create an options dictionary using ``WebAuthnManager/beginRegistration(user:timeout:attestation:publicKeyCredentialParameters:)`` instead.
Expand All @@ -64,20 +67,23 @@ public struct PublicKeyCredentialCreationOptions: Sendable {
/// - publicKeyCredentialParameters: A list of key types and signature algorithms the Relying Party supports. Ordered from most preferred to least preferred.
/// - timeout: A time, in seconds, that the caller is willing to wait for the call to complete. This is treated as a hint, and may be overridden by the client.
/// - attestation: Sets the Relying Party's preference for attestation conveyance. At the time of writing only `none` is supported.
/// - authenticatorSelection: A dictionary describing the Relying Party's requirements regarding authenticator attributes.
public init(
challenge: [UInt8],
user: PublicKeyCredentialUserEntity,
relyingParty: PublicKeyCredentialRelyingPartyEntity,
publicKeyCredentialParameters: [PublicKeyCredentialParameters],
timeout: Duration?,
attestation: AttestationConveyancePreference
attestation: AttestationConveyancePreference,
authenticatorSelection: AuthenticatorSelection? = nil
) {
self.challenge = challenge
self.user = user
self.relyingParty = relyingParty
self.publicKeyCredentialParameters = publicKeyCredentialParameters
self.timeout = timeout
self.attestation = attestation
self.authenticatorSelection = authenticatorSelection
}
}

Expand All @@ -93,6 +99,8 @@ extension PublicKeyCredentialCreationOptions: Codable {
self.timeout = .milliseconds(timeout)
}
self.attestation = try values.decode(AttestationConveyancePreference.self, forKey: .attestation)
self.authenticatorSelection = try values.decodeIfPresent(
AuthenticatorSelection.self, forKey: .authenticatorSelection)
}

public func encode(to encoder: any Encoder) throws {
Expand All @@ -104,6 +112,7 @@ extension PublicKeyCredentialCreationOptions: Codable {
try container.encode(publicKeyCredentialParameters, forKey: .publicKeyCredentialParameters)
try container.encodeIfPresent(timeout?.milliseconds, forKey: .timeout)
try container.encode(attestation, forKey: .attestation)
try container.encodeIfPresent(authenticatorSelection, forKey: .authenticatorSelection)
}

private enum CodingKeys: String, CodingKey {
Expand All @@ -113,6 +122,7 @@ extension PublicKeyCredentialCreationOptions: Codable {
case publicKeyCredentialParameters = "pubKeyCredParams"
case timeout
case attestation
case authenticatorSelection
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift WebAuthn open source project
//
// Copyright (c) 2022 the Swift WebAuthn project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Foundation

/// The Relying Party's requirements regarding whether the authenticator should create a client-side-resident public key credential source.
///
/// - SeeAlso: [WebAuthn Level 3 Working Draft §5.4.6. Resident Key Requirement Enumeration](https://www.w3.org/TR/webauthn-3/#enum-residentKeyRequirement)
public struct ResidentKeyRequirement: UnreferencedStringEnumeration, Sendable {
public var rawValue: String

public init(_ rawValue: String) {
self.rawValue = rawValue
}

/// This value indicates the Relying Party requires a client-side-resident credential (i.e., a discoverable credential).
///
/// If the authenticator cannot create a client-side-resident credential, it will return an error.
/// - SeeAlso: [WebAuthn Level 3 Working Draft §5.4.6. Resident Key Requirement Enumeration](https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-required)
public static let required: Self = "required"

/// This value indicates the Relying Party strongly prefers a client-side-resident credential, but will accept a server-side credential.
/// - SeeAlso: [WebAuthn Level 3 Working Draft §5.4.6. Resident Key Requirement Enumeration](https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-preferred)
public static let preferred: Self = "preferred"

/// This value indicates the Relying Party strongly prefers a server-side credential, but will accept a client-side-resident credential.
/// - SeeAlso: [WebAuthn Level 3 Working Draft §5.4.6. Resident Key Requirement Enumeration](https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-discouraged)
public static let discouraged: Self = "discouraged"
}

8 changes: 6 additions & 2 deletions Sources/WebAuthn/WebAuthnManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,15 @@ public struct WebAuthnManager: Sendable {
/// - attestation: The Relying Party's preference regarding attestation. Defaults to `.none`.
/// - publicKeyCredentialParameters: A list of public key algorithms the Relying Party chooses to restrict
/// support to. Defaults to all supported algorithms.
/// - authenticatorSelection: The Relying Party's authenticator selection criteria that should be communicated to the client when choosing an authenticator to use.
/// Defaults to `nil` (no requirements).
/// - Returns: Registration options ready for the browser.
public func beginRegistration(
user: PublicKeyCredentialUserEntity,
timeout: Duration? = .seconds(5*60),
attestation: AttestationConveyancePreference = .none,
publicKeyCredentialParameters: [PublicKeyCredentialParameters] = .supported
publicKeyCredentialParameters: [PublicKeyCredentialParameters] = .supported,
authenticatorSelection: AuthenticatorSelection? = nil
) -> PublicKeyCredentialCreationOptions {
let challenge = challengeGenerator.generate()

Expand All @@ -72,7 +75,8 @@ public struct WebAuthnManager: Sendable {
relyingParty: .init(id: configuration.relyingPartyID, name: configuration.relyingPartyName),
publicKeyCredentialParameters: publicKeyCredentialParameters,
timeout: timeout,
attestation: attestation
attestation: attestation,
authenticatorSelection: authenticatorSelection
)
}

Expand Down
Loading
Loading