Skip to content

Commit d6dfe56

Browse files
[spec] OID4VCI and SD-JWT VC spec changes
- The `user_pin_required` boolean property in a credential offer has been replaced with the `tx_code` JSON object. - The `credentials` property in a credential offer has been renamed to `credential_configurations`. - The structure of the supported credential for SD-JWT VC has been changed. - The structure of the credential request for SD-JWT VC has been changed.
1 parent ce47ac9 commit d6dfe56

File tree

5 files changed

+224
-140
lines changed

5 files changed

+224
-140
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<properties>
1313
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1414

15-
<authlete.java.common.version>3.88</authlete.java.common.version>
15+
<authlete.java.common.version>3.91</authlete.java.common.version>
1616
<authlete.java.jaxrs.version>2.70</authlete.java.jaxrs.version>
1717
<authlete.cbor.version>1.14</authlete.cbor.version>
1818
<javax.servlet-api.version>3.0.1</javax.servlet-api.version>

src/main/java/com/authlete/jaxrs/server/api/vci/CredentialOfferPageModel.java

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
*/
4848
public class CredentialOfferPageModel extends AuthorizationPageModel
4949
{
50-
private static final long serialVersionUID = 1L;
50+
private static final long serialVersionUID = 2L;
5151

5252

5353
private static final String DEFAULT_ENDPOINT = "openid-credential-offer://";
@@ -57,55 +57,64 @@ public class CredentialOfferPageModel extends AuthorizationPageModel
5757
public static final int QR_CODE_HEIGHT = 300;
5858

5959

60-
private static final String DEFAULT_CREDENTIALS = "[\n" +
60+
private static final String DEFAULT_CREDENTIAL_CONFIGURATIONS =
61+
"[\n" +
6162
" \"IdentityCredential\",\n" +
62-
" \"mDL\"\n" +
63+
" \"org.iso.18013.5.1.mDL\"\n" +
6364
"]";
6465

6566

66-
private String credentials;
67+
private String credentialConfigurations;
6768
private boolean authorizationCodeGrantIncluded;
6869
private boolean issuerStateIncluded;
6970
private boolean preAuthorizedCodeGrantIncluded;
70-
private boolean userPinRequired;
71-
private int userPinLength;
71+
private String txCode;
72+
private String txCodeInputMode;
73+
private String txCodeDescription;
7274
private int duration;
7375
private String credentialOfferEndpoint;
7476
private CredentialOfferInfo info;
7577
private String credentialOfferLink;
7678
private String credentialOfferQrCode;
7779
private String credentialOfferContent;
80+
private String credentialOfferUri;
7881
private String credentialOfferUriLink;
7982
private String credentialOfferUriQrCode;
8083

8184

8285
public CredentialOfferPageModel()
8386
{
8487
this.authorizationCodeGrantIncluded = false;
85-
this.issuerStateIncluded = true;
88+
this.issuerStateIncluded = true;
8689
this.preAuthorizedCodeGrantIncluded = true;
87-
this.userPinRequired = false;
88-
this.userPinLength = 0;
8990
this.duration = 0;
90-
this.credentials = DEFAULT_CREDENTIALS;
91-
this.credentialOfferEndpoint = DEFAULT_ENDPOINT;
91+
this.credentialConfigurations = DEFAULT_CREDENTIAL_CONFIGURATIONS;
92+
this.credentialOfferEndpoint = DEFAULT_ENDPOINT;
9293
}
9394

9495

9596
public CredentialOfferPageModel setValues(final Map<String, String> values)
9697
{
97-
this.credentials = values.getOrDefault("credentials", this.credentials);
98-
this.authorizationCodeGrantIncluded = ProcessingUtil.fromFormCheckbox(values, "authorizationCodeGrantIncluded");
99-
this.issuerStateIncluded = ProcessingUtil.fromFormCheckbox(values, "issuerStateIncluded");
100-
this.preAuthorizedCodeGrantIncluded = ProcessingUtil.fromFormCheckbox(values, "preAuthorizedCodeGrantIncluded");
101-
this.userPinRequired = ProcessingUtil.fromFormCheckbox(values, "userPinRequired");
102-
this.credentialOfferEndpoint = values.getOrDefault("credentialOfferEndpoint", this.credentialOfferEndpoint);
103-
this.userPinLength = extractInt(values, "userPinLength", this.userPinLength);
104-
this.duration = extractInt(values, "duration", this.duration);
98+
this.credentialConfigurations = values.getOrDefault("credentialConfigurations", this.credentialConfigurations);
99+
this.authorizationCodeGrantIncluded = fromCheckBox(values, "authorizationCodeGrantIncluded");
100+
this.issuerStateIncluded = fromCheckBox(values, "issuerStateIncluded");
101+
this.preAuthorizedCodeGrantIncluded = fromCheckBox(values, "preAuthorizedCodeGrantIncluded");
102+
this.txCode = values.getOrDefault("txCode", this.txCode);
103+
this.txCodeInputMode = values.getOrDefault("txCodeInputMode", this.txCodeInputMode);
104+
this.txCodeDescription = values.getOrDefault("txCodeDescription", this.txCodeDescription);
105+
this.duration = extractInt(values, "duration", this.duration);
106+
this.credentialOfferEndpoint = values.getOrDefault("credentialOfferEndpoint", this.credentialOfferEndpoint);
107+
105108
return this;
106109
}
107110

108111

112+
private static boolean fromCheckBox(Map<String, String> values, String key)
113+
{
114+
return ProcessingUtil.fromFormCheckbox(values, key);
115+
}
116+
117+
109118
private Integer extractInt(final Map<String, String> values,
110119
final String key, final Integer def)
111120
{
@@ -159,10 +168,12 @@ public CredentialOfferCreateRequest toRequest(final User user)
159168
.setAuthorizationCodeGrantIncluded(this.authorizationCodeGrantIncluded)
160169
.setIssuerStateIncluded(this.issuerStateIncluded)
161170
.setPreAuthorizedCodeGrantIncluded(this.preAuthorizedCodeGrantIncluded)
162-
.setUserPinRequired(this.userPinRequired)
163-
.setUserPinLength(this.userPinLength)
171+
.setTxCode(this.txCode)
172+
.setTxCodeInputMode(this.txCodeInputMode)
173+
.setTxCodeDescription(this.txCodeDescription)
164174
.setDuration(this.duration)
165-
.setCredentials(parseAsStringArray("credentials", this.credentials))
175+
.setCredentialConfigurations(
176+
parseAsStringArray("credentialConfigurations", this.credentialConfigurations))
166177
.setSubject(user.getSubject());
167178
}
168179

@@ -209,15 +220,15 @@ private List<?> parseAsList(String name, String json)
209220
}
210221

