We will now generate a key and associated DID via the issuer API. However, we need to store
the key material + DID ourselves.
For production environments, we recommend using an external KMS provider for key management due to the
enhanced security. Learn more about the different types of keys and the storage options here.
As the issuer API doesn't store any cryptographic key material by default, you need to manage and provide the key used for signing the credential. You can:
- Use a JWK key – provide the signing key directly in JWK format and manage it yourself.
- Use an external KMS – provide a reference object that points to a key stored in a supported KMS such as Hashicorp Vault or Oracle KMS.
In a production environment, we recommend using an external KMS provider to secure the key material.
To create keys for signing credentials,
you can use the /onboard/issuer endpoint and choose from a variety of algorithms (ed25519,
secp256k1, secp256r1, or RSA). The DID generated by the endpoint is not needed for the SD-JWT VC.
Please refer to our Key Management section
to learn more about the different options.
For this guide, we will proceed with generating a JWK Ed25519 key using the onboard endpoint.
Endpoint: /onboard/issuer | API Reference
Example Request
curl -X 'POST' \
'http://0.0.0.0:7002/onboard/issuer' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"key": {
"backend": "jwk",
"keyType": "Ed25519"
},
"did": {
"method": "jwk"
}
}'
Body
{
"key": {
"backend": "jwk",
"keyType": "Ed25519"
},
"did": {
"method": "jwk"
}
}
Body Parameters
keybackend: String - Specifies the storage type of key. It can be jwk (manged by you), TSE (managed by
Hashicorp
Vault) and others. Learn more about different types here.keyType: String - the algorithm used to generate the key. For local, it can
be ed25519, secp256k1, secp256r1, or RSA. For the other types and the supported algorithms, please
go here.
did:method: String - Specifies the DID method. It can be key, jwk, web, cheqd.
Example Response
The onboard/issuer endpoint will return an object containing both the generated key in JWK format and the related DID.
{
"issuerKey": {
"type": "jwk",
"jwk": {
"kty": "OKP",
"d": "Sr5-oOEXlYU_yCw7ADn-zsvJwMc7cEd2l5klg9i8n1k",
"crv": "Ed25519",
"kid": "Zf8NXHGvBlbzX3hdKX1YHZTindNf7LrixPZallJ7nxk",
"x": "4Iviyx79FlJH5w81DxJwfeRD6JPB4vCJoHzKOQwJ0cs"
}
},
"issuerDid": "did:cheqd:testnet:26948e3c-4493-48fe-a61c-9052ecb62718"
}
Important: As we've used the jwk key type, it's important to note that we need to save the returned values ourselves for future
reference.
The API doesn't save any information about created keys.
To issue a verifiable credential, we will use the obtained key or any other supported JWK. When issuing an SD-JWT
VC credential, we have the option to selectively disclose certain claims in the credential. By default, all claims are
visible. The configuration for selectively disclosing claims can be done through a configuration object outlined below.
To facilitate the issuance of the credential from us (the issuer) to the holder, we will utilise the OID4VCI protocol.
In particular, we will be generating an OID4VC offer URL that can be accepted by any OID compliant wallet to receive
credential(s).
The credential offer URL specifies the credentials to be issued. This includes details such as
the URL of the issuer, the authorisation and token endpoints, and information about the credential's format, type, and
scope.
When we execute the issuance command, two things will happen:
- The credential will be signed with the provided key.
- Information about the offered credential will be embedded into the OID Credential Offer
URL, which we can send off to our users to claim the credential(s) later on.
The Verifiable Credential Type (VCT) is resolved through the credentialConfigurationId. The vct is found as a parameter within the corresponding entry associated with that id.
Issuer API generates proper resolvable urls for VCT based on provided configuration and host endpoints to retrieve type metadata with the scheme https://<authority>/.well-known/vct/<type> (e.g. https://issuer.demo.walt.id/.well-known/vct/identity_credential).
Find more info here
Endpoint:/openid4vc/sdjwt/issue | API Reference
Example Request
curl -X 'POST' \
'https://issuer.demo.walt.id/openid4vc/sdjwt/issue' \
-H 'accept: text/plain' \
-H 'statusCallbackUri: https://example.com/$id' \
-H 'Content-Type: application/json' \
-d '{
"issuerKey": {
"type": "jwk",
"jwk": {
"kty": "OKP",
"d": "mDhpwaH6JYSrD2Bq7Cs-pzmsjlLj4EOhxyI-9DM1mFI",
"crv": "Ed25519",
"kid": "Vzx7l5fh56F3Pf9aR3DECU5BwfrY6ZJe05aiWYWzan8",
"x": "T3T4-u1Xz3vAV2JwPNxWfs4pik_JLiArz_WTCvrCFUM"
}
},
"issuerDid": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
"credentialConfigurationId": "identity_credential_vc+sd-jwt",
"credentialData": {
"given_name": "John",
"family_name": "Doe",
"email": "johndoe@example.com",
"phone_number": "+1-202-555-0101",
"address": {
"street_address": "123 Main St",
"locality": "Anytown",
"region": "Anystate",
"country": "US"
},
"birthdate": "1940-01-01",
"is_over_18": true,
"is_over_21": true,
"is_over_65": true
},
"mapping": {
"id": "<uuid>",
"iat": "<timestamp-seconds>",
"nbf": "<timestamp-seconds>",
"exp": "<timestamp-in-seconds:365d>"
},
"authenticationMethod": "PRE_AUTHORIZED"
"selectiveDisclosure": {
"fields": {
"birthdate": {
"sd": true
}
},
"decoyMode": "NONE",
"decoys": 0
}
}'
Header Parameters
statusCallbackUri: URL - Receive updates on the created issuance process, e.g. when a credential was successfully
claimed. The parameter expects a URL which can accept a JSON POST request. The URL can also hold a $id, which will
be
replaced by the issuance session id. For example: https://myurl.com/$id, https://myurl.com
or https://myurl.com/test/$id
Expand To Learn More
Body
The data send to the provided URL will contain a JSON body:
id : String - the issuance session idtype: String - the event typedata: JsonObject - the data for the event
Event Types
Possible events (event types) and their data are:
resolved_credential_offer with the credential offer as JSON (in our Web Wallet: called when the issuance offer is
entered into the wallet, but not processing / accepted yet)requested_token with the issuance request for the token as json object (called for the token required to receive the
credentials)
Credential issuance (called for every credential that's issued (= requested from wallet))
jwt_issue with jwt being the issued jwtsdjwt_issue with sdjwt being the issued sdjwtbatch_jwt_issue with jwt being the issued jwtbatch_sdjwt_issue with sdjwt being the issued sdjwtgenerated_mdoc with mdoc being the CBOR (HEX) of the signed mdoc
To allow for secure business logic flows, if a callback URL is set, and it cannot be reached, the issuance will not
commence further (after that point). If no callback URL is set, the issuance logic does not change in any way.
Body
{
"issuerKey": {
"type": "jwk",
"jwk": {
"kty": "OKP",
"d": "mDhpwaH6JYSrD2Bq7Cs-pzmsjlLj4EOhxyI-9DM1mFI",
"crv": "Ed25519",
"kid": "Vzx7l5fh56F3Pf9aR3DECU5BwfrY6ZJe05aiWYWzan8",
"x": "T3T4-u1Xz3vAV2JwPNxWfs4pik_JLiArz_WTCvrCFUM"
}
},
"issuerDid": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
"credentialConfigurationId": "identity_credential_vc+sd-jwt",
"credentialData": {
"given_name": "John",
"family_name": "Doe",
"email": "johndoe@example.com",
"phone_number": "+1-202-555-0101",
"address": {
"street_address": "123 Main St",
"locality": "Anytown",
"region": "Anystate",
"country": "US"
},
"birthdate": "1940-01-01",
"is_over_18": true,
"is_over_21": true,
"is_over_65": true
},
"mapping": {
"id": "<uuid>",
"iat": "<timestamp-seconds>",
"nbf": "<timestamp-seconds>",
"exp": "<timestamp-in-seconds:365d>"
},
"authenticationMethod": "PRE_AUTHORIZED",
"selectiveDisclosure": {
"fields": {
"birthdate": {
"sd": true
}
},
"decoyMode": "NONE",
"decoys": 0
}
}
Body Parameters
issuerKey: JSON - A JWK or reference object to a key stored in an external KMS to sign the credential with.
Supported algorithms: ed25519, secp256k1, secp256r1, or
RSA.
JWK Format: {"type": "jwk", "jwk": Here JSON Web Key Object}. Can be provided as String Or JWK object to "
issuerKey".
KMS Key: Please refer to the Key Management Section and the KMS you want to
use for more details on the structure
of the reference object.
credentialConfigurationId: String - Reference to a specific credential configuration the issuer supports. As our
issuer currently supports W3C JWT & SD-JWT credentials and SD-JWT VCs (IETF). The structure of the credentialConfigurationId is the following: "
credentialType_jwt_vc_json" for W3C JWT & SD-JWT credentials
and "credentialType_vc+sd-jwt" for SD-JWT VCs (IETF). E.g. "UniversityDegree_jwt_vc_json" or "UniversityDegree_vc+sd-jwt".
You can also view
the credentialConfigurationIds by visiting the /.well-known/openid-credential-issuer endpoint of your issuer.
The metadata of our deployed issuer for testing can be seen here.credentialData: JSON - Credential data structure to sign.mapping (optional): JSON - The mapping object that allows for dynamic value insertion via data functions,
executed at the time when the credentials is claimed. This feature enables personalized credentials based on real-time
data. Learn more about it and see a list of supported data functions here.
In case of IETF SD-JWT, the values could be id, iat, nbf and exp.authenticationMethod: (optional) String - Defines which OIDC4VC exchange flow is used (pre-auth or full-auth). If
no value is
provided, it will default to pre-auth flow indicated as PRE_AUTHORIZED. The parameter options are:
Expand To Learn More About The Options
PWD - used for authorization code flow with username/password authentication with external auth serverID_TOKEN - used for authorization code flow with id_token authentication. When this method is used an authorization
request is sent to the wallet, which generates and issues the ID_TOKEN using its DID (
Decentralized Identifier). The issuer then verifies the ID_TOKEN to confirm the holder's identity and DID.VP_TOKEN - used for authorization code flow with vp_token(OIDC4VP). With this method, the receiver of the
credential must be authenticated by presenting the requested credential. The credential which must be presented can
be configured using the vpRequestedValue as explained below. The policies which are applied to the presented credential
are 'signature,' 'expired,' and 'not-before.' Learn more about policies here.NONE - used for authorization code flow with none authentication methodPRE_AUTHORIZED - used for pre-authorizated code flow
Additional parameters required for selected auth method:
For "ID_TOKEN" and "VP_TOKEN":
useJar: (Optional) Boolean - used for using JAR OAuth specification in the id/vp_token requests. If omitted, the
default value is true.
For VP_TOKEN:
vpRequestedValue: String - Specifies the requested credential type value for the VP token. E.g. "VerifiableId"vpProfile: (Optional) String - Specifies the profile of the VP request. Available Profiles: DEFAULT: For W3C
OpenID4VP,
ISO_18013_7_MDOC: For MDOC OpenID4VP, EBSIV3: For EBSI V3 Compliant VP. If omitted, the default value is DEFAULT
selectiveDisclosure (optional): JSON -An object that configures which
claims in the credential should be
selectively disclosable. It's manged through the following properties:Expand To Learn More
fields: An object illustrating the hierarchical structure of the credential contents. Each key in this object
specifies the name of the fields in the credential. Every field, whether high-level or nested, can be represented
by an object with a "sd" key. If the value for "sd" is set to true, it means that the corresponding field is
selectively disclosable. Moreover, fields that have nested attributes are represented with a "children" key which
contains another fields object reflecting the structure of the nested object. For example:
{
"fields": {
"issuanceDate": {
"sd": true
},
"credentialSubject": {
"sd": false,
"children": {
"fields": {
"degree": {
"sd": false,
"children": {
"fields": {
"name": {
"sd": true
}
}
}
}
}
}
}
}
}
Example Response
The issuer endpoint will respond with Credential Offer URL.
Plain Response
openid-credential-offer://issuer.potential.walt-test.cloud/?credential_offer_uri=https%3A%2F%2Fissuer.potential.walt-test.cloud%2Fopenid4vc%2FcredentialOffer%3Fid%3D9aabdb65-defe-464b-baa0-9cc13b36074a
Decoded
{
"credential_issuer": "https://issuer.potential.walt-test.cloud",
"credential_configuration_ids": [
"identity_credential_vc+sd-jwt"
],
"grants": {
"authorization_code": {
"issuer_state": "9aabdb65-defe-464b-baa0-9cc13b36074a"
},
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiI5YWFiZGI2NS1kZWZlLTQ2NGItYmFhMC05Y2MxM2IzNjA3NGEiLCJpc3MiOiJodHRwczovL2lzc3Vlci5wb3RlbnRpYWwud2FsdC10ZXN0LmNsb3VkIiwiYXVkIjoiVE9LRU4ifQ.vl9fBjoYUqr1nrA0jZ3ZjpS45yHLp2roMPCxCoqjuLNyBhTGO0g_PXMw8_NWhD-3qllRq5J0kLAw8WmfvT95Cw"
}
}
}