Skip to content

Commit

Permalink
Merge pull request #15 from niscy-eudiw/feature/draft-13-support
Browse files Browse the repository at this point in the history
Feature - Draft 13 support
  • Loading branch information
dtsiflit authored Apr 1, 2024
2 parents 7c3fe7d + 6de77ed commit 60ef9cd
Showing 75 changed files with 1,668 additions and 833 deletions.
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/niscy-eudiw/JOSESwift.git",
"state" : {
"revision" : "ef86488a729fb1eb85edc3b174f528ac212c5ce6",
"version" : "2.4.1"
"revision" : "518cedba79ef18867191811b161471298b6cb7c8",
"version" : "2.4.1-gcm"
}
},
{
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -14,7 +14,10 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),
.package(url: "https://github.com/niscy-eudiw/JOSESwift.git", from: "2.4.1"),
.package(
url: "https://github.com/niscy-eudiw/JOSESwift.git",
exact: "2.4.1-gcm"
),
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.6.0"),
],
targets: [
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -56,7 +56,7 @@ To obtain the credentials issuer metadata use the ```CredentialIssuerMetadataRes
import OpenID4VCI

let credentialIdentifier = try CredentialIdentifier(value: "https://....")
let credentialIssuerIdentifier = try CredentialIssuerId(CredentialIssuer_URL)
let credentialIssuerIdentifier = try CredentialIssuerId(CREDENTIAL_ISSUER_PUBLIC_URL)

let resolver = CredentialIssuerMetadataResolver()
let metadata = await resolver.resolve(
@@ -125,7 +125,7 @@ It consists of two components:
import OpenID4VCI

let credentialIdentifier = try CredentialIdentifier(value: "https://....")
let credentialIssuerIdentifier = try CredentialIssuerId(CredentialIssuer_URL)
let credentialIssuerIdentifier = try CredentialIssuerId(CREDENTIAL_ISSUER_PUBLIC_URL)
let offer: CredentialOffer = ...
let config: WalletOpenId4VCIConfig = ...

@@ -144,7 +144,7 @@ Given an ```Issuer``` use [Authorization Code Flow](https://openid.github.io/Ope
import OpenID4VCI

let parPlaced = await issuer.pushAuthorizationCodeRequest(
credentials: offer.credentials
credentialOffer: offer
)

if case let .success(request) = parPlaced,
@@ -199,7 +199,7 @@ import eu.europa.ec.eudi.openid4vci.*
let requestOutcome = try await issuer.requestSingle(
proofRequest: ...,
bindingKey: ...,
credentialIdentifier: ...,
requestCredentialIdentifier: ...,
responseEncryptionSpecProvider: {
Issuer.createResponseEncryptionSpec($0)
}
Original file line number Diff line number Diff line change
@@ -18,4 +18,22 @@ import Foundation
public enum IdentityAndAccessManagementMetadata {
case oidc(OIDCProviderMetadata)
case oauth(AuthorizationServerMetadata)

var authorizationServerSupportsPar: Bool {
switch self {
case .oidc(let metaData):
return metaData.pushedAuthorizationRequestEndpoint != nil
case .oauth(let metaData):
return metaData.pushedAuthorizationRequestEndpoint != nil
}
}

var pushedAuthorizationRequestEndpointURI: URL? {
switch self {
case .oidc(let metaData):
return URL(string: metaData.pushedAuthorizationRequestEndpoint ?? "")
case .oauth(let metaData):
return URL(string: metaData.pushedAuthorizationRequestEndpoint ?? "")
}
}
}
96 changes: 96 additions & 0 deletions Sources/Entities/AuthorizationDetails.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2023 European Commission
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Foundation

// MARK: - OidCredentialAuthorizationDetail

public protocol OidCredentialAuthorizationDetail {}

// MARK: - ByCredentialConfiguration

public struct ByCredentialConfiguration: Codable, OidCredentialAuthorizationDetail {
public let credentialConfigurationId: CredentialConfigurationIdentifier
public let credentialIdentifiers: [CredentialIdentifier]?

public init(credentialConfigurationId: CredentialConfigurationIdentifier, credentialIdentifiers: [CredentialIdentifier]? = nil) {
self.credentialConfigurationId = credentialConfigurationId
self.credentialIdentifiers = credentialIdentifiers
}
}

// MARK: - ByFormat

public enum ByFormat: Codable, OidCredentialAuthorizationDetail {
case msoMdocAuthorizationDetails(MsoMdocAuthorizationDetails)
case sdJwtVcAuthorizationDetails(SdJwtVcAuthorizationDetails)

public enum CodingKeys: String, CodingKey {
case type, details
}

public enum ByFormatType: String, Codable {
case msoMdocAuthorizationDetails
case sdJwtVcAuthorizationDetails
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ByFormatType.self, forKey: .type)
let nestedDecoder = try container.superDecoder(forKey: .details)

switch type {
case .msoMdocAuthorizationDetails:
let details = try MsoMdocAuthorizationDetails(from: nestedDecoder)
self = .msoMdocAuthorizationDetails(details)
case .sdJwtVcAuthorizationDetails:
let details = try SdJwtVcAuthorizationDetails(from: nestedDecoder)
self = .sdJwtVcAuthorizationDetails(details)
}
}

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

switch self {
case .msoMdocAuthorizationDetails(let details):
try container.encode(ByFormatType.msoMdocAuthorizationDetails, forKey: .type)
try details.encode(to: container.superEncoder(forKey: .details))
case .sdJwtVcAuthorizationDetails(let details):
try container.encode(ByFormatType.sdJwtVcAuthorizationDetails, forKey: .type)
try details.encode(to: container.superEncoder(forKey: .details))
}
}
}

// MARK: - MsoMdocAuthorizationDetails

public struct MsoMdocAuthorizationDetails: Codable {
public let doctype: String

public init(doctype: String) {
self.doctype = doctype
}
}

// MARK: - SdJwtVcAuthorizationDetails

public struct SdJwtVcAuthorizationDetails: Codable {
public let vct: String

public init(vct: String) {
self.vct = vct
}
}
88 changes: 35 additions & 53 deletions Sources/Entities/CredentialIssuer/CredentialIssuerMetadata.swift
Original file line number Diff line number Diff line change
@@ -17,16 +17,17 @@ import Foundation
import SwiftyJSON
import JOSESwift

public struct CredentialIssuerMetadata: Codable, Equatable {
public struct CredentialIssuerMetadata: Decodable, Equatable {
public let credentialIssuerIdentifier: CredentialIssuerId
public let authorizationServers: [URL]
public let credentialEndpoint: CredentialIssuerEndpoint
public let batchCredentialEndpoint: CredentialIssuerEndpoint?
public let deferredCredentialEndpoint: CredentialIssuerEndpoint?
public let notificationEndpoint: CredentialIssuerEndpoint?
public let credentialResponseEncryption: CredentialResponseEncryption
public let requireCredentialResponseEncryption: Bool
public let credentialsSupported: [CredentialIdentifier: SupportedCredential]

public let credentialsSupported: [CredentialConfigurationIdentifier: CredentialSupported]
public let credentialIdentifiersSupported: Bool?
public let signedMetadata: String?
public let display: [Display]

public enum CodingKeys: String, CodingKey {
@@ -35,11 +36,15 @@ public struct CredentialIssuerMetadata: Codable, Equatable {
case credentialEndpoint = "credential_endpoint"
case batchCredentialEndpoint = "batch_credential_endpoint"
case deferredCredentialEndpoint = "deferred_credential_endpoint"
case notificationEndpoint = "notification_endpoint"
case credentialResponseEncryptionAlgorithmsSupported = "credential_response_encryption_alg_values_supported"
case credentialResponseEncryptionMethodsSupported = "credential_response_encryption_enc_values_supported"
case requireCredentialResponseEncryption = "require_credential_response_encryption"
case credentialsSupported = "credentials_supported"
case credentialConfigurationsSupported = "credential_configurations_supported"
case display = "display"
case credentialResponseEncryption = "credential_response_encryption"
case signedMetadata = "signed_metadata"
case credentialIdentifiersSupported = "credential_identifiers_supported"
}

public init(
@@ -48,20 +53,28 @@ public struct CredentialIssuerMetadata: Codable, Equatable {
credentialEndpoint: CredentialIssuerEndpoint,
batchCredentialEndpoint: CredentialIssuerEndpoint?,
deferredCredentialEndpoint: CredentialIssuerEndpoint?,
notificationEndpoint: CredentialIssuerEndpoint?,
credentialResponseEncryption: CredentialResponseEncryption = .notRequired,
requireCredentialResponseEncryption: Bool?,
credentialsSupported: [CredentialIdentifier: SupportedCredential],
display: [Display]?
credentialConfigurationsSupported: [CredentialConfigurationIdentifier: CredentialSupported],
signedMetadata: String?,
display: [Display]?,
credentialIdentifiersSupported: Bool? = nil
) {
self.credentialIssuerIdentifier = credentialIssuerIdentifier
self.authorizationServers = authorizationServers

self.credentialEndpoint = credentialEndpoint
self.batchCredentialEndpoint = batchCredentialEndpoint
self.deferredCredentialEndpoint = deferredCredentialEndpoint
self.notificationEndpoint = notificationEndpoint

self.credentialResponseEncryption = credentialResponseEncryption
self.requireCredentialResponseEncryption = requireCredentialResponseEncryption ?? false
self.credentialsSupported = credentialsSupported
self.credentialsSupported = credentialConfigurationsSupported

self.signedMetadata = signedMetadata
self.display = display ?? []

self.credentialIdentifiersSupported = credentialIdentifiersSupported
}

// Implement a custom init(from decoder:) method to handle decoding.
@@ -77,45 +90,36 @@ public struct CredentialIssuerMetadata: Codable, Equatable {
credentialEndpoint = try container.decode(CredentialIssuerEndpoint.self, forKey: .credentialEndpoint)
batchCredentialEndpoint = try container.decodeIfPresent(CredentialIssuerEndpoint.self, forKey: .batchCredentialEndpoint)
deferredCredentialEndpoint = try container.decodeIfPresent(CredentialIssuerEndpoint.self, forKey: .deferredCredentialEndpoint)
notificationEndpoint = try container.decodeIfPresent(CredentialIssuerEndpoint.self, forKey: .notificationEndpoint)

if let credentialResponseEncryptionAlgorithmsSupported = try? container.decodeIfPresent([JWEAlgorithm].self, forKey: .credentialResponseEncryptionAlgorithmsSupported),
let credentialResponseEncryptionMethodsSupported = try? container.decodeIfPresent([JOSEEncryptionMethod].self, forKey: .credentialResponseEncryptionMethodsSupported) {
credentialResponseEncryption = .required(
algorithmsSupported: credentialResponseEncryptionAlgorithmsSupported,
encryptionMethodsSupported: credentialResponseEncryptionMethodsSupported
)
} else {
credentialResponseEncryption = .notRequired
}
credentialResponseEncryption = try container.decode(CredentialResponseEncryption.self, forKey: .credentialResponseEncryption)

requireCredentialResponseEncryption = try container.decodeIfPresent(Bool.self, forKey: .requireCredentialResponseEncryption) ?? false

let json = try container.decodeIfPresent(JSON.self, forKey: .credentialsSupported) ?? []
var mapIdentifierCredential: [CredentialIdentifier: SupportedCredential] = [:]
let json = try container.decodeIfPresent(JSON.self, forKey: .credentialConfigurationsSupported) ?? []
var mapIdentifierCredential: [CredentialConfigurationIdentifier: CredentialSupported] = [:]
for (key, value): (String, JSON) in json {
if let dictionary = value.dictionary,
let credJson = JSON(rawValue: dictionary) {

let credentialIdentifier: CredentialIdentifier = try .init(value: key)
let credentialIdentifier: CredentialConfigurationIdentifier = try .init(value: key)
guard let format = credJson["format"].string else {
throw ValidationError.error(reason: "Profile format not found")
}

switch format {
case MsoMdocFormat.FORMAT:
let profile = try MsoMdocFormat.CredentialSupported(json: credJson)
let profile = try MsoMdocFormat.CredentialConfiguration(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .msoMdoc(profile)
case W3CSignedJwtFormat.FORMAT:
let profile = try W3CSignedJwtFormat.CredentialSupported(json: credJson)
let profile = try W3CSignedJwtFormat.CredentialConfiguration(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .w3CSignedJwt(profile)
case SdJwtVcFormat.FORMAT:
let profile = try SdJwtVcFormat.CredentialSupported(json: credJson)
let profile = try SdJwtVcFormat.CredentialConfiguration(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .sdJwtVc(profile)
case W3CJsonLdSignedJwtFormat.FORMAT:
let profile = try W3CJsonLdSignedJwtFormat.CredentialSupported(json: credJson)
let profile = try W3CJsonLdSignedJwtFormat.CredentialConfiguration(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .w3CJsonLdSignedJwt(profile)
case W3CJsonLdDataIntegrityFormat.FORMAT:
let profile = try W3CJsonLdDataIntegrityFormat.CredentialSupported(json: credJson)
let profile = try W3CJsonLdDataIntegrityFormat.CredentialConfiguration(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .w3CJsonLdDataIntegrity(profile)
default: throw ValidationError.error(reason: "Unknow credential format")
}
@@ -125,32 +129,10 @@ public struct CredentialIssuerMetadata: Codable, Equatable {
credentialsSupported = mapIdentifierCredential

display = try container.decodeIfPresent([Display].self, forKey: .display) ?? []
}

// Implement an encode(to:) method to handle encoding.
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

// Encode each property as necessary, handling optionals and conversions.
try container.encode(credentialIssuerIdentifier, forKey: .credentialIssuerIdentifier)
try container.encode(authorizationServers, forKey: .authorizationServers)
try container.encode(credentialEndpoint, forKey: .credentialEndpoint)
try container.encode(batchCredentialEndpoint, forKey: .batchCredentialEndpoint)
try container.encode(deferredCredentialEndpoint, forKey: .deferredCredentialEndpoint)

switch credentialResponseEncryption {
case .notRequired: break
case .required(
let algorithmsSupported,
let encryptionMethodsSupported
):
try container.encode(algorithmsSupported, forKey: .credentialResponseEncryptionAlgorithmsSupported)
try container.encode(encryptionMethodsSupported, forKey: .credentialResponseEncryptionMethodsSupported)
}
signedMetadata = try? container.decodeIfPresent(String.self, forKey: .signedMetadata)

try container.encode(requireCredentialResponseEncryption, forKey: .requireCredentialResponseEncryption)
try container.encode(credentialsSupported, forKey: .credentialsSupported)
try container.encode(display, forKey: .display)
credentialIdentifiersSupported = try? container.decodeIfPresent(Bool.self, forKey: .credentialIdentifiersSupported) ?? false
}

public static func == (lhs: CredentialIssuerMetadata, rhs: CredentialIssuerMetadata) -> Bool {
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ import Foundation

public enum CryptographicBindingMethod: Codable, Equatable {
case jwk
case x5c
case cose
case mso
case did(method: String)
@@ -32,6 +33,8 @@ public enum CryptographicBindingMethod: Codable, Equatable {
switch stringValue {
case "jwk":
self = .jwk
case "x5c":
self = .x5c
case "cose":
self = .cose
case "mso":
@@ -50,6 +53,8 @@ public enum CryptographicBindingMethod: Codable, Equatable {
switch self {
case .jwk:
try container.encode("jwk")
case .x5c:
try container.encode("x5c")
case .cose:
try container.encode("cose")
case .mso:
@@ -63,6 +68,8 @@ public enum CryptographicBindingMethod: Codable, Equatable {
switch method {
case "jwk":
self = .jwk
case "x5c":
self = .x5c
case "cose":
self = .cose
case "mso":
Loading
Oops, something went wrong.

0 comments on commit 60ef9cd

Please sign in to comment.