211222

212-
public String getCredentials()
223+
public String getCredentialConfigurations()
213224
{
214-
return credentials;
225+
return credentialConfigurations;
215226
}
216227

217228

218-
public void setCredentials(String credentials)
229+
public void setCredentialConfigurations(String configurations)
219230
{
220-
this.credentials = credentials;
231+
this.credentialConfigurations = configurations;
221232
}
222233

223234

@@ -257,27 +268,39 @@ public void setPreAuthorizedCodeGrantIncluded(boolean preAuthorizedCodeGrantIncl
257268
}
258269

259270

260-
public boolean isUserPinRequired()
271+
public String getTxCode()
261272
{
262-
return userPinRequired;
273+
return txCode;
263274
}
264275

265276

266-
public void setUserPinRequired(boolean userPinRequired)
277+
public void setTxCode(String txCode)
267278
{
268-
this.userPinRequired = userPinRequired;
279+
this.txCode = txCode;
269280
}
270281

271282

272-
public int getUserPinLength()
283+
public String getTxCodeInputMode()
273284
{
274-
return userPinLength;
285+
return txCodeInputMode;
275286
}
276287

277288

278-
public void setUserPinLength(int userPinLength)
289+
public void setTxCodeInputMode(String inputMode)
279290
{
280-
this.userPinLength = userPinLength;
291+
this.txCodeInputMode = inputMode;
292+
}
293+
294+
295+
public String getTxCodeDescription()
296+
{
297+
return txCodeDescription;
298+
}
299+
300+
301+
public void setTxCodeDescription(String description)
302+
{
303+
this.txCodeDescription = description;
281304
}
282305

