Skip to content
Open
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
Expand Up @@ -70,20 +70,40 @@ public struct PublicKeyCredentialRequestOptions: Encodable, Sendable {
public struct PublicKeyCredentialDescriptor: Equatable, Encodable, Sendable {
/// Defines hints as to how clients might communicate with a particular authenticator in order to obtain an
/// assertion for a specific credential
public enum AuthenticatorTransport: String, Equatable, Encodable, Sendable {
public struct AuthenticatorTransport: RawRepresentable, Equatable, Hashable, Encodable, Sendable {
public let rawValue: String

public init?(rawValue: String) {
switch rawValue {
case "usb", "nfc", "ble", "hybrid", "internal":
self.rawValue = rawValue
default:
return nil
}
}

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

public func encode(to encoder: any Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}

/// Indicates the respective authenticator can be contacted over removable USB.
case usb
public static let usb = AuthenticatorTransport("usb")
/// Indicates the respective authenticator can be contacted over Near Field Communication (NFC).
case nfc
public static let nfc = AuthenticatorTransport("nfc")
/// Indicates the respective authenticator can be contacted over Bluetooth Smart (Bluetooth Low Energy / BLE).
case ble
public static let ble = AuthenticatorTransport("ble")
/// Indicates the respective authenticator can be contacted using a combination of (often separate)
/// data-transport and proximity mechanisms. This supports, for example, authentication on a desktop
/// computer using a smartphone.
case hybrid
public static let hybrid = AuthenticatorTransport("hybrid")
/// Indicates the respective authenticator is contacted using a client device-specific transport, i.e., it is
/// a platform authenticator. These authenticators are not removable from the client device.
case `internal`
public static let `internal` = AuthenticatorTransport("internal")
}

/// Will always be ``CredentialType/publicKey``
Expand Down Expand Up @@ -122,15 +142,44 @@ public struct PublicKeyCredentialDescriptor: Equatable, Encodable, Sendable {
}
}

extension PublicKeyCredentialDescriptor.AuthenticatorTransport: CustomStringConvertible {
public var description: String { rawValue }
}

/// The Relying Party may require user verification for some of its operations but not for others, and may use this
/// type to express its needs.
public enum UserVerificationRequirement: String, Encodable, Sendable {
public struct UserVerificationRequirement: RawRepresentable, Equatable, Hashable, Encodable, Sendable {
public let rawValue: String

public init?(rawValue: String) {
switch rawValue {
case "required", "preferred", "discouraged":
self.rawValue = rawValue
default:
return nil
}
}

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

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}

/// The Relying Party requires user verification for the operation and will fail the overall ceremony if the
/// user wasn't verified.
case required
public static let required = UserVerificationRequirement("required")
/// The Relying Party prefers user verification for the operation if possible, but will not fail the operation.
case preferred
public static let preferred = UserVerificationRequirement("preferred")
/// The Relying Party does not want user verification employed during the operation (e.g., in the interest of
/// minimizing disruption to the user interaction flow).
case discouraged
public static let discouraged = UserVerificationRequirement("discouraged")
}

extension UserVerificationRequirement: CustomStringConvertible {
public var description: String { rawValue }
}

Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,34 @@ import Foundation

/// On successful authentication, this structure contains a summary of the authentication flow
public struct VerifiedAuthentication: Sendable {
public enum CredentialDeviceType: String, Sendable {
case singleDevice = "single_device"
case multiDevice = "multi_device"
public struct CredentialDeviceType: RawRepresentable, Equatable, Hashable, Codable, Sendable {
public let rawValue: String

public init?(rawValue: String) {
switch rawValue {
case "single_device", "multi_device":
self.rawValue = rawValue
default:
return nil
}
}

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

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
rawValue = try container.decode(String.self)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}

public static let singleDevice = CredentialDeviceType("single_device")
public static let multiDevice = CredentialDeviceType("multi_device")
}

/// The credential id associated with the public key
Expand All @@ -30,3 +55,7 @@ public struct VerifiedAuthentication: Sendable {
/// Whether the authenticator is known to be backed up currently
public let credentialBackedUp: Bool
}

extension VerifiedAuthentication.CredentialDeviceType: CustomStringConvertible {
public var description: String { rawValue }
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,34 @@
/// Options to specify the Relying Party's preference regarding attestation conveyance during credential generation.
///
/// Currently only supports `none`.
public enum AttestationConveyancePreference: String, Encodable, Sendable {
public struct AttestationConveyancePreference: RawRepresentable, Equatable, Hashable, Encodable, Sendable {
public let rawValue: String

public init?(rawValue: String) {
switch rawValue {
case "none":
self.rawValue = rawValue
default:
return nil
}
}

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

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}

/// Indicates the Relying Party is not interested in authenticator attestation.
case none
// case indirect
// case direct
// case enterprise
public static let none = AttestationConveyancePreference("none")
// public static let indirect = AttestationConveyancePreference("indirect")
// public static let direct = AttestationConveyancePreference("direct")
// public static let enterprise = AttestationConveyancePreference("enterprise")
}

extension AttestationConveyancePreference: CustomStringConvertible {
public var description: String { rawValue }
}
45 changes: 37 additions & 8 deletions Sources/WebAuthn/Ceremonies/Registration/AttestationFormat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,41 @@
//
//===----------------------------------------------------------------------===//

public enum AttestationFormat: String, RawRepresentable, Equatable, Sendable {
case packed
case tpm
case androidKey = "android-key"
case androidSafetynet = "android-safetynet"
case fidoU2F = "fido-u2f"
case apple
case none
public struct AttestationFormat: RawRepresentable, Equatable, Hashable, Codable, Sendable {
public let rawValue: String

public init?(rawValue: String) {
switch rawValue {
case "packed", "tpm", "android-key", "android-safetynet", "fido-u2f", "apple", "none":
self.rawValue = rawValue
default:
return nil
}
}

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

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
rawValue = try container.decode(String.self)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}

public static let packed = AttestationFormat("packed")
public static let tpm = AttestationFormat("tpm")
public static let androidKey = AttestationFormat("android-key")
public static let androidSafetynet = AttestationFormat("android-safetynet")
public static let fidoU2F = AttestationFormat("fido-u2f")
public static let apple = AttestationFormat("apple")
public static let none = AttestationFormat("none")
}

extension AttestationFormat: CustomStringConvertible {
public var description: String { rawValue }
}
35 changes: 32 additions & 3 deletions Sources/WebAuthn/Ceremonies/Shared/CollectedClientData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,34 @@ public struct CollectedClientData: Codable, Hashable, Sendable {
case originDoesNotMatch
}

public enum CeremonyType: String, Codable, Sendable {
case create = "webauthn.create"
case assert = "webauthn.get"
public struct CeremonyType: RawRepresentable, Equatable, Hashable, Codable, Sendable {
public let rawValue: String

public init?(rawValue: String) {
switch rawValue {
case "webauthn.create", "webauthn.get":
self.rawValue = rawValue
default:
return nil
}
}

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

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
rawValue = try container.decode(String.self)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}

public static let create = CeremonyType("webauthn.create")
public static let assert = CeremonyType("webauthn.get")
}

/// Contains the string "webauthn.create" when creating new credentials,
Expand All @@ -42,3 +67,7 @@ public struct CollectedClientData: Codable, Hashable, Sendable {
guard origin == relyingPartyOrigin else { throw .originDoesNotMatch }
}
}

extension CollectedClientData.CeremonyType: CustomStringConvertible {
public var description: String { rawValue }
}