Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,4 @@ settings.json
# Edit the file setupDocs.sh to edit the docs, run to generate
/docs
mkdocs.yml
*.code-workspace
49 changes: 49 additions & 0 deletions oid4vc/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
.venv/
venv/
ENV/
.pytest_cache/
.mypy_cache/
.ruff_cache/
*.egg-info/
dist/
build/
.eggs/

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# Test outputs
.test-reports/
test_results/
test_data/
htmlcov/
.coverage
coverage.xml

# Development
.dev/
.devcontainer/
demo/
devtools/
docs/

# Git
.git/
.gitignore

# Docker (don't need to copy these into image)
docker-compose*.yml
Dockerfile*

# Pre-commit
.pre-commit-config.yaml
46 changes: 46 additions & 0 deletions oid4vc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Local development tools - do not commit
.dev/

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# UV
.venv/
uv.lock

# UniFFI generated bindings (now use GitHub package)
**/uniffi_scratch/

# IDE
.vscode/
.idea/
*.code-workspace
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Test Results
test-results/
playwright-report/
44 changes: 32 additions & 12 deletions oid4vc/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
# OpenID4VCI Plugin for ACA-Py

This plugin implements [OpenID4VCI v1.0][oid4vci].
This plugin implements [OpenID4VCI 1.0][oid4vci]. This implementation follows the OpenID4VCI 1.0 final specification and is not backwards compatible with earlier drafts.

> [!WARNING]
> This plugin is under active development.
> Treat it as experimental; endpoints and records may change as implementation details evolve.
## Developer Documentation

| Document | Description |
|---|---|
| [Getting Started](docs/getting-started.md) | Prerequisites, installation, configuration, Docker quick-start |
| [Architecture](docs/architecture.md) | Two-server design, plugin lifecycle, credential format registry |
| [Admin API Reference](docs/admin-api-reference.md) | All `/oid4vci/*`, `/oid4vp/*`, `/mso_mdoc/*`, `/did/*` endpoints |
| [Public API Reference](docs/public-api-reference.md) | OID4VCI/OID4VP wallet-facing endpoints |
| [Issuance Cookbook](docs/cookbook-issuance.md) | Step-by-step curl examples for `jwt_vc_json`, `sd_jwt_vc`, `mso_mdoc` |
| [Verification Cookbook](docs/cookbook-verification.md) | PEX and DCQL-based VP flows with curl examples |
| [Credential Formats](docs/credential-formats.md) | Format-specific schema details, selective disclosure, mDOC namespaces |
| [Troubleshooting](docs/troubleshooting.md) | Error codes, common failures, debugging tips |

The plugin's admin endpoints appear automatically in the ACA-Py Swagger UI at `http://<admin-host>:<admin-port>/api/doc` under the `oid4vci`, `oid4vp`, `mso_mdoc`, and `did` tag groups.

## OpenID4VCI Plugin Demo with Sphereon Wallet

Expand Down Expand Up @@ -33,7 +44,7 @@ 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 oid4vc ..
DOCKER_DEFAULT_PLATFORM=linux/amd64 docker build -f docker/Dockerfile --tag oid4vc ../..
```

### Demo Flow
Expand Down Expand Up @@ -80,7 +91,7 @@ Documentation for the [Status List Plugin] (https://github.com/openwallet-founda
```
OID4VCI_STATUS_HANDLER: status_list.v1_0.status_handler
STATUS_LIST_SIZE: 131072
STATUS_LIST_SHARD_SIZE: 131072
STATUS_LIST_SHARD_SIZE: 1024
STATUS_LIST_PUBLIC_URI: https://localhost:8082/tenant/{tenant_id}/status/{list_number}
STATUS_LIST_FILE_PATH: /tmp/bitstring/{tenant_id}/{list_number}
```
Expand All @@ -94,8 +105,8 @@ Documentation for the [Status List Plugin] (https://github.com/openwallet-founda
```
{
"issuer_did": "did....",
"list_type": "w3c",
"list_size": 131072,
"list_type": "ietf",
"shard_size": 1024,
"status_message": [
{
Expand All @@ -107,7 +118,7 @@ Documentation for the [Status List Plugin] (https://github.com/openwallet-founda
"message": "revoked"
},
],
"status_purpose": "revocation",
"status_purpose": "message",
"status_size": 1,
"supported_cred_id": "string",
"verification_method": "did...."
Expand Down Expand Up @@ -371,20 +382,29 @@ When the Controller sets up a Supported Credential record using the Admin API, t
This project is managed using Poetry. To get started:

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

> TODO: Pre-commit should move to the repo root
#### Installing mso_mdoc Dependencies (Optional)

The `mso_mdoc` module requires the `isomdl-uniffi` library, which needs Rust to compile.

**⚠️ Note**: Automated installation through Poetry/pip doesn't currently work due to build system limitations.

For manual installation instructions, see [mso_mdoc/README.md](mso_mdoc/README.md).

### 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/
# Run all tests except mso_mdoc (which requires isomdl-uniffi)
poetry run pytest jwt_vc_json/tests/ oid4vc/tests/ sd_jwt_vc/tests/

# Or run all tests including mso_mdoc (requires isomdl-uniffi installed)
poetry run pytest
```

### Integration Tests
Expand Down
11 changes: 9 additions & 2 deletions oid4vc/jwt_vc_json/cred_processor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Issue a jwt_vc_json credential."""

import base64
import datetime
import json
import logging
import uuid
from typing import Any
Expand Down Expand Up @@ -47,8 +49,13 @@ async def issue(
if pop.holder_kid and pop.holder_kid.startswith("did:"):
subject = DIDUrl(pop.holder_kid).did
elif pop.holder_jwk:
# TODO implement this
raise ValueError("Unsupported pop holder value")
# JWK binding (RFC 7517): derive subject as did:jwk
# Wallets like Credo use JWK binding when no DID is available
jwk_str = json.dumps(pop.holder_jwk, separators=(",", ":"), sort_keys=True)
subject = (
"did:jwk:"
+ base64.urlsafe_b64encode(jwk_str.encode()).rstrip(b"=").decode()
)
else:
raise ValueError("Unsupported pop holder value")

Expand Down
Loading
Loading