283306

@@ -321,7 +344,7 @@ public void setInfo(CredentialOfferInfo info)
321344
URLEncoder.encode(info.getCredentialOffer(), "UTF-8"));
322345
this.credentialOfferQrCode = asQrCode(this.credentialOfferLink);
323346

324-
final String credentialOfferUri = String.format("%s/api/offer/%s",
347+
this.credentialOfferUri = String.format("%s/api/offer/%s",
325348
info.getCredentialIssuer().toString(),
326349
info.getIdentifier());
327350
this.credentialOfferUriLink = String.format(CREDENTIAL_OFFER_URI_QR_PATTERN, credentialOfferEndpoint,
@@ -380,6 +403,18 @@ public void setCredentialOfferContent(String credentialOfferContent)
380403
}
381404

382405

406+
public String getCredentialOfferUri()
407+
{
408+
return credentialOfferUri;
409+
}
410+
411+
412+
public void setCredentialOfferUri(String credentialOfferUri)
413+
{
414+
this.credentialOfferUri = credentialOfferUri;
415+
}
416+
417+
383418
public String getCredentialOfferUriLink()
384419
{
385420
return credentialOfferUriLink;

src/main/java/com/authlete/jaxrs/server/vc/SdJwtOrderProcessor.java

Lines changed: 24 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,11 @@
2525

2626
public class SdJwtOrderProcessor extends AbstractOrderProcessor
2727
{
28-
private static final String KEY_CREDENTIAL_DEFINITION = "credential_definition";
29-
private static final String KEY_FORMAT = "format";
30-
private static final String KEY_SUB = "sub";
31-
private static final String KEY_VCT = "vct";
28+
private static final String KEY_FORMAT = "format";
29+
private static final String KEY_SUB = "sub";
30+
private static final String KEY_VCT = "vct";
3231

3332

34-
@SuppressWarnings("unchecked")
3533
@Override
3634
protected void checkPermissions(
3735
OrderContext context,
@@ -55,12 +53,8 @@ protected void checkPermissions(
5553
//
5654
// https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/
5755

58-
// The requested credential must contain "credential_definition".
59-
Map<String, Object> credentialDefinition =
60-
extractCredentialDefinition(requestedCredential);
61-
62-
// The "credential_definition" object must contain "vct".
63-
String vct = extractVct(credentialDefinition);
56+
// The requested credential must contain "vct".
57+
String vct = extractVct(requestedCredential);
6458

6559
// For each issuable credential.
6660
for (Map<String, Object> issuableCredential : issuableCredentials)
@@ -75,17 +69,8 @@ protected void checkPermissions(
7569
continue;
7670
}
7771

78-
// The "credential_definition" in the issuable credential.
79-
Object value = issuableCredential.get(KEY_CREDENTIAL_DEFINITION);
80-
81-
// If the "credential_definition" property is not available as a JSON object.
82-
if (!(value instanceof Map))
83-
{
84-
continue;
85-
}
86-
87-
// The "vct" in the "credential_definition" object of the issuable credential.
88-
value = ((Map<String, Object>)value).get(KEY_VCT);
72+
// The "vct" in the issuable credential.
73+
Object value = issuableCredential.get(KEY_VCT);
8974

9075
// If the "type" property is not available as a string.
9176
if (!(value instanceof String))
@@ -94,10 +79,9 @@ protected void checkPermissions(
9479
}
9580

9681
// This implementation of the checkPermissions method is simple.
97-
// If "credential_definition.vct" of the requested credential
98-
// matches "credential_definition.vct" of any of the issuable
99-
// credentials, it is regarded that the credential request is
100-
// permitted.
82+
// If "vct" of the requested credential matches "vct" of any of
83+
// the issuable credentials, it is regarded that the credential
84+
// request is permitted.
10185
if (vct.equals(value))
10286
{
10387
// The credential request is permitted.
@@ -110,49 +94,24 @@ protected void checkPermissions(
11094
}
11195

11296

113-
@SuppressWarnings("unchecked")
114-
private Map<String, Object> extractCredentialDefinition(
115-
Map<String, Object> requestedCredential) throws InvalidCredentialRequestException
116-
{
117-
// If the requested credential does not contain "credential_definition".
118-
if (!requestedCredential.containsKey(KEY_CREDENTIAL_DEFINITION))
119-
{
120-
throw new InvalidCredentialRequestException(
121-
"The credential request does not contain 'credential_definition'.");
122-
}
123-
124-
// The value of the "credential_definition" property.
125-
Object value = requestedCredential.get(KEY_CREDENTIAL_DEFINITION);
126-
127-
// If the value of the "credential_definition" property is not a JSON object.
128-
if (!(value instanceof Map))
129-
{
130-
throw new InvalidCredentialRequestException(
131-
"The value of the 'credential_definition' property in the credential request is not a JSON object.");
132-
}
133-
134-
return (Map<String, Object>)value;
135-
}
136-
137-
13897
private String extractVct(
139-
Map<String, Object> credentialDefinition) throws InvalidCredentialRequestException
98+
Map<String, Object> requestedCredential) throws InvalidCredentialRequestException
14099
{
141-
// If the "credential_definition" does not contain "vct".
142-
if (!credentialDefinition.containsKey(KEY_VCT))
100+
// If the requested credential does not contain "vct".
101+
if (!requestedCredential.containsKey(KEY_VCT))
143102
{
144103
throw new InvalidCredentialRequestException(
145-
"The 'credential_definition' object does not contain 'vct'.");
104+
"The credential request does not contain 'vct'.");
146105
}
147106

148107
// The value of the "vct" property.
149-
Object value = credentialDefinition.get(KEY_VCT);
108+
Object value = requestedCredential.get(KEY_VCT);
150109

151110
// If the value of the "vct" property is not a string.
152111
if (!(value instanceof String))
153112
{
154113
throw new InvalidCredentialRequestException(
155-
"The value of the 'vct' property in the 'credential_definition' object is not a string.");
114+
"The value of the 'vct' property in the credential request is not a string.");
156115
}
157116

158117
return (String)value;
@@ -164,18 +123,17 @@ protected Map<String, Object> collectClaims(
164123
OrderContext context, User user, String format,
165124
Map<String, Object> requestedCredential) throws VerifiableCredentialException
166125
{
167-
// The "credential_definition.vct" in the requested credential.
168-
@SuppressWarnings("unchecked")
169-
String vct = (String)((Map<String, Object>)requestedCredential.get(KEY_CREDENTIAL_DEFINITION)).get(KEY_VCT);
126+
// The "vct" in the requested credential.
127+
String vctId = (String)requestedCredential.get(KEY_VCT);
170128

171-
// Find a CredentialDefinitionType having the vct.
172-
CredentialDefinitionType cdType = CredentialDefinitionType.byId(vct);
129+
// Find a VerifiableCredentialType corresponding to the vct.
130+
VerifiableCredentialType vct = VerifiableCredentialType.byId(vctId);
173131

174-
if (cdType == null)
132+
if (vct == null)
175133
{
176134
// The credential type is not supported.
177135
throw new UnsupportedCredentialTypeException(String.format(
178-
"The credential type '%s' is not supported.", vct));
136+
"The credential type '%s' is not supported.", vctId));
179137
}
180138

181139
// For testing purposes, the credential issuance for a certain user
@@ -191,14 +149,14 @@ protected Map<String, Object> collectClaims(
191149
Map<String, Object> claims = new LinkedHashMap<>();
192150

193151
// "vct"
194-
claims.put(KEY_VCT, vct);
152+
claims.put(KEY_VCT, vctId);
195153

196154
// "sub"
197155
claims.put(KEY_SUB, user.getSubject());
198156

199157
// The CredentialDefinitionType has a set of claims.
200158
// For each claim in the set.
201-
for (String claimName : cdType.getClaims())
159+
for (String claimName : vct.getClaims())
202160
{
203161
// The value of the claim.
204162
Object claimValue = user.getClaim(claimName, null);

0 commit comments

Comments
 (0)