Skip to content

Commit a255a4a

Browse files
authored
Merge pull request #66 from FIWARE/dcql
Support DCQL
2 parents f29f44d + 9962cd4 commit a255a4a

File tree

8 files changed

+262
-95
lines changed

8 files changed

+262
-95
lines changed

config/configClient.go

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ type ScopeEntry struct {
4949
// credential types with their trust configuration
5050
Credentials []Credential `json:"credentials" mapstructure:"credentials"`
5151
// Proofs to be requested - see https://identity.foundation/presentation-exchange/#presentation-definition
52-
PresentationDefinition PresentationDefinition `json:"presentationDefinition" mapstructure:"presentationDefinition"`
52+
PresentationDefinition *PresentationDefinition `json:"presentationDefinition" mapstructure:"presentationDefinition"`
53+
// JSON encoded query to request the credentials to be included in the presentation
54+
DCQL *DCQL `json:"dcql,omitempty" mapstructure:"dcql,omitempty"`
5355
// When set, the claim are flatten to plain JWT-claims before beeing included, instead of keeping the credential/presentation structure, where the claims are under the key vc or vp
5456
FlatClaims bool `json:"flatClaims" mapstructure:"flatClaims"`
5557
}
@@ -138,6 +140,78 @@ type Fields struct {
138140
Filter interface{} `json:"filter" mapstructure:"filter"`
139141
}
140142

