The Keycloak-VC-Issuer is plugin for Keycloak to support SIOP-2/ OIDC4VP clients and issue VerifiableCredentials through the OIDC4VCI-Protocol to compliant wallets.
Keycloak is a well established OpenSource Identity Management System. It's relied on in numerous environments and serves credentials and secrets to be used in different protocols, for different types of clients. While a VerifiableCredentials-based decentralized Identity Management does not necessarily require an IDM, a component to issue credentials to users is required. Since Keycloak already provides capabilities for managing users, clients and their roles, it's well suited to also serve VerifiableCredentials. The Keycloak-VC-Issuer therefor extends the Keycloak-APIs with such functionality and provides a theme that extends the Account Console with a UI for issuance of credentials to users.
The plugin is developed with the 20.0.3 libraries and tested against all Keycloak Minor-Releases >=18.0.0. Please check the Compatibility-Matrix for more information. The matrix gets updated every night.
The plugin targets compliance with the OpenID for Verifiable Credential Issuance(OIDC4VCI) standard, in order to be compatible with Wallets complying with the European Digital Identity Wallet Architecture and Reference Framework and any other standard-conformant Wallet-implementation. As of now, it supports the following parts of the spec:
- 3.5 Pre-Authorized Code Flow:
- in order to securely issue credentials, the plugin can offer pre-authorized authorization codes to authenticated users
- the code is connected to the user-session that requested the Credential Offer
- the code can be exchanged for an access-token through a token-endpoint as described in RFC 6749
- 4. Credential Offer Endpoint:
- to initiate standard conformant issuance, an endpoint to retrieve Credential Offer is provided
- the endpoint is available at
/realms/{realm-id}/verifiable-credential/{issuer-did}/credential-offer/{nonce}
. Nonce should be retrieved from the endpoint/realms/{realm-id}/verifiable-credential/{issuer-did}/credential-offer-uri
, which accepts the type and format of the credential - see api-spec for more
- 6. Token Endpoint
- supports token exchange through the pre-authorized flow
- available per issuer at
/realms/{realm-id}/verifiable-credential/{issuer-did}/token
- see api-spec for more
- pin-check is currently not supported
- 7. Credential Endpoint
- provides a valid credential, according to the requested type and format
- currently supports jwt_vc_json, jwt_vc_json-ld, ldp_vc and for backward-compatibility jwt_vc(which defaults to jwt_vc_json)
- proof-checking for the request is only supported for proof-type jwt(yet)
- 10.2. Credential Issuer Metadata
- provides the metadata for the issuer
⚠️ Since this is a plugin for Keycloak, having an instance of Keycloak running is a logical precondition. See the official Keycloak-Documentation on how to set it up.
The VC Issuer is a fully-self-contained provider, thus its jar-file only has to be added to the providers
-folder
of Keycloak(typically under /opt/keycloak/providers
). Keycloak will automatically pick up the provider at
start-time. The plugin is available as jar-file
through the github-releases.
In order to ease the deployment in containerized environments, a container including the jar-file is available at quay.io. The container can be used in containerized environments, to copy the jar file into a Keycloak instance, without having to manipulate the Keycloak-Image itself. An example usage of the container as an init-container in Kubernetes setups can be found in the integration test-setup(based on k3s).
To have the functionality integrated into the account-console, the SIOP-2
-theme has to be enabled for the realm:
In addition to Keycloak, an installation of the WaltID-SSIKit needs to be provided. WaltId manages the requiered decentralized identifiers and creates the actual credentials based on templates. It can f.e. be deployed via Helm-Chart or as a plain docker-container.
All CredentialTypes to be supported by the SIOP-2 clients need to have a corresponding template in WaltId. See WaltId Templates for details about the templating and the helm-chart as an example to provide them.
In order to provide the capabilities of issuing VerifiableCredentials and handling DIDs, Keycloak relies on Walt-ID as a downstream component. The integration-test setup provides an example on how to run and integrate it. The configuration is provided via environment variables:
Name | Description | Default |
---|---|---|
VCISSUER_WALTID_ADDRESS | Base address of walt-id. Has to include the protocol. | |
VCISSUER_WALTID_CORE_PORT | Port to be used for connecting the walt-id's core-api. | 7000 |
VCISSUER_WALTID_SIGNATORY_PORT | Port to be used for connecting the walt-id's signatory-api. | 7001 |
VCISSUER_ISSUER_DID | DID to be used for issuing credentials. If none is provided, Keycloak will create one. | |
VCISSUER_ISSUER_KEY_FILE | Path to the file containing the issuer key. |
The VC-Issuer plugin provides an integration for VerifiableCredentials
into Keycloak. It allows managing potential receivers of VerifiableCredentials
as SIOP-2 Clients, allowing to manage users and roles
in the well-known Keycloak way. In addition to that, it provides the endpoints for (authenticated) users to receive
VerifiableCredentials for their account. To integrate with the Account-Console frontend, a theme(siop-2
) is
included.
The plugin provides multiple endpoints through its API (see OpenApi-Doc) as a realm resource. They seperate into two categories. See the see OpenApi-Doc for detailed information and examples:
- OpenID for Verifiable Credential Issuance
compatible endpoints - tag
OIDC4VCI
- convenience endpoints to allow a more comfortable frontend integration - tag
IssuerPlugin
Most endpoints are only available to authenticated users, the following three informatory endpoints are publicly available:
- /{issuerDid}/.well-known/openid-credential-issuer - provides the issuer metadata in an OIDC4VCI compliant way
- /{issuerDid}/.well-known/openid-configuration - provides the openid-configuration in an OIDC4VCI compliant(and therefor als RFC8414 compliant) way
- /issuer - provides just the did of the configured issuer, that can be used to construct the other paths. Provided to ease frontend integrations
The provider does support the protocol-type SIOP-2
, therefore such clients can be created and managed. Since
integration into the Admin-Console is still open, the clients need to be created through the api. A registration will
look like:
{
"clientId": "did:key:z6Mkv4Lh9zBTPLoFhLHHMFJA7YAeVw5HFYZV8rkdfY9fNtm3",
"enabled": true,
"description": "Client to receive Verifiable Credentials.",
"protocol": "SIOP-2",
"supportedVCTypes": [
{
"type": "PacketDeliveryService",
"format": "ldp_vc"
},
{
"type": "PacketDeliveryService",
"format": "jwt_vc_json"
}
]
}
Alternatively, the client can also be directly create through the clients api(for example when using declarative configuration via keycloak-config-cli, find an example here):
{
"clientId": "did:key:z6MkigCEnopwujz8Ten2dzq91nvMjqbKQYcifuZhqBsEkH7g",
"enabled": true,
"description": "Client to receive Verifiable Credentials",
"surrogateAuthRequired": false,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"defaultRoles": [],
"redirectUris": [],
"webOrigins": [],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": false,
"serviceAccountsEnabled": false,
"publicClient": false,
"frontchannelLogout": false,
"protocol": "SIOP-2",
"attributes": {
"expiryInMin": "3600",
// attributes are of type "string": "string", thus we provide the prefixed-type, together with a comma-seperated list of formats
"vctypes_BatteryPassAuthCredential": "ldp_vc,jwt_vc_json",
// in order to provide static values to the credentials template, fields of structur vc_<claim-name> will be provided to the credential
"vc_subjectDid": "did:web:my.did.de",
// defines the claims to be included in the given credential type
"BatteryPassAuthCredential_claims": "email,firstName,roles,subjectDid"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"defaultClientScopes": [],
"optionalClientScopes": []
}
Once the client is created, roles and role-assignemnts can be managed the same way as for every other type, through the API or the Admin-Console.
Depending on the credentials type and the used template( see WaltId documentation), Keycloak can provide either static or dynamic values to the credential.
Static values can be configured on a "per-client"-base. Every client attribute, that is prefixed with "vc_" will be provided to the resource provider and can be used for the credential. F.e.:
{
...
attibutes: {
...
"vc_myFirstClaim": "first",
"vc_mySecondClaim": "second",
"CredentialType1_claims": "myFirstClaim",
"CredentialType2_claims": "myFirstClaim,mySecondClaim"
}
}
The configuration would provide the 2 claims myFirstClaim
and mySecondClaim
with the static
values first
and second
to the credentials provider. They are used in credentials that are configured to be
supported. For each credentials type, a comma-separated list of claim-names can be provided. They have to either
correspond to one of the statically configured one or need to be available as dynamic claims.
The Credential Provider supports some claims, filled by the user-attributes available:
email
- if an email address is configured for the user, it can be via the claimemail
firstName
- if the user has a firstName set, it can be via the claimfirstName
familyName
- if the user has a firstName set, it can be via the claimfamilyName
roles
- to use the credentials in authorization frameworks, roles can be provided to the credential.roles
will include all roles configured for the user in the given client
In order to issue credentials, first a SIOP-2 client has to be created. Integration in the admin-console is still open, thus has to be done through the api:
url --location --request POST '<KEYCLOAK_HOST>/realms/master/clients-registrations/SIOP-2' \
--header 'Authorization: Bearer <TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
// did of the client
"clientDid": "did:key:z6MkmxVQztpb1JpAEgfJaqFN5g7CcJFPSMsJ1S6PiBjxR7Vxp",
// type of the supported credentials
"supportedVCTypes": [
{
"type": "PacketDeliveryService",
"format": "ldp_vc"
},
{
"type": "PacketDeliveryService",
"format": "jwt_vc_json"
}
],
// 'traditional' description
"description": "Client to receive Verifiable Credentials.",
// max lifetime of the VC
"expiryInMin": 3600,
// additionalClaims to be added to the VC
"additionalClaims": {
"a":"b",
"c":"d"
}
}'
Client in the console:
Once the client is created, it is available in the admin-console. Through the standard interfaces, client-roles can be created and assigned to users. Once that is done, a logged in user can use the account-interface to get Verifiable Credentials:
Account-Console overview:
Get a VC:
The displayed QR provides a URI-formatted reference to the OIDC4VCI compatible credential-offer, that can be scanned and used by compliant wallets. For demonstrational purposes, the demo-wallet.fiware.dev can be used. It's browser based wallet, intended to be used in demo-scenarios, not suitable for real-world use-cases.
The example shows a "Cross-Device Flow", as described by OIDC4VCI. See the following diagramm for the detailed flow:
The unit-tests are located at src/test/java and are postfixed with Test
. The tests
use Junit5
and Mockito and will cover the essential logic inside the plugin.
Since the plugin has to work as part of Keycloak and does
use WaltId
as a downstream dependency, integration-test are essential. The tests use a k3s setup, integrated
through the k3s-maven-plugin, which provides a preconfigured Keycloak
and WaltId environment. The manifests can be found at src/test/k3s. The test implementations are
postfixed with IntegrationTest
. To run the full integration test suite, use the maven
profile integration-test
:
mvn clean install -Pintegration-test
This will automatically build and deploy the current development, bind selected services to localhost and run all integration-tests. To support local development, the same test-setup can be run locally via:
mvn clean install -Pdev
This will not tear-down the environement after the excution, thus can be used for testing/debugging from the IDE.
See pom.xml-Profile dev
-k3s-maven-plugin
for the available ports.
To ensure compatibility with released Keycloak-Versions, the integration-tests support exectuion with different
Keycloak-Versions. In order to execute the tests with a specific version, provide either the
property keycloak.version
(which has to be a valid tag from the official quay-image)
or keycloak.image
. Be aware that the configuration might differ for different builds of Keycloak, thus alternative
images might require some additional changes in the k3s-setup. The compatibility tests are executed as part of the
pipeline and additionally once every night. The results can be found
at the compatibility-matrix
Keycloak VC-Issuer is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.
© 2023 FIWARE Foundation e.V.