Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenID4VCI Plugin #47

Merged
merged 99 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
d3f492e
feat: first of oid4vci plugin files
burdettadam Oct 10, 2023
7807042
BUG: non working docker
burdettadam Oct 10, 2023
ef66f37
BUG: non working docker
burdettadam Oct 10, 2023
01d1f5a
feat: working docker img
burdettadam Oct 10, 2023
e4e29c6
feat: loading plugin with status points
burdettadam Oct 10, 2023
6ff0b83
feat: empty handlers
burdettadam Oct 10, 2023
f004899
feat: some openapi schemas
burdettadam Oct 12, 2023
e6f9977
feat: add logic to get_cred_offer endpoint
cjhowland Oct 11, 2023
fd2687e
feat: create CredentialOfferRecord
cjhowland Oct 11, 2023
d2dfb90
feat: no head allowed on get's
burdettadam Oct 12, 2023
70d3631
feat: check jwt token
burdettadam Oct 16, 2023
ef939a3
feat: oid_cred_issuer logic (WIP)
cjhowland Oct 13, 2023
b900f5d
feat: mostly copied routes.py
burdettadam Oct 18, 2023
fec69bc
feat: demo directory
burdettadam Oct 19, 2023
d37f70a
feat: demo frontend with docker compose
burdettadam Oct 23, 2023
56ac10d
feat: demo docker compose with less bugs
burdettadam Oct 26, 2023
8ba849a
feat: cred ex record management
burdettadam Oct 27, 2023
2b1b3f6
chore: add pre-commit
dbluhm Oct 31, 2023
ea0024e
refactor: remove unused record
dbluhm Oct 31, 2023
87ded51
refactor: models into model package
dbluhm Oct 31, 2023
77411c9
refactor: minor tweaks to issuer meta endpoint
dbluhm Oct 31, 2023
68ce98a
refactor: move public routes to new module
dbluhm Oct 31, 2023
ad54db8
feat: more code
burdettadam Nov 2, 2023
f41e3f7
refactor: minor tweaks, draft 13 compliance
dbluhm Nov 2, 2023
2300277
feat: docker_compose with ngrok
burdettadam Nov 8, 2023
ab10e0b
fix: exchange record schema issues
dbluhm Nov 8, 2023
427c982
refactor: shorten the name of exchange record
dbluhm Nov 8, 2023
8c0ac8e
refactor: rename model module
dbluhm Nov 8, 2023
0376fcc
refactor: rename records a bit
dbluhm Nov 8, 2023
91d745c
refactor: rename model module
dbluhm Nov 8, 2023
0a3cbfb
refactor: adjust naming of record id
dbluhm Nov 8, 2023
9abcbc7
feat: less bugs
burdettadam Nov 8, 2023
426aee7
fix: record value after save
dbluhm Nov 8, 2023
5afcad5
feat: add initial framework for testing against afj
dbluhm Nov 9, 2023
dfc8e03
feat: message buffering and TCP
dbluhm Nov 9, 2023
2fb6612
refactor: rearranging
dbluhm Nov 9, 2023
06fa2d7
feat: add docker-compose setup and test
dbluhm Nov 9, 2023
b05edc4
feat: separate pieces into reusable chunks
dbluhm Nov 9, 2023
b55d3fa
chore: add jsdocs
dbluhm Nov 9, 2023
b5a6ef7
chore: use remote json-rpc-api-proxy lib
dbluhm Nov 14, 2023
779ab0f
feat: more bugs with mt tests
burdettadam Nov 14, 2023
c2d2c24
feat: changes to records
burdettadam Nov 14, 2023
ed084a4
feat: broken token issueance
burdettadam Nov 15, 2023
514e2a9
refactor: code review and draft 11 vs 13
dbluhm Nov 15, 2023
80568f5
feat: issue token untested handler
burdettadam Nov 15, 2023
586f38a
feat: multiple id bug fix
burdettadam Nov 15, 2023
150f8bd
feat: meta sup id bug
burdettadam Nov 16, 2023
c4d2a95
fix: draft 11 metadata structure
dbluhm Nov 16, 2023
459d40c
fix: extract identifier from cred sup create
dbluhm Nov 16, 2023
7bc634b
test: add tests for routes
dbluhm Nov 16, 2023
ce72f0f
fix: display is a list of objects
dbluhm Nov 16, 2023
5f1c452
fix: token endpoint
burdettadam Nov 17, 2023
262d2ea
feat: token endpoint issues jwt
burdettadam Nov 17, 2023
e76f390
fix: minor cleanup
dbluhm Nov 19, 2023
1d15b03
feat: integration test anticipated changes
dbluhm Nov 19, 2023
ef98007
chore: remove sphereon int
dbluhm Nov 19, 2023
1914973
test: implement client for int tests
dbluhm Nov 19, 2023
285c640
feat: credential endpoint code
burdettadam Nov 20, 2023
0907e48
feat: more credential endpoint code
burdettadam Nov 20, 2023
49a84ba
feat: more work on issuance
dbluhm Nov 20, 2023
89226fa
feat: more credential endpoint code for front end
burdettadam Nov 20, 2023
734fed1
feat: more credential endpoint code for manual testing
burdettadam Nov 21, 2023
148fbe3
feat: issue credential endpoint with app succuss
burdettadam Nov 21, 2023
cea60d4
fix: removed some dead code
burdettadam Nov 22, 2023
14197ec
refactor: amended remove dead code
dbluhm Nov 23, 2023
0eab578
feat: add config object
dbluhm Nov 23, 2023
2733176
refactor: rearrange plugin structure slightly
dbluhm Nov 26, 2023
e6b97b6
refactor: refine configuration object
dbluhm Nov 26, 2023
f6a4a71
refactor: further refine config object; errors
dbluhm Nov 26, 2023
4f38e50
refactor: minimize oid4vci server responsibilities
dbluhm Nov 26, 2023
ef51f8d
chore: small clean up in public routes
dbluhm Nov 26, 2023
69998da
refactor: minor rearrangement of route models
dbluhm Nov 26, 2023
9b9aaf3
refactor: refine record listing
dbluhm Nov 27, 2023
e5e153f
refactor: refine admin endpoints further
dbluhm Nov 27, 2023
1d1c538
feat: implement supported credential deletion
dbluhm Nov 27, 2023
c7b49b7
feat: implement pin support and token expiry
dbluhm Nov 27, 2023
a0695f9
feat: add did:jwk resolver
dbluhm Nov 27, 2023
9768bd5
feat: implement proof of possession verification
dbluhm Nov 27, 2023
f8b7a4e
fix: save record state on offer created
dbluhm Nov 27, 2023
1c5bbcf
fix: failing tests, missing config
dbluhm Nov 27, 2023
5415ce7
fix: types checking, issues in int tests
dbluhm Nov 27, 2023
f39fa3b
fix: signature is b64url encoded
dbluhm Nov 27, 2023
e106f50
test: es256k pop
dbluhm Nov 27, 2023
75af405
fix: small fixes
dbluhm Nov 29, 2023
e6e2a8a
chore: bump askar, acapy versions
dbluhm Nov 29, 2023
40b6780
fix: add registration admin page
burdettadam Nov 29, 2023
794a135
fix: remove credential page, fix get by id
burdettadam Nov 30, 2023
11461cf
fix: cleaner demo code with async
burdettadam Nov 30, 2023
a0b3d22
fix: vars names
burdettadam Nov 30, 2023
a807f21
feat: better error reporting
burdettadam Nov 30, 2023
76f64de
feat: webpage name change
burdettadam Dec 1, 2023
68319b2
feat: update readme
dbluhm Dec 1, 2023
5e16ca5
fix: add readme arch image
dbluhm Dec 1, 2023
07d3ecc
chore: rearrange int
dbluhm Dec 1, 2023
78f591a
fix: pytest opts to only run from tests dir
dbluhm Dec 1, 2023
4a738b3
chore: remove copy-paste error
dbluhm Dec 1, 2023
1923a5e
fix: unit tests
dbluhm Dec 4, 2023
7f3cefd
feat: update aca-py as optional dep
dbluhm Dec 5, 2023
a72a951
chore: bump node version
dbluhm Dec 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional']
};
21 changes: 21 additions & 0 deletions oid4vci/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
repos:
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.5.0
hooks:
- id: commitlint
stages: [commit-msg]
args: ["--config", "./.commitlint.config.js"]
additional_dependencies: ['@commitlint/config-conventional']
- repo: https://github.com/psf/black
rev: 23.7.0
hooks:
- id: black
stages: [commit]
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.285
hooks:
- id: ruff
stages: [commit]
args: [--fix, --exit-non-zero-on-fix]
332 changes: 332 additions & 0 deletions oid4vci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,332 @@
# OpenID4VCI Plugin for ACA-Py