143+
// DCQL defines a JSON encoded query to request the credentials to be included in the presentation
144+
type DCQL struct {
145+
// A non-empty array of Credential Queries that specify the requested Credentials.
146+
Credentials []CredentialQuery `json:"credentials" mapstructure:"credentials"`
147+
// A non-empty array of Credential Set Queries that specifies additional constraints on which of the requested Credentials to return.
148+
CredentialSets []CredentialSetQuery `json:"credential_sets,omitempty" mapstructure:"credential_sets,omitempty"`
149+
}
150+
151+
// CredentialQuery is an object representing a request for a presentation of one or more matching Credentials
152+
type CredentialQuery struct {
153+
// A string identifying the Credential in the response and, if provided, the constraints in credential_sets. The value MUST be a non-empty string consisting of alphanumeric, underscore (_), or hyphen (-) characters. Within the Authorization Request, the same id MUST NOT be present more than once.
154+
Id string `json:"id,omitempty" mapstructure:"id,omitempty"`
155+
// A string that specifies the format of the requested Credential.
156+
Format string `json:"format,omitempty" mapstructure:"format,omitempty"`
157+
// A boolean which indicates whether multiple Credentials can be returned for this Credential Query. If omitted, the default value is false.
158+
Multiple bool `json:"multiple,omitempty" mapstructure:"multiple,omitempty"`
159+
// A non-empty array of objects that specifies claims in the requested Credential. Verifiers MUST NOT point to the same claim more than once in a single query. Wallets SHOULD ignore such duplicate claim queries.
160+
Claims []ClaimsQuery `json:"claims,omitempty" mapstructure:"claims,omitempty"`
161+
// Defines additional properties requested by the Verifier that apply to the metadata and validity data of the Credential. The properties of this object are defined per Credential Format. If empty, no specific constraints are placed on the metadata or validity of the requested Credential.
162+
Meta *MetaDataQuery `json:"meta,omitempty" mapstructure:"meta,omitempty"`
163+
// A boolean which indicates whether the Verifier requires a Cryptographic Holder Binding proof. The default value is true, i.e., a Verifiable Presentation with Cryptographic Holder Binding is required. If set to false, the Verifier accepts a Credential without Cryptographic Holder Binding proof.
164+
RequireCryptographicHolderBinding bool `json:"require_cryptographic_holder_binding,omitempty" mapstructure:"require_cryptographic_holder_binding,omitempty"`
165+
// A non-empty array containing arrays of identifiers for elements in claims that specifies which combinations of claims for the Credential are requested.
166+
ClaimSets [][]string `json:"claim_sets,omitempty" mapstructure:"claim_sets,omitempty"`
167+
// A non-empty array of objects that specifies expected authorities or trust frameworks that certify Issuers, that the Verifier will accept. Every Credential returned by the Wallet SHOULD match at least one of the conditions present in the corresponding trusted_authorities array if present.
168+
TrustedAuthorities []TrustedAuthorityQuery `json:"trusted_authorities,omitempty" mapstructure:"trusted_authorities,omitempty"`
169+
}
170+
171+
// ClaimsQuery is a query to specifies claims in the requested Credential.
172+
type ClaimsQuery struct {
173+
// REQUIRED if claim_sets is present in the Credential Query; OPTIONAL otherwise. A string identifying the particular claim. The value MUST be a non-empty string consisting of alphanumeric, underscore (_), or hyphen (-) characters. Within the particular claims array, the same id MUST NOT be present more than once.
174+
Id string `json:"id,omitempty" mapstructure:"id,omitempty"`
175+
// The value MUST be a non-empty array representing a claims path pointer that specifies the path to a claim within the Credential. See https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-claims-path-pointer
176+
Path []interface{} `json:"path,omitempty" mapstructure:"path,omitempty"`
177+
// A non-empty array of strings, integers or boolean values that specifies the expected values of the claim. If the values property is present, the Wallet SHOULD return the claim only if the type and value of the claim both match exactly for at least one of the elements in the array.
178+
Values []interface{} `json:"values,omitempty" mapstructure:"values,omitempty"`
179+
// MDoc specific parameter, ignored for all other types. The flag can be set to inform that the reader wishes to keep(store) the data. In case of false, its data is only used to be dispalyed and verified.
180+
IntentToRetain bool `json:"intent_to_retain,omitempty" mapstructure:"intent_to_retain,omitempty"`
181+
// MDoc specific parameter, ignored for all other types. Refers to a namespace inside an mdoc.
182+
Namespace string `json:"namespace,omitempty" mapstructure:"namespace,omitempty"`
183+
// MDoc specific parameter, ignored for all other types. Identifier for the data-element in the namespace.
184+
ClaimName string `json:"claim_name,omitempty" mapstructure:"claim_name,omitempty"`
185+
}
186+
187+
// MetaDataQuery defines additional properties requested by the Verifier that apply to the metadata and validity data of the Credential.
188+
type MetaDataQuery struct {
189+
// SD-JWT and JWT specific parameter. A non-empty array of strings that specifies allowed values for the type of the requested Verifiable Credential.The Wallet MAY return Credentials that inherit from any of the specified types, following the inheritance logic defined in https://datatracker.ietf.org/doc/html/draft-ietf-oauth-sd-jwt-vc-10
190+
VctValues []string `json:"vct_values,omitempty" mapstructure:"vct_values,omitempty"`
191+
// Required for MDoc. String that specifies an allowed value for the doctype of the requested Verifiable Credential. It MUST be a valid doctype identifier as defined in https://www.iso.org/standard/69084.html
192+
DoctypeValue string `json:"doctype_value,omitempty" mapstructure:"doctype_value,omitempty"`
193+
// Required for ldp_vc. A non-empty array of string arrays. The Type value of the credential needs to be a subset of at least one of the string-arrays.
194+
TypeValues [][]string `json:"type_values,omitempty" mapstructure:"type_values,omitempty"`
195+
}
196+
197+
// TrustedAuthorityQuery is an object representing information that helps to identify an authority or the trust framework that certifies Issuers.
198+
type TrustedAuthorityQuery struct {
199+
// A string uniquely identifying the type of information about the issuer trust framework.
200+
Type string `json:"type" mapstructure:"type"`
201+
// A non-empty array of strings, where each string (value) contains information specific to the used Trusted Authorities Query type that allows the identification of an issuer, a trust framework, or a federation that an issuer belongs to.
202+
Values []string `json:"values" mapstructure:"values"`
203+
}
204+
205+
// CredentialSetQuery is a Credential Set Query is an object representing a request for one or more Credentials to satisfy a particular use case with the Verifier.
206+
type CredentialSetQuery struct {
207+
// A non-empty array, where each value in the array is a list of Credential Query identifiers representing one set of Credentials that satisfies the use case. The value of each element in the options array is a non-empty array of identifiers which reference elements in credentials.
208+
Options [][]string `json:"options,omitempty" mapstructure:"options,omitempty"`
209+
// A boolean which indicates whether this set of Credentials is required to satisfy the particular use case at the Verifier.
210+
Required bool `json:"required,omitempty" mapstructure:"required,omitempty"`
211+
// A string, number or object specifying the purpose of the query. This specification does not define a specific structure or specific values for this property. The purpose is intended to be used by the Verifier to communicate the reason for the query to the Wallet. The Wallet MAY use this information to show the user the reason for the request.
212+
Purpose interface{} `json:"purpose,omitempty" mapstructure:"purpose,omitempty"`
213+
}
214+
141215
func (cs ConfiguredService) GetRequiredCredentialTypes(scope string) []string {
142216
types := []string{}
143217
for _, credential := range cs.GetCredentials(scope) {
@@ -157,10 +231,14 @@ func (cs ConfiguredService) GetCredentials(scope string) []Credential {
157231
return cs.GetScope(scope).Credentials
158232
}
159233

160-
func (cs ConfiguredService) GetPresentationDefinition(scope string) PresentationDefinition {
234+
func (cs ConfiguredService) GetPresentationDefinition(scope string) *PresentationDefinition {
161235
return cs.GetScope(scope).PresentationDefinition
162236
}
163237

238+
func (cs ConfiguredService) GetDcqlQuery(scope string) *DCQL {
239+
return cs.GetScope(scope).DCQL
240+
}
241+
164242
func (cs ConfiguredService) GetCredential(scope, credentialType string) (Credential, bool) {
165243
credentials := cs.GetCredentials(scope)
166244
for _, credential := range credentials {

config/configClient_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func Test_getServices(t *testing.T) {
4949
HolderVerification: HolderVerification{Enabled: false, Claim: "subject"},
5050
},
5151
},
52-
PresentationDefinition: PresentationDefinition{
52+
PresentationDefinition: &PresentationDefinition{
5353
Id: "my-pd",
5454
InputDescriptors: []InputDescriptor{
5555
{
@@ -65,6 +65,26 @@ func Test_getServices(t *testing.T) {
6565
},
6666
},
6767
},
68+
DCQL: &DCQL{
69+
Credentials: []CredentialQuery{
70+
{
71+
Id: "my-credential-query-id",
72+
Format: "jwt_vc_json",
73+
Claims: []ClaimsQuery{
74+
{
75+
Path: []interface{}{"$.vc.credentialSubject.familyName"},
76+
IntentToRetain: true,
77+
},
78+
},
79+
},
80+
},
81+
CredentialSets: []CredentialSetQuery{
82+
{
83+
Options: [][]string{{"my-credential-query-id"}},
84+
Purpose: "Please provide your family name.",
85+
},
86+
},
87+
},
6888
},
6989
},
7090
},

config/data/ccs_full.json

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,71 @@
11
{
22
"total": 1,
33
"pageNumber": 0,
4-
"pageSize": 10,
4+
"pageSize": 100,
55
"services": [
6-
{
7-
"id": "service_all",
8-
"defaultOidcScope": "did_write",
9-
"oidcScopes": {
10-
"did_write": {
11-
"credentials": [
12-
{
13-
"type": "VerifiableCredential",
14-
"trustedParticipantsLists": [
15-
{
16-
"type": "ebsi",
17-
"url": "https://tir-pdc.ebsi.fiware.dev"
18-
}
19-
],
20-
"trustedIssuersLists": [
21-
"https://til-pdc.ebsi.fiware.dev"
22-
],
23-
"holderVerification": {
24-
"enabled": false,
25-
"claim": "subject"
26-
}
27-
}
28-
],
29-
"presentationDefinition": {
30-
"id": "my-pd",
31-
"input_descriptors": [
32-
{
33-
"id": "my-descriptor",
34-
"constraints": {
35-
"fields": [
36-
{
37-
"id": "my-field",
38-
"path": [
39-
"$.vc.my.claim"
6+
{
7+
"id": "service_all",
8+
"defaultOidcScope": "did_write",
9+
"oidcScopes": {
10+
"did_write": {
11+
"credentials": [
12+
{
13+
"type": "VerifiableCredential",
14+
"trustedParticipantsLists": [
15+
{
16+
"type": "ebsi",
17+
"url": "https://tir-pdc.ebsi.fiware.dev"
18+
}
19+
],
20+
"trustedIssuersLists": [
21+
"https://til-pdc.ebsi.fiware.dev"
22+
],
23+
"holderVerification": {
24+
"enabled": false,
25+
"claim": "subject"
26+
}
27+
}
28+
],
29+
"presentationDefinition": {
30+
"id": "my-pd",
31+
"input_descriptors": [
32+
{
33+
"id": "my-descriptor",
34+
"constraints": {
35+
"fields": [
36+
{
37+
"id": "my-field",
38+
"path": [
39+
"$.vc.my.claim"
40+
]
41+
}
42+
]
43+
}
44+
}
45+
]
46+
},
47+
"dcql": {
48+
"credentials": [
49+
{
50+
"id": "my-credential-query-id",
51+
"format": "jwt_vc_json",
52+
"claims": [
53+
{
54+
"path": ["$.vc.credentialSubject.familyName"],
55+
"intent_to_retain": true
56+
}
57+
]
58+
}
59+
],
60+
"credential_sets": [
61+
{
62+
"options": [["my-credential-query-id"]],
63+
"purpose": "Please provide your family name."
64+
}
4065
]
41-
}
42-
]
43-
}
66+
}
4467
}
45-
]
4668
}
47-
}
4869
}
49-
}
5070
]
51-
}
71+
}

config/provider_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func Test_ReadConfig(t *testing.T) {
7373
TrustedIssuersLists: []string{"https://til-pdc.ebsi.fiware.dev"},
7474
},
7575
},
76-
PresentationDefinition: PresentationDefinition{
76+
PresentationDefinition: &PresentationDefinition{
7777
Id: "my-pd",
7878
InputDescriptors: []InputDescriptor{
7979
{

openapi/api_api.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ func StartSIOPSameDevice(c *gin.Context) {
291291
}
292292

293293
clientId, clientIdExists := c.GetQuery("client_id")
294+
logging.Log().Debugf("The client id %s", clientId)
294295
if !clientIdExists {
295296
logging.Log().Infof("Start a login flow for a not specified client.")
296297
}
@@ -379,10 +380,24 @@ func GetRequestByReference(c *gin.Context) {
379380

380381
func extractVpFromToken(c *gin.Context, vpToken string) (parsedPresentation *verifiable.Presentation, err error) {
381382

382-
tokenBytes := decodeVpString(vpToken)
383-
384383
logging.Log().Debugf("The token %s.", vpToken)
385384

385+
parsedPresentation, err = getPresentationFromQuery(c, vpToken)
386+
387+
if err != nil {
388+
logging.Log().Debugf("Received a vpToken with a query, but was not able to extract the presentation. Token: %s", vpToken)
389+
return parsedPresentation, err
390+
}
391+
if parsedPresentation != nil {
392+
return parsedPresentation, err
393+
}
394+
return tokenToPresentation(c, vpToken)
395+
396+
}
397+
398+
func tokenToPresentation(c *gin.Context, vpToken string) (parsedPresentation *verifiable.Presentation, err error) {
399+
tokenBytes := decodeVpString(vpToken)
400+
386401
isSdJWT, parsedPresentation, err := isSdJWT(c, vpToken)
387402
if isSdJWT && err != nil {
388403
return
@@ -402,6 +417,31 @@ func extractVpFromToken(c *gin.Context, vpToken string) (parsedPresentation *ver
402417
return
403418
}
404419

420+
func getPresentationFromQuery(c *gin.Context, vpToken string) (parsedPresentation *verifiable.Presentation, err error) {
421+
tokenBytes := decodeVpString(vpToken)
422+
423+
var queryMap map[string]string
424+
//unmarshal
425+
err = json.Unmarshal(tokenBytes, &queryMap)
426+
if err != nil {
427+
logging.Log().Debug("VP Token does not contain query map. Checking the other options.", err)
428+
return nil, nil
429+
}
430+
431+
for _, v := range queryMap {
432+
p, err := tokenToPresentation(c, v)
433+
if err != nil {
434+
return nil, err
435+
}
436+
if parsedPresentation == nil {
437+
parsedPresentation = p
438+
} else {
439+
parsedPresentation.AddCredentials(p.Credentials()...)
440+
}
441+
}
442+
return parsedPresentation, err
443+
}
444+
405445
// checks if the presented token contains a single sd-jwt credential. Will be repackage to a presentation for further validation
406446
func isSdJWT(c *gin.Context, vpToken string) (isSdJwt bool, presentation *verifiable.Presentation, err error) {
407447
claims, err := getSdJwtParser().Parse(vpToken)

0 commit comments

Comments
 (0)