diff --git a/Sources/WebAuthn/Ceremonies/Authentication/PublicKeyCredentialRequestOptions.swift b/Sources/WebAuthn/Ceremonies/Authentication/PublicKeyCredentialRequestOptions.swift index 81340ed..290069d 100644 --- a/Sources/WebAuthn/Ceremonies/Authentication/PublicKeyCredentialRequestOptions.swift +++ b/Sources/WebAuthn/Ceremonies/Authentication/PublicKeyCredentialRequestOptions.swift @@ -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`` @@ -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 } } + diff --git a/Sources/WebAuthn/Ceremonies/Authentication/VerifiedAuthentication.swift b/Sources/WebAuthn/Ceremonies/Authentication/VerifiedAuthentication.swift index 723b777..81ba326 100644 --- a/Sources/WebAuthn/Ceremonies/Authentication/VerifiedAuthentication.swift +++ b/Sources/WebAuthn/Ceremonies/Authentication/VerifiedAuthentication.swift @@ -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 @@ -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 } +} diff --git a/Sources/WebAuthn/Ceremonies/Registration/AttestationConveyancePreference.swift b/Sources/WebAuthn/Ceremonies/Registration/AttestationConveyancePreference.swift index 770af12..e17e8f9 100644 --- a/Sources/WebAuthn/Ceremonies/Registration/AttestationConveyancePreference.swift +++ b/Sources/WebAuthn/Ceremonies/Registration/AttestationConveyancePreference.swift @@ -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 } } diff --git a/Sources/WebAuthn/Ceremonies/Registration/AttestationFormat.swift b/Sources/WebAuthn/Ceremonies/Registration/AttestationFormat.swift index 521fd38..a046ef1 100644 --- a/Sources/WebAuthn/Ceremonies/Registration/AttestationFormat.swift +++ b/Sources/WebAuthn/Ceremonies/Registration/AttestationFormat.swift @@ -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 } } diff --git a/Sources/WebAuthn/Ceremonies/Shared/CollectedClientData.swift b/Sources/WebAuthn/Ceremonies/Shared/CollectedClientData.swift index 88ca7f3..db562c7 100644 --- a/Sources/WebAuthn/Ceremonies/Shared/CollectedClientData.swift +++ b/Sources/WebAuthn/Ceremonies/Shared/CollectedClientData.swift @@ -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, @@ -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 } +}