This plugin implements [OpenID4VCI (Draft 11)][oid4vci]. The OpenID4VCI specification is in active development, as is this plugin. Consider this plugin experimental; endpoints and records may change to reflect upstream changes in the specification.

## OpenID4VCI Plugin Demo with Sphereon Wallet

### Demo Overview

This repository showcases a simplified demonstration of the OID4VCI (OpenID for Verifiable Credential Issuers) integration with the [Sphereon Wallet app](https://github.com/Sphereon-Opensource/ssi-mobile-wallet). Follow the steps below to run the demo successfully.

### Prerequisites

- Sphereon Wallet App on your mobile device
- Docker + Docker Compose

### Steps to Run the Demo

```shell
cd oid4vci/demo
docker compose build
docker compose up
docker compose down -v # Clean up
```

If you're using Apple Silicon, you may have to separately build the image with the appropriate platform flag (from the `demo` directory):

```sh
$ DOCKER_DEFAULT_PLATFORM=linux/amd64 docker build -f ../docker/Dockerfile --tag oid4vci ..
```

### Demo Flow

Navigate to `http://localhost:3002` in your browser. You will start at the registration page.

#### 1. Admin Registration Page
- Demonstrates the issuer's process of registering a new OID4VCI credential type.
- Utilizes the admin API to create a supported credential record for issuance tracking.
- In a production environment, this process is dynamic, but for the demo, it's simplified to a single button click.

#### 2. Input Form Page
- Illustrates the user's initiation of an interaction with an issuer to request a credential.
- The data submited here will end up in the issued credential.

#### 3. Credential Offer Page
- Presents a credential offer in the form of a QR code.
- The Input Form Page uses the admin API to create an exchange record, tracking user information, OID4VCI token, codes, pins, and credential subjects.
- Scan the QR code using the Sphereon Wallet app.
- The Sphereon Wallet follows the OID4VCI flow, requesting an authentication token and using it to obtain a credential.
- The OID4VCI plugin determines the credential subjects based on the exchange record.

### Note
In a production environment, the described processes would be more dynamic and involve additional security measures. This demo provides a streamlined representation for clarity and ease of understanding.

## Architecture

![oid4vci-component](docs/images/oid4vci-component.png)

### Public Routes
ACA-Py provides a pluggable mechanism for registering routes for consumption by the controller, the Admin API. This Admin Server makes it trivial to extend the controller from plugins. ACA-Py does not, however, provide a similar mechanism for publicly accessible HTTP Endpoints. Generally speaking, the only public endpoint ACA-Py provides is the DIDComm Messaging endpoint. The OpenID4VCI protocol requires endpoints that are publicly accessible to present and exchange tokens for credentials. This Plugin accomplishes this by starting a separate [aiohttp server](https://docs.aiohttp.org/en/stable/index.html) (similar to how the Admin server is separate from the DIDComm messaging server) and publishes the OpenID4VCI endpoints through this separate server.

Details of the endpoints can be found at `/api/docs` or in the [OpenId4VCI Specification][oid4vci].

### Admin Routes
The plugin exposes Admin API routes for consumption by the Controller to facilitate Credential Issuance over OpenID4VCI. The Admin API Routes can be found under `/api/docs` of the Admin Server in the `oid4vci` section.

### Records
The plugin adds two records to acapy, `OID4VCIExchangeRecord` and `SupportedCredential`. The exchange record keeps track of user data use during the exchange. The supported credential record keeps track of information a issuer needs to issue a credential.

### How it works

It is the Controller's responsibility to prepare Credential Issuer Metadata, collect and record details about the credential subject, (optionally) generate and deliver a User PIN to the holder out of band, and to generate and present the credential offer to the holder.

```mermaid
sequenceDiagram
autonumber

actor alice as Alice
participant holder as Wallet
participant controller as Controller
box OpenID4VCI Plugin
participant public as Public Routes
participant admin as Admin Routes
end
participant acapy as ACA-Py Core

controller ->> admin: POST /oid4vci/credential-supported/create
admin -->> controller: created (supported_cred_id)
controller ->> admin: POST /wallet/did/create
admin -->> controller: created (did)
alice -> controller: Open URL in browser with form
alice ->> controller: Submit form
activate controller
controller ->> admin: POST /oid4vci/exchange/create (did, supported_cred_id, credential subject)
admin -->> controller: created (record)
alt PIN required
controller -->> alice: Deliver PIN Out of Band (email, sms, etc.)
end
controller ->> admin: GET /oid4vci/credential-offer (exchange id, pin)
admin -->> controller: credential offer
controller ->> alice: redirects alice to a page with cred offer
deactivate controller
alice ->> holder: Scan cred offer
holder --> controller: scanned cred offer (pre-auth code)
holder ->> holder: reads offer, extracts issuer
holder ->> public: Retrieve issuer metadata
public -->> holder: issuer metadata
holder ->> public: token request (pre-auth code)
public ->> holder: token response (access token)
holder ->> public: credential request (access token)
activate public
public ->> acapy: Verify Proof of Possession
acapy -->> public: Verified
public ->> acapy: Recall cred values
public ->> acapy: jwt sign
acapy -->> public: signed cred
public ->> acapy: store exchange result
public ->> holder: credential response
deactivate public
admin ->> controller: POST /topic/oid4vci (issued)
loop alice polling for exchange status
alice ->> controller: get exchange status
controller -->> alice: status
alt complete
controller ->> alice: redirect to success page
end
end
```


## Usage
### Configuration

The Plugin expects the following configuration options. These options can either be set by environment variable (`OID4VCI_*`) or by plugin config value (`-o oid4vci.*`).

- `OID4VCI_HOST` or `oid4vci.host`
- Host used for the OpenID4VCI public server
- `OID4VCI_PORT` or `oid4vci.port`
- Port used for the OpenID4VCI public server
- `OID4VCI_ENDPOINT` or `oid4vci.endpoint`
- `credential_issuer` endpoint, seen in the Credential Offer

### Creating Supported Credential Records

To issue a credential using OpenID4VCI, the Issuer must first prepare credential issuer metadata including which credentials the Issuer can issue. Below is an example payload to the `POST /oid4vci/credential-supported/create` endpoint:

```json
{
"cryptographic_binding_methods_supported": [
"did"
],
"cryptographic_suites_supported": [
"ES256K"
],
"display": [
{
"name": "University Credential",
"locale": "en-US",
"logo": {
"url": "https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png",
"alt_text": "a square logo of a university"
},
"background_color": "#12107C",
"text_color": "#FFFFFF"
}
],
"format": "jwt_vc_json",
"format_data": {
"credentialSubject": {
"degree": {},
"given_name": {
"display": [
{
"name": "Given Name",
"locale": "en-US"
}
]
},
"gpa": {
"display": [
{
"name": "GPA"
}
]
},
"last_name": {
"display": [
{
"name": "Surname",
"locale": "en-US"
}
]
}
},
"types": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
},
"id": "UniversityDegreeCredential",
"vc_additional_data": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
}
}
```

For the `id`, `format`, `cryptographic_binding_supported`, `cryptographic_suites_supported`, and `display` attributes, see the [OpenID4VCI Specification, Section 10.2.3][oid4vci].

- `format_data`: This attribute represents data specific to a given credential format. In this Supported Credential, which is of format `jwt_vc_json`, this includes `types` (required for JWT-VC) and `credentialSubject` (which represents display characteristics of the credential only and is not an exhaustive list of the credential attributes). These values are reported in the credential issuer metadata.
- `vc_additional_data`: This attribute represents data that is included in all credentials of this type. In this Supported Credential, this includes the `@context` of credential to be issued as well as the `type`. These values are NOT reported in the credential issuer metadata.

When the Controller sets up a Supported Credential record using the Admin API, the holder, upon requesting Credential Issuer Metadata, will receive the following information in response:

```json
{
"credential_issuer": "https://e116-198-91-62-58.ngrok.io/",
"credential_endpoint": "https://e116-198-91-62-58.ngrok.io/credential",
"credentials_supported": [
{
"format": "jwt_vc_json",
"cryptographic_binding_methods_supported": [
"did"
],
"cryptographic_suites_supported": [
"ES256K"
],
"display": [
{
"name": "University Credential",
"locale": "en-US",
"logo": {
"url": "https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png",
"alt_text": "a square logo of a university"
},
"background_color": "#12107c",
"text_color": "#FFFFFF"
}
],
"id": "UniversityDegreeCredential",
"credentialSubject": {
"degree": {},
"given_name": {
"display": [
{
"name": "Given Name",
"locale": "en-US"
}
]
},
"gpa": {
"display": [
{
"name": "GPA"
}
]
},
"last_name": {
"display": [
{
"name": "Surname",
"locale": "en-US"
}
]
}
},
"types": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
}
]
}
```

## Contributing

This project is managed using Poetry. To get started:

```shell
poetry install
poetry run pre-commit install
poetry run pre-commit install --hook-type commit-msg
```

> TODO: Pre-commit should move to the repo root

### Unit Tests

To run unit tests:

```shell
# Run only unit tests; leaving off the directory will attempt to run integration tests
poetry run pytest tests/
```

### Integration Tests

This plugin includes two sets of integration tests:

- Tests against a minimal OpenID4VCI Client written in Python
- Tests against AFJ + OpenID4VCI Client Package (not complete!)

AFJ has an active PR working on adding support for Draft 11 version of the OpenID4VCI specification. Until that PR is in and available in a release, these tests are incomplete and ignored.

To run the integration tests:

```shell
cd oid4vci/int
docker compose build
docker compose run tests
docker compose down -v # Clean up
```

For Apple Silicon, the `DOCKER_DEFAULT_PLATFORM=linux/amd64` environment variable will be required.

## Not Implemented

- `ldp_vc`, `sd_jwt_vc`
- Authorization Code Flow
- Only signature suite supported by ACA-Py for jwt-vc right now is `EdDSA`
- GET /.well-known/openid-configuration
- GET /.well-known/oauth-authorization-server
- Batch Credential Issuance
- We're limited to DID Methods that ACA-Py supports for issuance (more can be added by Plugin, e.g. DID Web); `did:sov`, `did:key`

[oid4vci]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-11.html
Loading
Loading