-
Notifications
You must be signed in to change notification settings - Fork 29
RSA support and modularity add-ons #87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,7 +34,7 @@ public struct AuthenticationCredential: Sendable { | |
public let type: CredentialType | ||
} | ||
|
||
extension AuthenticationCredential: Decodable { | ||
extension AuthenticationCredential: Codable { | ||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
|
||
|
@@ -44,6 +44,17 @@ extension AuthenticationCredential: Decodable { | |
authenticatorAttachment = try container.decodeIfPresent(AuthenticatorAttachment.self, forKey: .authenticatorAttachment) | ||
type = try container.decode(CredentialType.self, forKey: .type) | ||
} | ||
|
||
public func encode(to encoder: Encoder) throws { | ||
var container = encoder.container(keyedBy: CodingKeys.self) | ||
|
||
try container.encode(id, forKey: .id) | ||
try container.encode(rawID.base64URLEncodedString(), forKey: .rawID) | ||
try container.encode(response, forKey: .response) | ||
try container.encodeIfPresent(authenticatorAttachment, forKey: .authenticatorAttachment) | ||
try container.encode(type, forKey: .type) | ||
} | ||
Comment on lines
+48
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: formatting |
||
|
||
|
||
private enum CodingKeys: String, CodingKey { | ||
case id | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,7 +48,7 @@ public struct AuthenticatorAssertionResponse: Sendable { | |
public let attestationObject: [UInt8]? | ||
} | ||
|
||
extension AuthenticatorAssertionResponse: Decodable { | ||
extension AuthenticatorAssertionResponse: Codable { | ||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
|
||
|
@@ -58,6 +58,17 @@ extension AuthenticatorAssertionResponse: Decodable { | |
userHandle = try container.decodeBytesFromURLEncodedBase64IfPresent(forKey: .userHandle) | ||
attestationObject = try container.decodeBytesFromURLEncodedBase64IfPresent(forKey: .attestationObject) | ||
} | ||
|
||
public func encode(to encoder: Encoder) throws { | ||
var container = encoder.container(keyedBy: CodingKeys.self) | ||
|
||
try container.encode(clientDataJSON.base64URLEncodedString(), forKey: .clientDataJSON) | ||
try container.encode(authenticatorData.base64URLEncodedString(), forKey: .authenticatorData) | ||
try container.encode(signature.base64URLEncodedString(), forKey: .signature) | ||
try container.encodeIfPresent(userHandle?.base64URLEncodedString(),forKey: .userHandle) | ||
try container.encodeIfPresent(attestationObject?.base64URLEncodedString(),forKey: .attestationObject) | ||
} | ||
Comment on lines
+62
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: formatting |
||
|
||
|
||
private enum CodingKeys: String, CodingKey { | ||
case clientDataJSON | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -12,40 +12,42 @@ | |||||||||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||||||||||
|
||||||||||||||||||||
import Foundation | ||||||||||||||||||||
import SwiftOpenAPI | ||||||||||||||||||||
|
||||||||||||||||||||
/// The `PublicKeyCredentialCreationOptions` gets passed to the WebAuthn API (`navigator.credentials.create()`) | ||||||||||||||||||||
/// | ||||||||||||||||||||
/// Generally this should not be created manually. Instead use `RelyingParty.beginRegistration()`. When encoding using | ||||||||||||||||||||
/// `Encodable` byte arrays are base64url encoded. | ||||||||||||||||||||
/// | ||||||||||||||||||||
/// - SeeAlso: https://www.w3.org/TR/webauthn-2/#dictionary-makecredentialoptions | ||||||||||||||||||||
public struct PublicKeyCredentialCreationOptions: Encodable, Sendable { | ||||||||||||||||||||
@OpenAPIDescriptable | ||||||||||||||||||||
public struct PublicKeyCredentialCreationOptions: Codable, Sendable { | ||||||||||||||||||||
/// A byte array randomly generated by the Relying Party. Should be at least 16 bytes long to ensure sufficient | ||||||||||||||||||||
/// entropy. | ||||||||||||||||||||
/// | ||||||||||||||||||||
/// The Relying Party should store the challenge temporarily until the registration flow is complete. When | ||||||||||||||||||||
/// encoding using `Encodable`, the challenge is base64url encoded. | ||||||||||||||||||||
public let challenge: [UInt8] | ||||||||||||||||||||
public var challenge: [UInt8] | ||||||||||||||||||||
|
||||||||||||||||||||
/// Contains names and an identifier for the user account performing the registration | ||||||||||||||||||||
public let user: PublicKeyCredentialUserEntity | ||||||||||||||||||||
public var user: PublicKeyCredentialUserEntity | ||||||||||||||||||||
|
||||||||||||||||||||
/// Contains a name and an identifier for the Relying Party responsible for the request | ||||||||||||||||||||
public let relyingParty: PublicKeyCredentialRelyingPartyEntity | ||||||||||||||||||||
public var relyingParty: PublicKeyCredentialRelyingPartyEntity | ||||||||||||||||||||
|
||||||||||||||||||||
/// A list of key types and signature algorithms the Relying Party supports. Ordered from most preferred to least | ||||||||||||||||||||
/// preferred. | ||||||||||||||||||||
public let publicKeyCredentialParameters: [PublicKeyCredentialParameters] | ||||||||||||||||||||
public var publicKeyCredentialParameters: [PublicKeyCredentialParameters] | ||||||||||||||||||||
|
||||||||||||||||||||
/// 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. | ||||||||||||||||||||
/// | ||||||||||||||||||||
/// - Note: When encoded, this value is represented in milleseconds as a ``UInt32``. | ||||||||||||||||||||
public let timeout: Duration? | ||||||||||||||||||||
public var timeout: Duration? | ||||||||||||||||||||
|
||||||||||||||||||||
/// Sets the Relying Party's preference for attestation conveyance. At the time of writing only `none` is | ||||||||||||||||||||
/// supported. | ||||||||||||||||||||
public let attestation: AttestationConveyancePreference | ||||||||||||||||||||
public var attestation: AttestationConveyancePreference | ||||||||||||||||||||
|
||||||||||||||||||||
public func encode(to encoder: Encoder) throws { | ||||||||||||||||||||
var container = encoder.container(keyedBy: CodingKeys.self) | ||||||||||||||||||||
|
@@ -57,6 +59,29 @@ public struct PublicKeyCredentialCreationOptions: Encodable, Sendable { | |||||||||||||||||||
try container.encodeIfPresent(timeout?.milliseconds, forKey: .timeout) | ||||||||||||||||||||
try container.encode(attestation, forKey: .attestation) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
public init(from decoder: any Decoder) throws { | ||||||||||||||||||||
let values = try decoder.container(keyedBy: CodingKeys.self) | ||||||||||||||||||||
|
||||||||||||||||||||
self.challenge = try values.decodeBytesFromURLEncodedBase64(forKey: .challenge) | ||||||||||||||||||||
self.user = try values.decode(PublicKeyCredentialUserEntity.self, forKey: .user) | ||||||||||||||||||||
self.relyingParty = try values.decode(PublicKeyCredentialRelyingPartyEntity.self, forKey:.relyingParty) | ||||||||||||||||||||
self.publicKeyCredentialParameters = try values.decode([PublicKeyCredentialParameters].self, forKey:.publicKeyCredentialParameters) | ||||||||||||||||||||
if let timeout = try values.decodeIfPresent(UInt32.self,forKey:.timeout) { | ||||||||||||||||||||
self.timeout = Duration.milliseconds(timeout) | ||||||||||||||||||||
} | ||||||||||||||||||||
self.attestation = try values.decode(AttestationConveyancePreference.self, forKey:.attestation) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
public init(challenge: [UInt8],user: PublicKeyCredentialUserEntity,relyingParty: PublicKeyCredentialRelyingPartyEntity,publicKeyCredentialParameters: [PublicKeyCredentialParameters], | ||||||||||||||||||||
timeout: Duration?,attestation: AttestationConveyancePreference) { | ||||||||||||||||||||
Comment on lines
+76
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: formatting
Suggested change
|
||||||||||||||||||||
self.challenge = challenge | ||||||||||||||||||||
self.user = user | ||||||||||||||||||||
self.relyingParty = relyingParty | ||||||||||||||||||||
self.publicKeyCredentialParameters = publicKeyCredentialParameters | ||||||||||||||||||||
self.timeout = timeout | ||||||||||||||||||||
self.attestation = attestation | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private enum CodingKeys: String, CodingKey { | ||||||||||||||||||||
case challenge | ||||||||||||||||||||
|
@@ -70,7 +95,7 @@ public struct PublicKeyCredentialCreationOptions: Encodable, Sendable { | |||||||||||||||||||
|
||||||||||||||||||||
// MARK: - Credential parameters | ||||||||||||||||||||
/// From §5.3 (https://w3c.github.io/TR/webauthn/#dictionary-credential-params) | ||||||||||||||||||||
public struct PublicKeyCredentialParameters: Equatable, Encodable, Sendable { | ||||||||||||||||||||
public struct PublicKeyCredentialParameters: Equatable, Codable, Sendable { | ||||||||||||||||||||
/// The type of credential to be created. At the time of writing always ``CredentialType/publicKey``. | ||||||||||||||||||||
public let type: CredentialType | ||||||||||||||||||||
/// The cryptographic signature algorithm with which the newly generated credential will be used, and thus also | ||||||||||||||||||||
|
@@ -87,6 +112,18 @@ public struct PublicKeyCredentialParameters: Equatable, Encodable, Sendable { | |||||||||||||||||||
self.type = type | ||||||||||||||||||||
self.alg = alg | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
public init(from decoder: any Decoder) throws { | ||||||||||||||||||||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||||||||||||||||||||
let type = try container.decode(CredentialType.self,forKey: .type) | ||||||||||||||||||||
let alg = try container.decode(COSEAlgorithmIdentifier.self, forKey: .alg) | ||||||||||||||||||||
self.init(type:type,alg:alg) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private enum CodingKeys: String, CodingKey { | ||||||||||||||||||||
case type | ||||||||||||||||||||
case alg | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
extension Array where Element == PublicKeyCredentialParameters { | ||||||||||||||||||||
|
@@ -103,22 +140,31 @@ extension Array where Element == PublicKeyCredentialParameters { | |||||||||||||||||||
/// From §5.4.2 (https://www.w3.org/TR/webauthn/#sctn-rp-credential-params). | ||||||||||||||||||||
/// The PublicKeyCredentialRelyingPartyEntity dictionary is used to supply additional Relying Party attributes when | ||||||||||||||||||||
/// creating a new credential. | ||||||||||||||||||||
public struct PublicKeyCredentialRelyingPartyEntity: Encodable, Sendable { | ||||||||||||||||||||
public struct PublicKeyCredentialRelyingPartyEntity: Codable, Sendable { | ||||||||||||||||||||
/// A unique identifier for the Relying Party entity. | ||||||||||||||||||||
public let id: String | ||||||||||||||||||||
|
||||||||||||||||||||
/// A human-readable identifier for the Relying Party, intended only for display. For example, "ACME Corporation", | ||||||||||||||||||||
/// "Wonderful Widgets, Inc." or "ОАО Примертех". | ||||||||||||||||||||
public let name: String | ||||||||||||||||||||
|
||||||||||||||||||||
public init(_ src : PublicKeyCredentialRelyingPartyEntity) { | ||||||||||||||||||||
self.id = src.id | ||||||||||||||||||||
self.name = src.name | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
public init(id : String, name : String) { | ||||||||||||||||||||
self.id = id | ||||||||||||||||||||
self.name = name | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
/// From §5.4.3 (https://www.w3.org/TR/webauthn/#dictionary-user-credential-params) | ||||||||||||||||||||
/// The PublicKeyCredentialUserEntity dictionary is used to supply additional user account attributes when | ||||||||||||||||||||
/// creating a new credential. | ||||||||||||||||||||
/// | ||||||||||||||||||||
/// When encoding using `Encodable`, `id` is base64url encoded. | ||||||||||||||||||||
public struct PublicKeyCredentialUserEntity: Encodable, Sendable { | ||||||||||||||||||||
public struct PublicKeyCredentialUserEntity: Codable, Sendable { | ||||||||||||||||||||
/// Generated by the Relying Party, unique to the user account, and must not contain personally identifying | ||||||||||||||||||||
/// information about the user. | ||||||||||||||||||||
/// | ||||||||||||||||||||
|
@@ -149,6 +195,15 @@ public struct PublicKeyCredentialUserEntity: Encodable, Sendable { | |||||||||||||||||||
try container.encode(name, forKey: .name) | ||||||||||||||||||||
try container.encode(displayName, forKey: .displayName) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
public init(from decoder: any Decoder) throws { | ||||||||||||||||||||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||||||||||||||||||||
let id = try container.decodeBytesFromURLEncodedBase64(forKey: .id) | ||||||||||||||||||||
let name = try container.decode(String.self, forKey: .name) | ||||||||||||||||||||
let displayName = try container.decode(String.self, forKey: .displayName) | ||||||||||||||||||||
self.init(id: id, name: name, displayName: displayName) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
private enum CodingKeys: String, CodingKey { | ||||||||||||||||||||
case id | ||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't feel appropriate to add to this package as it isn't guaranteed to be used with vapor or OpenAPI. As an alternative to removing it completely, could this instead live in a separate package that augments swift-webauthn for those who want this functionality.