Merge pull request #69 from imsglc/feature/upload_uri_in_api
OB 3.0 / CLR 2.0 cert suite
This commit is contained in:
commit
5f9335155c
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.1edtech</groupId>
|
||||
<artifactId>inspector</artifactId>
|
||||
<version>0.9.10</version>
|
||||
<version>0.9.11</version>
|
||||
</parent>
|
||||
<artifactId>inspector-vc</artifactId>
|
||||
<dependencies>
|
||||
|
@ -148,6 +148,14 @@ public class Assertion extends Credential {
|
||||
public List<Validation> getValidations() {
|
||||
return validationMap.get(this);
|
||||
}
|
||||
@Override
|
||||
public Map<String, List<String>> getContextAliases() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
@Override
|
||||
public Map<String, List<String>> getContextVersionPatterns() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ValueType {
|
||||
|
@ -92,6 +92,8 @@ public abstract class Credential extends GeneratedObject {
|
||||
List<String> getAllowedTypeValues();
|
||||
boolean isAllowedTypeValuesRequired();
|
||||
List<String> getContextUris();
|
||||
Map<String, List<String>> getContextAliases();
|
||||
Map<String, List<String>> getContextVersionPatterns();
|
||||
String toString();
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@ public class EndorsementInspector extends VCInspector implements SubInspector {
|
||||
|
||||
//credentialSubject
|
||||
probeCount++;
|
||||
accumulator.add(new CredentialSubjectProbe().run(endorsement.getJson(), ctx));
|
||||
accumulator.add(new CredentialSubjectProbe("EndorsementSubject").run(endorsement.getJson(), ctx));
|
||||
|
||||
//signatures, proofs
|
||||
probeCount++;
|
||||
|
@ -6,6 +6,7 @@ import static org.oneedtech.inspect.core.report.ReportUtil.onProbeException;
|
||||
import static org.oneedtech.inspect.util.code.Defensives.*;
|
||||
import static org.oneedtech.inspect.util.json.ObjectMapperCache.Config.DEFAULT;
|
||||
import static org.oneedtech.inspect.vc.Credential.CREDENTIAL_KEY;
|
||||
import static org.oneedtech.inspect.vc.VerifiableCredential.REFRESH_SERVICE_MIME_TYPES;
|
||||
import static org.oneedtech.inspect.vc.VerifiableCredential.ProofType.EXTERNAL;
|
||||
import static org.oneedtech.inspect.vc.payload.PayloadParser.fromJwt;
|
||||
import static org.oneedtech.inspect.vc.util.JsonNodeUtil.asNodeList;
|
||||
@ -43,7 +44,9 @@ import org.oneedtech.inspect.vc.probe.CredentialSubjectProbe;
|
||||
import org.oneedtech.inspect.vc.probe.ExpirationProbe;
|
||||
import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe;
|
||||
import org.oneedtech.inspect.vc.probe.IssuanceProbe;
|
||||
import org.oneedtech.inspect.vc.probe.IssuerProbe;
|
||||
import org.oneedtech.inspect.vc.probe.EmbeddedProofProbe;
|
||||
import org.oneedtech.inspect.vc.probe.EvidenceProbe;
|
||||
import org.oneedtech.inspect.vc.probe.RevocationListProbe;
|
||||
import org.oneedtech.inspect.vc.probe.ExternalProofProbe;
|
||||
import org.oneedtech.inspect.vc.probe.TypePropertyProbe;
|
||||
@ -170,7 +173,17 @@ public class OB30Inspector extends VCInspector implements SubInspector {
|
||||
|
||||
//credentialSubject
|
||||
probeCount++;
|
||||
accumulator.add(new CredentialSubjectProbe().run(ob.getJson(), ctx));
|
||||
accumulator.add(new CredentialSubjectProbe("AchievementSubject", true).run(ob.getJson(), ctx));
|
||||
|
||||
// evidence
|
||||
probeCount++;
|
||||
accumulator.add(new EvidenceProbe().run(ob.getJson(), ctx));
|
||||
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
|
||||
|
||||
// issuer
|
||||
probeCount++;
|
||||
accumulator.add(new IssuerProbe().run(ob.getJson(), ctx));
|
||||
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
|
||||
|
||||
//signatures, proofs
|
||||
probeCount++;
|
||||
@ -188,9 +201,11 @@ public class OB30Inspector extends VCInspector implements SubInspector {
|
||||
if(resource.getContext().get(REFRESHED) != TRUE) {
|
||||
Optional<String> newID = checkRefreshService(ob, ctx);
|
||||
if(newID.isPresent()) {
|
||||
return this.run(
|
||||
new UriResource(new URI(newID.get()))
|
||||
.setContext(new ResourceContext(REFRESHED, TRUE)));
|
||||
// If the refresh is not successful, continue the verification process using the original OpenBadgeCredential.
|
||||
UriResource uriResource = new UriResource(new URI(newID.get()), null, REFRESH_SERVICE_MIME_TYPES);
|
||||
if (uriResource.exists()) {
|
||||
return this.run(uriResource.setContext(new ResourceContext(REFRESHED, TRUE)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,10 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.oneedtech.inspect.schema.Catalog;
|
||||
import org.oneedtech.inspect.schema.SchemaKey;
|
||||
import org.oneedtech.inspect.util.resource.MimeType;
|
||||
import org.oneedtech.inspect.util.resource.Resource;
|
||||
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
||||
|
||||
@ -53,17 +53,24 @@ public class VerifiableCredential extends Credential {
|
||||
private static final Map<Set<VerifiableCredential.Type>, List<String>> contextMap = new ImmutableMap.Builder<Set<VerifiableCredential.Type>, List<String>>()
|
||||
.put(Set.of(Type.OpenBadgeCredential, AchievementCredential, EndorsementCredential),
|
||||
List.of("https://www.w3.org/2018/credentials/v1",
|
||||
//"https://purl.imsglobal.org/spec/ob/v3p0/context.json")) //dev legacy
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json"))
|
||||
.put(Set.of(ClrCredential),
|
||||
List.of("https://www.w3.org/2018/credentials/v1",
|
||||
// "https://dc.imsglobal.org/draft/clr/v2p0/context", //dev legacy
|
||||
// "https://purl.imsglobal.org/spec/ob/v3p0/context.json")) //dev legacy
|
||||
"https://purl.imsglobal.org/spec/clr/v2p0/context.json",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json"))
|
||||
|
||||
.build();
|
||||
|
||||
private static final Map<String, List<String>> contextAliasesMap = new ImmutableMap.Builder<String, List<String>>()
|
||||
.put("https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
List.of("https://purl.imsglobal.org/spec/ob/v3p0/context/ob_v3p0.jsonld"))
|
||||
.build();
|
||||
|
||||
private static final Map<String, List<String>> contextVersioningPatternMap = new ImmutableMap.Builder<String, List<String>>()
|
||||
.put("https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
List.of("https:\\/\\/purl\\.imsglobal\\.org\\/spec\\/ob\\/v3p0\\/context(-\\d+\\.\\d+\\.\\d+)*\\.json"))
|
||||
.build();
|
||||
|
||||
public enum Type implements CredentialEnum {
|
||||
AchievementCredential(Collections.emptyList()),
|
||||
OpenBadgeCredential(List.of("OpenBadgeCredential", "AchievementCredential")), //treated as an alias of AchievementCredential
|
||||
@ -119,6 +126,14 @@ public class VerifiableCredential extends Credential {
|
||||
.findFirst()
|
||||
.orElseThrow(()-> new IllegalArgumentException(this.name() + " not recognized")));
|
||||
}
|
||||
@Override
|
||||
public Map<String, List<String>> getContextAliases() {
|
||||
return contextAliasesMap;
|
||||
}
|
||||
@Override
|
||||
public Map<String, List<String>> getContextVersionPatterns() {
|
||||
return contextVersioningPatternMap;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ProofType {
|
||||
@ -145,4 +160,5 @@ public class VerifiableCredential extends Credential {
|
||||
private static final String ISSUED_ON_PROPERTY_NAME = "issuanceDate";
|
||||
private static final String EXPIRES_AT_PROPERTY_NAME = "expirationDate";
|
||||
public static final String JWT_NODE_NAME = "vc";
|
||||
public static final List<MimeType> REFRESH_SERVICE_MIME_TYPES = List.of(MimeType.JSON, MimeType.JSON_LD, MimeType.TEXT_PLAIN);
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
package org.oneedtech.inspect.vc;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.oneedtech.inspect.vc.jsonld.JsonLDObjectUtils;
|
||||
|
||||
import com.danubetech.verifiablecredentials.VerifiableCredential;
|
||||
|
||||
import foundation.identity.jsonld.ConfigurableDocumentLoader;
|
||||
import info.weboftrust.ldsignatures.LdProof;
|
||||
|
||||
/**
|
||||
* Holder for W3C's Verifiable Credential
|
||||
*/
|
||||
public class W3CVCHolder {
|
||||
private VerifiableCredential credential;
|
||||
|
||||
public W3CVCHolder(VerifiableCredential credential) {
|
||||
this.credential = credential;
|
||||
ConfigurableDocumentLoader documentLoader = new ConfigurableDocumentLoader();
|
||||
documentLoader.setEnableHttp(true);
|
||||
documentLoader.setEnableHttps(true);
|
||||
credential.setDocumentLoader(documentLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of proofs in the credential.
|
||||
* {@link VerifiableCredential} contains the method getLdProof(), but only works with one proof. This methods
|
||||
* returns a list of all proofs defined in the credential.
|
||||
* @return proofs defined in the credential
|
||||
*/
|
||||
public List<LdProof> getProofs() {
|
||||
return JsonLDObjectUtils.getListFromJsonLDObject(LdProof.class, credential);
|
||||
}
|
||||
|
||||
public VerifiableCredential getCredential() {
|
||||
return credential;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package org.oneedtech.inspect.vc.jsonld;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import foundation.identity.jsonld.JsonLDObject;
|
||||
|
||||
public class JsonLDObjectUtils {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <C extends JsonLDObject> List<C> getListFromJsonLDObject(Class<C> cl, JsonLDObject jsonLdObject) {
|
||||
String term = JsonLDObject.getDefaultJsonLDPredicate(cl);
|
||||
List<Map<String, Object>> jsonObjects = jsonLdGetJsonObjectList(jsonLdObject.getJsonObject(), term);
|
||||
if (jsonObjects == null) return null;
|
||||
try {
|
||||
Method method = cl.getMethod("fromMap", Map.class);
|
||||
return jsonObjects.stream().map(jsonObject -> {
|
||||
try {
|
||||
return (C) method.invoke(null, jsonObject);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
} catch (NoSuchMethodException | SecurityException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<Map<String, Object>> jsonLdGetJsonObjectList(Map<String, Object> jsonObject, String term) {
|
||||
Object entry = jsonObject.get(term);
|
||||
if (entry == null) return null;
|
||||
|
||||
if (entry instanceof Map<?, ?>) {
|
||||
return Collections.singletonList((Map<String, Object>) entry);
|
||||
} else if (entry instanceof List<?> && ((List<Object>) entry).stream().allMatch(e -> e instanceof Map<?, ?>)) {
|
||||
return (List<Map<String, Object>>) (List<Map<String,Object>>) entry;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot get json object '" + term + "' from " + jsonObject);
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ public class ContextPropertyProbe extends StringValuePropertyProbe {
|
||||
|
||||
int pos = 0;
|
||||
for (String uri : contextUris) {
|
||||
if ((nodeValues.size() < pos + 1) || !nodeValues.get(pos).equals(uri)) {
|
||||
if ((nodeValues.size() < pos + 1) || !contains(uri, nodeValues.get(pos))) {
|
||||
return error("missing required @context uri " + uri + " at position " + (pos + 1), ctx);
|
||||
}
|
||||
pos++;
|
||||
@ -46,5 +46,25 @@ public class ContextPropertyProbe extends StringValuePropertyProbe {
|
||||
return success(ctx);
|
||||
}
|
||||
|
||||
private boolean contains(String uri, String nodeValue) {
|
||||
// check equal case
|
||||
if (nodeValue.equals(uri)) {
|
||||
return true;
|
||||
}
|
||||
// check aliases
|
||||
if (type.getContextAliases().containsKey(uri)) {
|
||||
if (type.getContextAliases().get(uri).stream().anyMatch(alias -> nodeValue.equals(alias))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// check versioning
|
||||
if (type.getContextVersionPatterns().containsKey(uri)) {
|
||||
if (type.getContextVersionPatterns().get(uri).stream().anyMatch(version -> nodeValue.matches(version))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final String ID = ContextPropertyProbe.class.getSimpleName();
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package org.oneedtech.inspect.vc.probe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.oneedtech.inspect.core.probe.Probe;
|
||||
import org.oneedtech.inspect.core.probe.RunContext;
|
||||
import org.oneedtech.inspect.core.report.ReportItems;
|
||||
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
@ -14,8 +17,20 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
*/
|
||||
public class CredentialSubjectProbe extends Probe<JsonNode> {
|
||||
|
||||
public CredentialSubjectProbe() {
|
||||
/**
|
||||
* Required type to be present.
|
||||
*/
|
||||
private final String requiredType;
|
||||
private boolean achivementRequired;
|
||||
|
||||
public CredentialSubjectProbe(String requiredType) {
|
||||
this(requiredType, false);
|
||||
}
|
||||
|
||||
public CredentialSubjectProbe(String requiredType, boolean achivementRequired) {
|
||||
super(ID);
|
||||
this.requiredType = requiredType;
|
||||
this.achivementRequired = achivementRequired;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -24,18 +39,86 @@ public class CredentialSubjectProbe extends Probe<JsonNode> {
|
||||
JsonNode subject = root.get("credentialSubject");
|
||||
if(subject == null) return notRun("no credentialSubject node found", ctx); //error reported by schema
|
||||
|
||||
/**
|
||||
* Check that type contains AchievementSubject
|
||||
*/
|
||||
if (!JsonNodeUtil.asStringList(subject.get("type")).contains(requiredType)) {
|
||||
return error("credentialSubject is not of type \"" + requiredType + "\"", ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that we have either .id or .identifier populated
|
||||
*/
|
||||
if (idAndIdentifierEmpty(subject)) {
|
||||
return error("no id in credentialSubject", ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* if .identifier is provider, check its type
|
||||
*/
|
||||
if (subject.hasNonNull("identifier")) {
|
||||
List<JsonNode> identifiers = JsonNodeUtil.asNodeList(subject.get("identifier"));
|
||||
for (JsonNode identifier : identifiers) {
|
||||
// check that type contains "IdentityObject"
|
||||
if (!JsonNodeUtil.asStringList(identifier.get("type")).contains("IdentityObject")) {
|
||||
return error("identifier in credentialSubject is not of type \"IdentityObject\"", ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check results
|
||||
*/
|
||||
if (subject.hasNonNull("result")) {
|
||||
List<JsonNode> results = JsonNodeUtil.asNodeList(subject.get("result"));
|
||||
for (JsonNode result : results) {
|
||||
// check that type contains "Result"
|
||||
if (!JsonNodeUtil.asStringList(result.get("type")).contains("Result")) {
|
||||
return error("result in credentialSubject is not of type \"Result\"", ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check achievement result description
|
||||
*/
|
||||
if (subject.hasNonNull("achievement")) {
|
||||
JsonNode achievement = subject.get("achievement");
|
||||
if (achievement.hasNonNull("resultDescription")) {
|
||||
List<JsonNode> resultDescriptions = JsonNodeUtil.asNodeList(achievement.get("resultDescription"));
|
||||
for (JsonNode resultDescription : resultDescriptions) {
|
||||
// check that type contains "ResultDescription"
|
||||
if (!JsonNodeUtil.asStringList(resultDescription.get("type")).contains("ResultDescription")) {
|
||||
return error("resultDescription in achievement of credentialSubject is not of type \"ResultDescription\"", ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (achivementRequired) {
|
||||
return error("missing required achievement in credentialSubject", ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that source type contains "Profile"
|
||||
*/
|
||||
if (subject.hasNonNull("source")) {
|
||||
JsonNode source = subject.get("source");
|
||||
// check that type contains "Profile"
|
||||
if (!JsonNodeUtil.asStringList(source.get("type")).contains("Profile")) {
|
||||
return error("source in credentialSubject is not of type \"Profile\"", ctx);
|
||||
}
|
||||
}
|
||||
return success(ctx);
|
||||
}
|
||||
|
||||
private boolean idAndIdentifierEmpty(JsonNode root) {
|
||||
JsonNode id = root.get("id");
|
||||
if (id != null && id.textValue().strip().length() > 0) return success(ctx);
|
||||
if (id != null && id.textValue().strip().length() > 0) return false;
|
||||
|
||||
JsonNode identifier = root.get("identifier");
|
||||
if(identifier != null && identifier instanceof ArrayNode
|
||||
&& ((ArrayNode)identifier).size() > 0) return success(ctx);
|
||||
|
||||
return error("no id in credentialSubject", ctx);
|
||||
&& ((ArrayNode)identifier).size() > 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final String ID = CredentialSubjectProbe.class.getSimpleName();
|
||||
|
@ -2,13 +2,14 @@ package org.oneedtech.inspect.vc.probe;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.oneedtech.inspect.core.probe.Probe;
|
||||
import org.oneedtech.inspect.core.probe.RunContext;
|
||||
import org.oneedtech.inspect.core.report.ReportItems;
|
||||
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||
import org.oneedtech.inspect.vc.util.CachingDocumentLoader;
|
||||
import org.oneedtech.inspect.vc.W3CVCHolder;
|
||||
|
||||
import com.apicatalog.jsonld.StringUtils;
|
||||
import com.apicatalog.jsonld.document.Document;
|
||||
@ -17,7 +18,6 @@ import com.apicatalog.multibase.Multibase;
|
||||
import com.apicatalog.multicodec.Multicodec;
|
||||
import com.apicatalog.multicodec.Multicodec.Codec;
|
||||
|
||||
import foundation.identity.jsonld.ConfigurableDocumentLoader;
|
||||
import info.weboftrust.ldsignatures.LdProof;
|
||||
import info.weboftrust.ldsignatures.verifier.Ed25519Signature2020LdVerifier;
|
||||
import jakarta.json.JsonObject;
|
||||
@ -41,18 +41,23 @@ public class EmbeddedProofProbe extends Probe<VerifiableCredential> {
|
||||
@Override
|
||||
public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception {
|
||||
|
||||
// TODO: What there are multiple proofs?
|
||||
W3CVCHolder credentiaHolder = new W3CVCHolder(com.danubetech.verifiablecredentials.VerifiableCredential.fromJson(new StringReader(crd.getJson().toString())));
|
||||
|
||||
com.danubetech.verifiablecredentials.VerifiableCredential vc = com.danubetech.verifiablecredentials.VerifiableCredential.fromJson(new StringReader(crd.getJson().toString()));
|
||||
ConfigurableDocumentLoader documentLoader = new ConfigurableDocumentLoader();
|
||||
documentLoader.setEnableHttp(true);
|
||||
documentLoader.setEnableHttps(true);
|
||||
vc.setDocumentLoader(documentLoader);
|
||||
|
||||
LdProof proof = vc.getLdProof();
|
||||
if (proof == null) {
|
||||
List<LdProof> proofs = credentiaHolder.getProofs();
|
||||
if (proofs == null || proofs.size() == 0) {
|
||||
return error("The verifiable credential is missing a proof.", ctx);
|
||||
}
|
||||
|
||||
// get proof of standard type and purpose
|
||||
Optional<LdProof> selectedProof = proofs.stream().filter(proof -> proof.isType("Ed25519Signature2020") && proof.getProofPurpose().equals("assertionMethod"))
|
||||
.findFirst();
|
||||
|
||||
if (!selectedProof.isPresent()) {
|
||||
return error("No proof with type \"Ed25519Signature2020\" or proof purpose \"assertionMethod\" found", ctx);
|
||||
}
|
||||
|
||||
LdProof proof = selectedProof.get();
|
||||
|
||||
if (!proof.isType("Ed25519Signature2020")) {
|
||||
return error("Unknown proof type: " + proof.getType(), ctx);
|
||||
}
|
||||
@ -92,7 +97,7 @@ public class EmbeddedProofProbe extends Probe<VerifiableCredential> {
|
||||
}
|
||||
} else if (method.getScheme().equals("http") || method.getScheme().equals("https")) {
|
||||
try {
|
||||
Document keyDocument = vc.getDocumentLoader().loadDocument(method, new DocumentLoaderOptions());
|
||||
Document keyDocument = credentiaHolder.getCredential().getDocumentLoader().loadDocument(method, new DocumentLoaderOptions());
|
||||
Optional<JsonStructure> keyStructure = keyDocument.getJsonContent();
|
||||
if (keyStructure.isEmpty()) {
|
||||
return error("Key document not found at " + method, ctx);
|
||||
@ -135,8 +140,8 @@ public class EmbeddedProofProbe extends Probe<VerifiableCredential> {
|
||||
}
|
||||
|
||||
if (controller != null) {
|
||||
if (!controller.equals(vc.getIssuer().toString())) {
|
||||
return error("Key controller does not match issuer: " + vc.getIssuer(), ctx);
|
||||
if (!controller.equals(credentiaHolder.getCredential().getIssuer().toString())) {
|
||||
return error("Key controller does not match issuer: " + credentiaHolder.getCredential().getIssuer(), ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +151,7 @@ public class EmbeddedProofProbe extends Probe<VerifiableCredential> {
|
||||
Ed25519Signature2020LdVerifier verifier = new Ed25519Signature2020LdVerifier(publicKey);
|
||||
|
||||
try {
|
||||
boolean verify = verifier.verify(vc);
|
||||
boolean verify = verifier.verify(credentiaHolder.getCredential(), proof);
|
||||
if (!verify) {
|
||||
return error("Embedded proof verification failed.", ctx);
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package org.oneedtech.inspect.vc.probe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.oneedtech.inspect.core.probe.Probe;
|
||||
import org.oneedtech.inspect.core.probe.RunContext;
|
||||
import org.oneedtech.inspect.core.report.ReportItems;
|
||||
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public class EvidenceProbe extends Probe<JsonNode> {
|
||||
public EvidenceProbe() {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReportItems run(JsonNode root, RunContext ctx) throws Exception {
|
||||
|
||||
if (root.hasNonNull("evidence")) {
|
||||
/*
|
||||
* evidence is an array, so check type of each element
|
||||
*/
|
||||
List<JsonNode> evidences = JsonNodeUtil.asNodeList(root.get("evidence"));
|
||||
for (JsonNode evidence : evidences) {
|
||||
// check that type contains "Evidence"
|
||||
if (!JsonNodeUtil.asStringList(evidence.get("type")).contains("Evidence")) {
|
||||
return error("evidence is not of type \"Evidence\"", ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success(ctx);
|
||||
}
|
||||
|
||||
public static final String ID = EvidenceProbe.class.getSimpleName();
|
||||
|
||||
}
|
@ -50,7 +50,7 @@ public class ExternalProofProbe extends Probe<VerifiableCredential> {
|
||||
try {
|
||||
verifySignature(crd, ctx);
|
||||
} catch (Exception e) {
|
||||
return fatal("Error verifying jwt signature: " + e.getMessage(), ctx);
|
||||
return fatal("Error verifying jwt signature: " + e.getMessage() + (e.getCause() != null ? ". Reason: " + e.getCause().getMessage() : ""), ctx);
|
||||
}
|
||||
return success(ctx);
|
||||
}
|
||||
@ -75,7 +75,9 @@ public class ExternalProofProbe extends Probe<VerifiableCredential> {
|
||||
JsonNode alg = headerObj.get("alg");
|
||||
if(alg == null || !alg.textValue().equals("RS256")) { throw new Exception("alg must be present and must be 'RS256'"); }
|
||||
|
||||
//TODO: decoded jwt will check timestamps, but shall we explicitly break these out?
|
||||
// decoded jwt will check timestamps, but shall we explicitly break these out?
|
||||
// JWT verifier throws and exception with the cause when claims are invalid. Adding that cause
|
||||
// to the probe result can avoid having to explicitly check the claims.
|
||||
|
||||
//Option 1, fetch directly from header
|
||||
JsonNode jwk = headerObj.get("jwk");
|
||||
|
@ -12,6 +12,7 @@ import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe;
|
||||
import org.oneedtech.inspect.core.report.ReportItems;
|
||||
import org.oneedtech.inspect.schema.SchemaKey;
|
||||
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
@ -42,7 +43,7 @@ public class InlineJsonSchemaProbe extends Probe<JsonNode> {
|
||||
JsonNode credentialSchemaNode = root.get("credentialSchema");
|
||||
if(credentialSchemaNode == null) return success(ctx);
|
||||
|
||||
ArrayNode schemas = (ArrayNode) credentialSchemaNode; //TODO guard this cast
|
||||
List<JsonNode> schemas = JsonNodeUtil.asNodeList(credentialSchemaNode);
|
||||
|
||||
for(JsonNode schemaNode : schemas) {
|
||||
JsonNode typeNode = schemaNode.get("type");
|
||||
|
@ -0,0 +1,66 @@
|
||||
package org.oneedtech.inspect.vc.probe;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import org.oneedtech.inspect.core.probe.Probe;
|
||||
import org.oneedtech.inspect.core.probe.RunContext;
|
||||
import org.oneedtech.inspect.core.report.ReportItems;
|
||||
import org.oneedtech.inspect.util.resource.UriResource;
|
||||
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public class IssuerProbe extends Probe<JsonNode> {
|
||||
public IssuerProbe() {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReportItems run(JsonNode root, RunContext ctx) throws Exception {
|
||||
JsonNode issuer = root.get("issuer");
|
||||
if(issuer == null) return error("no issuer node found", ctx);
|
||||
|
||||
// check that type contains "Profile"
|
||||
if (!JsonNodeUtil.asStringList(issuer.get("type")).contains("Profile")) {
|
||||
return error("issuer is not of type \"Profile\"", ctx);
|
||||
}
|
||||
|
||||
// check url is accessible
|
||||
if (issuer.hasNonNull("url")) {
|
||||
try {
|
||||
UriResource urlResource = new UriResource(new URI(issuer.get("url").asText().strip()));
|
||||
if (!urlResource.exists()) {
|
||||
return warning("url \"" + issuer.get("url").asText().strip() + "\" in issuer is not accessible", ctx);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return warning("url \"" + issuer.get("url").asText().strip() + "\" in issuer is not accessible", ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// check other identifier
|
||||
if (issuer.hasNonNull("otherIdentifier")) {
|
||||
List<JsonNode> otherIdentifiers = JsonNodeUtil.asNodeList(issuer.get("otherIdentifier"));
|
||||
for (JsonNode otherIdentifier : otherIdentifiers) {
|
||||
// check that type contains "IdentityObject"
|
||||
if (!JsonNodeUtil.asStringList(otherIdentifier.get("type")).contains("IdentityObject")) {
|
||||
return error("otherIdentifier in issuer is not of type \"IdentityObject\"", ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check parent issuer
|
||||
if (issuer.hasNonNull("parentOrg")) {
|
||||
JsonNode parentOrg = issuer.get("parentOrg");
|
||||
// check that type contains "Profile"
|
||||
if (!JsonNodeUtil.asStringList(parentOrg.get("type")).contains("Profile")) {
|
||||
return error("parentOrg in issuer is not of type \"Profile\"", ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return success(ctx);
|
||||
}
|
||||
|
||||
public static final String ID = IssuerProbe.class.getSimpleName();
|
||||
|
||||
}
|
@ -3,6 +3,7 @@ package org.oneedtech.inspect.vc.probe;
|
||||
import static org.oneedtech.inspect.core.probe.RunContext.Key.JACKSON_OBJECTMAPPER;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
@ -10,6 +11,7 @@ import java.util.List;
|
||||
import org.oneedtech.inspect.core.probe.Probe;
|
||||
import org.oneedtech.inspect.core.probe.RunContext;
|
||||
import org.oneedtech.inspect.core.report.ReportItems;
|
||||
import org.oneedtech.inspect.util.resource.MimeType;
|
||||
import org.oneedtech.inspect.vc.Credential;
|
||||
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
||||
@ -45,7 +47,9 @@ public class RevocationListProbe extends Probe<Credential> {
|
||||
if(listID != null) {
|
||||
try {
|
||||
URL url = new URI(listID.asText().strip()).toURL();
|
||||
try (InputStream is = url.openStream()) {
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestProperty("Accept", MimeType.JSON.toString());
|
||||
try (InputStream is = connection.getInputStream()) {
|
||||
JsonNode revocList = ((ObjectMapper)ctx.get(JACKSON_OBJECTMAPPER)).readTree(is.readAllBytes());
|
||||
|
||||
/* To check if a credential has been revoked, the verifier issues a GET request
|
||||
|
@ -108,6 +108,7 @@ public class CachingDocumentLoader extends ConfigurableDocumentLoader {
|
||||
|
||||
static final ImmutableMap<String, URL> bundled = ImmutableMap.<String, URL>builder()
|
||||
.put("https://purl.imsglobal.org/spec/clr/v2p0/context.json",Resources.getResource("contexts/clr-v2p0.json"))
|
||||
.put("https://purl.imsglobal.org/spec/ob/v3p0/context/ob_v3p0.jsonld",Resources.getResource("contexts/ob-v3p0.json"))
|
||||
.put("https://purl.imsglobal.org/spec/ob/v3p0/context.json",Resources.getResource("contexts/ob-v3p0.json"))
|
||||
.put("https://purl.imsglobal.org/spec/ob/v3p0/extensions.json",Resources.getResource("contexts/ob-v3p0-extensions.json"))
|
||||
.put("https://www.w3.org/ns/did/v1", Resources.getResource("contexts/did-v1.jsonld"))
|
||||
|
@ -11,10 +11,13 @@ import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe;
|
||||
import org.oneedtech.inspect.core.report.Report;
|
||||
import org.oneedtech.inspect.test.PrintHelper;
|
||||
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
|
||||
import org.oneedtech.inspect.vc.probe.CredentialSubjectProbe;
|
||||
import org.oneedtech.inspect.vc.probe.ExpirationProbe;
|
||||
import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe;
|
||||
import org.oneedtech.inspect.vc.probe.IssuanceProbe;
|
||||
import org.oneedtech.inspect.vc.probe.IssuerProbe;
|
||||
import org.oneedtech.inspect.vc.probe.EmbeddedProofProbe;
|
||||
import org.oneedtech.inspect.vc.probe.EvidenceProbe;
|
||||
import org.oneedtech.inspect.vc.probe.TypePropertyProbe;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
@ -49,6 +52,15 @@ public class OB30Tests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleMultipleProofsJsonValid() {
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_MULTIPLE_PROOF_JSON.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertValid(report);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimplePNGPlainValid() {
|
||||
assertDoesNotThrow(()->{
|
||||
@ -180,6 +192,26 @@ public class OB30Tests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonContextAlias() {
|
||||
//removed one of the reqd context uris
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_ALIAS_CONTEXT.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertValid(report);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonContextVersion() {
|
||||
//removed one of the reqd context uris
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_VERSION_CONTEXT.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertHasValidProbeID(report, ContextPropertyProbe.ID);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonSchemaError() throws Exception {
|
||||
//issuer removed
|
||||
@ -191,6 +223,114 @@ public class OB30Tests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonInvalidCredentialSubjectType() {
|
||||
//add a dumb value to .type and remove the ob type
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_TYPE.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertInvalid(report);
|
||||
// assertFatalCount(report, 1);
|
||||
assertHasProbeID(report, CredentialSubjectProbe.ID, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonInvalidCredentialSubjectIdentifierType() {
|
||||
//add a dumb value to .type and remove the ob type
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_IDENTIFIER_TYPE.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertInvalid(report);
|
||||
// assertFatalCount(report, 1);
|
||||
assertHasProbeID(report, CredentialSubjectProbe.ID, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonInvalidCredentialSubjectResultType() {
|
||||
//add a dumb value to .type and remove the ob type
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_RESULT_TYPE.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertInvalid(report);
|
||||
// assertFatalCount(report, 1);
|
||||
assertHasProbeID(report, CredentialSubjectProbe.ID, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonInvalidCredentialSubjectAchievementResultDescriptionType() {
|
||||
//add a dumb value to .type and remove the ob type
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_ACHIEVEMENT_RESULT_DESCRIPTION_TYPE.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertInvalid(report);
|
||||
// assertFatalCount(report, 1);
|
||||
assertHasProbeID(report, CredentialSubjectProbe.ID, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonInvalidCredentialSubjectProfileType() {
|
||||
//add a dumb value to .type and remove the ob type
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_PROFILE_TYPE.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertInvalid(report);
|
||||
// assertFatalCount(report, 1);
|
||||
assertHasProbeID(report, CredentialSubjectProbe.ID, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonInvalidEvidenceType() {
|
||||
//add a dumb value to .type and remove the ob type
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_EVIDENCE_TYPE.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertInvalid(report);
|
||||
// assertFatalCount(report, 1);
|
||||
assertHasProbeID(report, EvidenceProbe.ID, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonInvalidIssuerType() {
|
||||
//add a dumb value to .type and remove the ob type
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_ISSUER_TYPE.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertInvalid(report);
|
||||
// assertFatalCount(report, 1);
|
||||
assertHasProbeID(report, IssuerProbe.ID, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonInvalidIssuerParentOrgType() {
|
||||
//add a dumb value to .type and remove the ob type
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_ISSUER_PARENTORG_TYPE.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertInvalid(report);
|
||||
// assertFatalCount(report, 1);
|
||||
assertHasProbeID(report, IssuerProbe.ID, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleJsonInvalidIssuerOtherIdentifierType() {
|
||||
//add a dumb value to .type and remove the ob type
|
||||
assertDoesNotThrow(()->{
|
||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_ISSUER_OTHERIDENTIFIER_TYPE.asFileResource());
|
||||
if(verbose) PrintHelper.print(report, true);
|
||||
assertInvalid(report);
|
||||
// assertFatalCount(report, 1);
|
||||
assertHasProbeID(report, IssuerProbe.ID, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Disabled //TODO IssuanceVerifierProbe is not run because FATAL: InvalidSignature terminates
|
||||
@Test
|
||||
void testSimpleJsonNotIssued() {
|
||||
|
@ -12,8 +12,18 @@ public class Samples {
|
||||
public final static Sample COMPLETE_JSON = new Sample("ob30/complete.json", false);
|
||||
public final static Sample SIMPLE_JSON = new Sample("ob30/simple.json", true);
|
||||
public final static Sample SIMPLE_DID_METHOD_JSON = new Sample("ob30/simple-did-method.json", true);
|
||||
public final static Sample SIMPLE_MULTIPLE_PROOF_JSON = new Sample("ob30/simple-multiple-proofs.json", true);
|
||||
public final static Sample SIMPLE_JSON_NOPROOF = new Sample("ob30/simple-noproof.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_TYPE = new Sample("ob30/simple-err-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_TYPE = new Sample("ob30/simple-err-credential-subject-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_IDENTIFIER_TYPE = new Sample("ob30/simple-err-credential-subject-identifier-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_RESULT_TYPE = new Sample("ob30/simple-err-credential-subject-result-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_ACHIEVEMENT_RESULT_DESCRIPTION_TYPE = new Sample("ob30/simple-err-credential-subject-achievement-result-description-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_PROFILE_TYPE = new Sample("ob30/simple-err-credential-subject-profile-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_EVIDENCE_TYPE = new Sample("ob30/simple-err-evidence-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_ISSUER_TYPE = new Sample("ob30/simple-err-issuer-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_ISSUER_OTHERIDENTIFIER_TYPE = new Sample("ob30/simple-err-issuer-otheridentifier-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_UNKNOWN_ISSUER_PARENTORG_TYPE = new Sample("ob30/simple-err-issuer-parentorg-type.json", false);
|
||||
public final static Sample SIMPLE_JSON_PROOF_METHOD_ERROR = new Sample("ob30/simple-err-proof-method.json", false);
|
||||
public final static Sample SIMPLE_JSON_PROOF_METHOD_NO_SCHEME_ERROR = new Sample("ob30/simple-err-proof-method-no-scheme.json", false);
|
||||
public final static Sample SIMPLE_JSON_PROOF_METHOD_UNKNOWN_SCHEME_ERROR = new Sample("ob30/simple-err-proof-method-unknown-scheme.json", false);
|
||||
@ -25,6 +35,8 @@ public class Samples {
|
||||
public final static Sample SIMPLE_JSON_ERR_CONTEXT = new Sample("ob30/simple-err-context.json", false);
|
||||
public final static Sample ENDORSEMENT_ERR_SCHEMA_STATUS_REFRESH = new Sample("ob30/endorsement-err-schema-status-refresh.json", false);
|
||||
public final static Sample ENDORSEMENT_VALID = new Sample("ob30/endorsement-valid.json", false);
|
||||
public final static Sample SIMPLE_JSON_ALIAS_CONTEXT = new Sample("ob30/simple-context-alias.json", true);
|
||||
public final static Sample SIMPLE_JSON_VERSION_CONTEXT = new Sample("ob30/simple-context-version.json", true);
|
||||
}
|
||||
public static final class PNG {
|
||||
public final static Sample SIMPLE_JWT_PNG = new Sample("ob30/simple-jwt.png", true);
|
||||
|
@ -0,0 +1,47 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context/ob_v3p0.jsonld",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
}
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.1.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
}
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork",
|
||||
"resultDescription": [
|
||||
{
|
||||
"id": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c",
|
||||
"type": "ResultDescription",
|
||||
"alignment": [
|
||||
{
|
||||
"type": "Alignment",
|
||||
"targetCode": "project",
|
||||
"targetDescription": "Project description",
|
||||
"targetName": "Final Project",
|
||||
"targetFramework": "1EdTech University Program and Course Catalog",
|
||||
"targetType": "CFItem",
|
||||
"targetUrl": "https://1edtech.edu/catalog/degree/project"
|
||||
}
|
||||
],
|
||||
"allowedValue": [
|
||||
"D",
|
||||
"C",
|
||||
"B",
|
||||
"A"
|
||||
],
|
||||
"name": "Final Project Grade",
|
||||
"requiredValue": "C",
|
||||
"resultType": "LetterGrade"
|
||||
},
|
||||
{
|
||||
"id": "urn:uuid:a70ddc6a-4c4a-4bd8-8277-cb97c79f40c5",
|
||||
"type": "ResultDescription",
|
||||
"alignment": [
|
||||
{
|
||||
"type": "Alignment",
|
||||
"targetCode": "project",
|
||||
"targetDescription": "Project description",
|
||||
"targetName": "Final Project",
|
||||
"targetFramework": "1EdTech University Program and Course Catalog",
|
||||
"targetType": "CFItem",
|
||||
"targetUrl": "https://1edtech.edu/catalog/degree/project"
|
||||
}
|
||||
],
|
||||
"allowedValue": [
|
||||
"D",
|
||||
"C",
|
||||
"B",
|
||||
"A"
|
||||
],
|
||||
"name": "Final Project Grade",
|
||||
"requiredLevel": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a",
|
||||
"resultType": "RubricCriterionLevel",
|
||||
"rubricCriterionLevel": [
|
||||
{
|
||||
"id": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a",
|
||||
"type": "RubricCriterionLevel",
|
||||
"alignment": [
|
||||
{
|
||||
"type": "Alignment",
|
||||
"targetCode": "project",
|
||||
"targetDescription": "Project description",
|
||||
"targetName": "Final Project",
|
||||
"targetFramework": "1EdTech University Program and Course Catalog",
|
||||
"targetType": "CFRubricCriterionLevel",
|
||||
"targetUrl": "https://1edtech.edu/catalog/degree/project/rubric/levels/mastered"
|
||||
}
|
||||
],
|
||||
"description": "The author demonstrated...",
|
||||
"level": "Mastered",
|
||||
"name": "Mastery",
|
||||
"points": "4"
|
||||
},
|
||||
{
|
||||
"id": "urn:uuid:6b84b429-31ee-4dac-9d20-e5c55881f80e",
|
||||
"type": "RubricCriterionLevel",
|
||||
"alignment": [
|
||||
{
|
||||
"type": "Alignment",
|
||||
"targetCode": "project",
|
||||
"targetDescription": "Project description",
|
||||
"targetName": "Final Project",
|
||||
"targetFramework": "1EdTech University Program and Course Catalog",
|
||||
"targetType": "CFRubricCriterionLevel",
|
||||
"targetUrl": "https://1edtech.edu/catalog/degree/project/rubric/levels/basic"
|
||||
}
|
||||
],
|
||||
"description": "The author demonstrated...",
|
||||
"level": "Basic",
|
||||
"name": "Basic",
|
||||
"points": "4"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "urn:uuid:b07c0387-f2d6-4b65-a3f4-f4e4302ea8f7",
|
||||
"type": "InvalidResultDescription",
|
||||
"name": "Project Status",
|
||||
"resultType": "Status"
|
||||
}
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
{
|
||||
"type": [
|
||||
"Result"
|
||||
],
|
||||
"alignment": [
|
||||
{
|
||||
"type": "Alignment",
|
||||
"targetCode": "project",
|
||||
"targetDescription": "Project description",
|
||||
"targetName": "Final Project",
|
||||
"targetFramework": "1EdTech University Program and Course Catalog",
|
||||
"targetType": "CFItem",
|
||||
"targetUrl": "https://1edtech.edu/catalog/degree/project/result/1"
|
||||
}
|
||||
],
|
||||
"resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c",
|
||||
"value": "A"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"Result"
|
||||
],
|
||||
"achievedLevel": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a",
|
||||
"alignment": [
|
||||
{
|
||||
"type": "Alignment",
|
||||
"targetCode": "project",
|
||||
"targetDescription": "Project description",
|
||||
"targetName": "Final Project",
|
||||
"targetFramework": "1EdTech University Program and Course Catalog",
|
||||
"targetType": "CFItem",
|
||||
"targetUrl": "https://1edtech.edu/catalog/degree/project/result/1"
|
||||
}
|
||||
],
|
||||
"resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"Result"
|
||||
],
|
||||
"resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c",
|
||||
"status": "Completed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"identifier": [{
|
||||
"type": "InvalidIdentityObject",
|
||||
"hashed": true,
|
||||
"identityHash": "asdjhsadas",
|
||||
"identityType": "lisSourcedId"
|
||||
}],
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
}
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
},
|
||||
"source": {
|
||||
"id": "https://school.edu/issuers/201234",
|
||||
"type": "InvalidProfile",
|
||||
"name": "1EdTech College of Arts"
|
||||
}
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
},
|
||||
"result": [
|
||||
{
|
||||
"type": [
|
||||
"Result"
|
||||
],
|
||||
"alignment": [
|
||||
{
|
||||
"type": "Alignment",
|
||||
"targetCode": "project",
|
||||
"targetDescription": "Project description",
|
||||
"targetName": "Final Project",
|
||||
"targetFramework": "1EdTech University Program and Course Catalog",
|
||||
"targetType": "CFItem",
|
||||
"targetUrl": "https://1edtech.edu/catalog/degree/project/result/1"
|
||||
}
|
||||
],
|
||||
"resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c",
|
||||
"value": "A"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"InvalidResult"
|
||||
],
|
||||
"achievedLevel": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a",
|
||||
"alignment": [
|
||||
{
|
||||
"type": "Alignment",
|
||||
"targetCode": "project",
|
||||
"targetDescription": "Project description",
|
||||
"targetName": "Final Project",
|
||||
"targetFramework": "1EdTech University Program and Course Catalog",
|
||||
"targetType": "CFItem",
|
||||
"targetUrl": "https://1edtech.edu/catalog/degree/project/result/1"
|
||||
}
|
||||
],
|
||||
"resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"Result"
|
||||
],
|
||||
"resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c",
|
||||
"status": "Completed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"InvalidAchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
}
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
}
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"id": "https://1edtech.edu/credentials/3732/evidence/1",
|
||||
"type": "Evidence",
|
||||
"narrative": "# Final Project Report \n This project was ...",
|
||||
"name": "Final Project Report",
|
||||
"description": "This is the final project report.",
|
||||
"genre": "Research",
|
||||
"audience": "Department"
|
||||
},
|
||||
{
|
||||
"id": "https://github.com/somebody/project",
|
||||
"type": "InvalidEvidence",
|
||||
"name": "Final Project Code",
|
||||
"description": "This is the source code for the final project app.",
|
||||
"genre": "Research",
|
||||
"audience": "Department"
|
||||
}
|
||||
],
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp",
|
||||
"otherIdentifier": [
|
||||
{
|
||||
"type": "IdentifierEntry",
|
||||
"identifier": "12345",
|
||||
"identifierType": "sourcedId"
|
||||
},
|
||||
{
|
||||
"type": "InvalidIdentifierEntry",
|
||||
"identifier": "67890",
|
||||
"identifierType": "nationalIdentityNumber"
|
||||
}
|
||||
]
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
}
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp",
|
||||
"parentOrg": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"InvalidProfile"
|
||||
],
|
||||
"name": "Example Parent Corp"
|
||||
}
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
}
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"InvalidProfile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
}
|
||||
},
|
||||
"proof": [
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/2018/credentials/v1",
|
||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||
],
|
||||
"id": "http://example.com/credentials/3527",
|
||||
"type": [
|
||||
"VerifiableCredential",
|
||||
"OpenBadgeCredential"
|
||||
],
|
||||
"issuer": {
|
||||
"id": "https://example.com/issuers/876543",
|
||||
"type": [
|
||||
"Profile"
|
||||
],
|
||||
"name": "Example Corp"
|
||||
},
|
||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||
"name": "Teamwork Badge",
|
||||
"credentialSubject": {
|
||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||
"type": [
|
||||
"AchievementSubject"
|
||||
],
|
||||
"achievement": {
|
||||
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||
"type": [
|
||||
"Achievement"
|
||||
],
|
||||
"criteria": {
|
||||
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||
},
|
||||
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||
"name": "Teamwork"
|
||||
}
|
||||
},
|
||||
"proof": [{
|
||||
"type": "SomeProofType",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"proofPurpose": "assertionMethod"
|
||||
},
|
||||
{
|
||||
"type": "Ed25519Signature2020",
|
||||
"created": "2022-11-16T18:54:22Z",
|
||||
"verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user