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
40 changes: 19 additions & 21 deletions docs/sirosid/issuers/api-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,19 +426,19 @@ apigw:

### Credential Offer Format

The deep link contains a credential offer with pre-authorized code grant:
The offer carries a pre-authorized code grant. `credential_configuration_ids` holds the credential **scope** (here `diploma`), and the offer is valid for 5 minutes. The `tx_code` object is present only when `enable_pin` is on:

```json
{
"credential_issuer": "https://issuer.example.org",
"credential_configuration_ids": ["urn:eudi:diploma:1"],
"credential_configuration_ids": ["diploma"],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "oaKazRN8I0IbtZ...",
"tx_code": {
"input_mode": "numeric",
"length": 6,
"description": "Enter the PIN sent to your email"
"description": "Enter the PIN provided by the issuer"
}
}
}
Expand All @@ -449,21 +449,9 @@ The deep link contains a credential offer with pre-authorized code grant:

## Revocation

Credential revocation is managed via [Token Status Lists](/sirosid/reference/token-status-lists). When a credential needs to be revoked, delete the underlying document and the issuer updates the status list entry:
Credential status is published as [Token Status Lists](/sirosid/reference/token-status-lists) by the registry service. Deleting a document through `DELETE /api/v1/datastore` removes the stored document so no further credentials can be issued from it, but it does **not** change the status of credentials that were already issued: apigw never calls the registry to update a status.

```bash
DELETE /api/v1/datastore
Content-Type: application/json
Authorization: Bearer <your-jwt-token>

{
"authentic_source": "hr.example.org",
"scope": "diploma",
"document_id": "diploma-2025-001234"
}
```

Verifiers checking the Token Status List will see the credential as revoked.
To revoke an issued credential, update its status entry in the registry, either through the registry admin GUI (`registry.admin_gui`, `POST /admin/status`) or by calling the registry's `TokenStatusListUpdateStatus` gRPC method directly. Verifiers checking the Token Status List then see the credential as revoked.

---

Expand All @@ -478,7 +466,11 @@ service IssuerService {
rpc MakeSDJWT (MakeSDJWTRequest) returns (MakeSDJWTReply) {}
rpc MakeMDoc (MakeMDocRequest) returns (MakeMDocReply) {}
rpc MakeVC20 (MakeVC20Request) returns (MakeVC20Reply) {}
rpc MakeJWP (MakeJWPRequest) returns (MakeJWPReply) {}
rpc JWKS (Empty) returns (JwksReply) {}
rpc SignMetadata (SignMetadataRequest) returns (SignMetadataReply) {}
rpc GetIACAs (Empty) returns (GetIACAsReply) {}
rpc Status (v1.status.StatusRequest) returns (v1.status.StatusReply) {}
}
```

Expand All @@ -499,22 +491,25 @@ message MakeSDJWTRequest {
```protobuf
message MakeMDocRequest {
string scope = 1; // Credential scope
string doc_type = 2; // e.g., "org.iso.18013.5.1.mDL"
reserved 2; // was: string doc_type; the doctype now comes from mddl
bytes document_data = 3; // JSON encoded mDL data
bytes device_public_key = 4; // CBOR encoded COSE_Key
string device_key_format = 5; // "cose", "jwk", or "x509"
bytes mddl = 6; // Raw MDDL (mso_mdoc) schema JSON bytes
}
```

### Connection

The generated Go stubs live under `internal/` in the [vc repository](https://github.com/SUNET/vc) and cannot be imported from other modules; generate your own client from `proto/v1-issuer.proto`. The gRPC server listens on `issuer.grpc_server.addr` (default `:8090`).

```go
import (
"google.golang.org/grpc"
pb "vc/internal/gen/issuer/apiv1_issuer"
pb "example.org/yourmodule/gen/issuer/apiv1_issuer" // generated from proto/v1-issuer.proto
)

conn, err := grpc.Dial("issuer.example.org:9090", grpc.WithTransportCredentials(creds))
conn, err := grpc.Dial("issuer.example.org:8090", grpc.WithTransportCredentials(creds))
client := pb.NewIssuerServiceClient(conn)

reply, err := client.MakeSDJWT(ctx, &pb.MakeSDJWTRequest{
Expand Down Expand Up @@ -604,6 +599,9 @@ apigw:
enable: true
issuer_url: "https://auth.example.com"
audience: "vc-issuer"
# client_id and redirect_uri are required whenever oidc.enable is true
client_id: "vc-apigw"
redirect_uri: "https://issuer.example.org/ui/callback"
```

When SPOCP rules are configured, each request is evaluated as a **six-part s-expression** (all six parts are mandatory and must appear in this exact order):
Expand Down Expand Up @@ -714,7 +712,7 @@ a string.
3. **mTLS for gRPC**: Use mutual TLS for the internal Issuer gRPC service (configurable via `grpc_server.tls`)
4. **Input Validation**: The issuer validates all document data against VCTM schemas
5. **Audit Logging**: All API operations are logged for compliance (configurable webhook destinations)
6. **Rate Limiting**: Implement rate limiting at the reverse proxy level to prevent abuse
6. **Rate Limiting**: apigw has built-in per-IP rate limiting (`apigw.rate_limit`, including `datastore_requests_per_minute`); add reverse-proxy limits on top if needed

---

Expand Down
96 changes: 43 additions & 53 deletions docs/sirosid/issuers/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Compatible wallets include:
- **SIROS ID Credential Manager** (based on [wwWallet](/opensource#wwwallet-project)) – used in examples throughout this documentation
- **EUDI Reference Wallet** – the EU Digital Identity reference implementation
- **Native mobile wallets** – iOS and Android applications with OID4VCI support
- **Third-party wallets** – any wallet implementing OID4VCI and supported formats (SD-JWT VC, mDL, JWT VC)
- **Third-party wallets** – any wallet implementing OID4VCI and supported formats (SD-JWT VC, mDL/mdoc, W3C VC 2.0)

:::tip Multi-Wallet Support
A single issuer deployment can issue credentials to multiple different wallet implementations. The OID4VCI protocol ensures consistent behavior across all compliant wallets.
Expand Down Expand Up @@ -116,7 +116,8 @@ The SIROS ID Issuer supports multiple credential formats to meet different use c
|--------|----------|----------|---------------------|
| **SD-JWT VC** | [IETF SD-JWT VC](https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/) | EU Digital Identity, general use | ✅ Yes |
| **mDL/mDoc** | [ISO 18013-5](https://www.iso.org/standard/69084.html) | Mobile driving licenses | ✅ Yes |
| **JWT VC** | [W3C VC Data Model](https://www.w3.org/TR/vc-data-model/) | Legacy systems | ❌ No |
| **W3C VC 2.0** (`ldp_vc`) | [W3C VC Data Model 2.0](https://www.w3.org/TR/vc-data-model-2.0/) with Data Integrity proofs | Linked-data ecosystems | Depends on cryptosuite |
| **JWP** (blind BBS) | [IETF JSON Web Proof](https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-proof/) | Unlinkable presentations | ✅ Yes |

:::tip Recommended Format
**SD-JWT VC** is the recommended format for new deployments. It provides selective disclosure (users can share only necessary claims) and is the format specified by the EU Digital Identity Wallet Architecture Reference Framework (ARF).
Expand All @@ -134,7 +135,7 @@ A **credential type** defines the schema and semantics of a credential. Each typ
```mermaid
graph LR
subgraph "Credential Type Definition (VCTM)"
VCT["VCT Identifier<br/>urn:eudi:pid:arf-1.8:1"]
VCT["VCT Identifier<br/>urn:eudi:pid:1"]
Claims["Claim Schema<br/>given_name, family_name, ..."]
Display["Display Rules<br/>Labels, logos, templates"]
end
Expand All @@ -155,9 +156,9 @@ Each credential type is defined by a **VCTM file** that specifies:

```json
{
"vct": "urn:eudi:pid:arf-1.8:1",
"vct": "urn:eudi:pid:1",
"name": "Person Identification Data",
"description": "EU Person Identification Data credential (ARF 1.8)",
"description": "EU Person Identification Data credential",
"display": [
{
"lang": "en-US",
Expand All @@ -178,24 +179,23 @@ Each credential type is defined by a **VCTM file** that specifies:
}
```

#### Built-in Credential Types
#### Shipped Credential Types

SIROS ID includes pre-configured types based on EU standards:
The [vc repository `metadata/` directory](https://github.com/SUNET/vc/tree/main/metadata) ships type metadata for common types, which you reference from `common.credential_metadata`:

| Type | VCT | Description |
|------|-----|-------------|
| **PID (ARF 1.5)** | `urn:eudi:pid:arf-1.5:1` | Person Identification Data (ARF 1.5) |
| **PID (ARF 1.8)** | `urn:eudi:pid:arf-1.8:1` | Person Identification Data (ARF 1.8+) |
| **EHIC** | `urn:eudi:ehic:1` | European Health Insurance Card |
| **PDA1** | `urn:eudi:pda1:1` | Portable Document A1 |
| **Diploma** | `urn:eudi:diploma:1` | Educational credentials |
| **ELM** | `urn:eudi:elm:1` | European Learning Model |
| **Microcredential** | `urn:eudi:micro_credential:1` | Short learning achievements |
| **OpenBadge** | `urn:eudi:openbadge_complete:1` | Open Badges 3.0 |
| Type | VCT / doctype | File |
|------|---------------|------|
| **PID** | `urn:eudi:pid:1` | `vctm_pid.json` |
| **EHIC** | `urn:eudi:ehic:1` | `vctm_ehic.json` |
| **PDA1** | `urn:eudi:pda1:1` | `vctm_pda1.json` |
| **Diploma** | `urn:eudi:diploma:1` | `vctm_diploma.json` |
| **ELM** | `urn:eudi:elm:1` | `vctm_elm.json` |
| **Microcredential** | `urn:eudi:micro_credential:1` | `vctm_microcredential.json` |
| **eduID** | `urn:credential:eduid:1` | `vctm_eduid.json` |
| **PID (mdoc)** | `eu.europa.ec.eudi.pid.1` | `pid_mdoc.mdoc.json` |
| **mDL (mdoc)** | `org.iso.18013.5.1.mDL` | `mdl.mdoc.json` |

:::note ARF Version Selection
SIROS ID supports both ARF 1.5 and ARF 1.8 PID schemas. Which one a scope uses is determined by the VCTM it points at in `common.credential_metadata`. The generic VCT `urn:eudi:pid:1` is accepted for compatibility but maps to a configured ARF version.
:::
Which schema a scope uses is determined by the VCTM (or MDDL) file it points at in `common.credential_metadata`.

### Building the Claim Set

Expand All @@ -208,7 +208,8 @@ configuration:
from and which auth provider identifies the user.
3. `apigw.auth_providers.<provider>.attribute_mapping` normalises the
provider's own attribute names to canonical claim names, with optional
transforms and defaults.
defaults. Mapping is a rename-and-presence step only; value
transformations are `derivations` on the scope (see below).

APIGW then validates the resulting claim set against the VCTM schema before
sending it to the issuer for signing.
Expand All @@ -217,7 +218,7 @@ sending it to the issuer for signing.
common:
credential_metadata:
pid:
vctm_file_path: "/metadata/vctm_pid_arf_1_8.json"
vctm_file_path: "/metadata/vctm_pid.json"
format: "dc+sd-jwt"

apigw:
Expand All @@ -234,20 +235,28 @@ apigw:
required: true
"urn:oid:0.9.2342.19200300.100.1.3":
claim: "email_address"
transform: "lowercase"

data_sources:
# The SAML assertion's claims ARE the credential data
assertion:
scopes:
pid:
auth_provider: saml
# Value transformations are derivations on the scope
derivations:
- lowercase:
input: email_address
```

Supported `transform` values are `lowercase`, `uppercase`, `trim`,
`country_alpha2`, `country_alpha3` and `yyyymmdd_to_iso`. `default:` supplies a
value when the attribute is absent, and `as_array: true` wraps a scalar in a
single-element array.
`default:` supplies a value when the attribute is absent, and `as_array: true`
wraps a scalar in a single-element array. Available derivation primitives are
`age_over_thresholds`, `lowercase`, `uppercase`, `trim`, `country_alpha2`,
`country_alpha3`, `yyyymmdd_to_iso`, `swamid_highest_assurance_level` and
`random`. Derivations are supported on `datastore`, `assertion`, `external_api`
and `presentation` scopes; each takes an `input` claim name and, where
applicable, an `output` (default: in place). See the
[VC Configuration Reference](/sirosid/reference/vc-configuration) for each
primitive's parameters.

:::note Removed configuration
Earlier releases had a top-level `credential_constructor` section. It no longer
Expand Down Expand Up @@ -309,32 +318,15 @@ flowchart TB
| **SAML SP** | Service Provider for SAML federations | SAML 2.0 |
| **OIDC RP** | Relying Party for OIDC providers | OpenID Connect |
| **Session Manager** | OAuth2 session and state management | OAuth 2.0 |
| **Issuer API** | Core credential operations | gRPC + REST |
| **Issuer API** | Core credential operations (called by apigw; the REST datastore API is served by apigw) | gRPC |
| **Credential Constructor** | Builds credentials from claims | Internal |
| **Signing Service** | Cryptographic signing (SW or HSM) | JWS, COSE |
| **Status Lists** | Revocation status tracking | Token Status List |
| **Audit Log** | Compliance and monitoring | Internal |

### Service Architecture

The issuer can run as a single process or as separate microservices:

<div className="row">
<div className="col col--6">

**Single Process Mode**

```mermaid
flowchart TB
Combined[apigw + issuer + registry]
```

All components in one binary—simplest deployment for development and small-scale production.

</div>
<div className="col col--6">

**Microservices Mode**
The issuer runs as three separate services (`apigw`, `issuer` and `registry`, each its own binary and container image) that talk gRPC to each other and share a MongoDB:

```mermaid
flowchart TB
Expand All @@ -343,13 +335,11 @@ flowchart TB
Registry[Registry Service]

APIGW -->|gRPC| Issuer
APIGW -->|gRPC| Registry
Issuer -->|gRPC| Registry
```

Separate services for independent scaling and high availability.

</div>
</div>
All three can run on one host (for example with Docker Compose, see [Deployment](./deployment)) or be scaled independently for high availability.

## Deployment Models

Expand Down Expand Up @@ -465,12 +455,12 @@ flowchart LR

### Model 4: Standalone Issuer

Minimal deployment for testing or air-gapped environments.
Minimal single-host deployment for testing or air-gapped environments: the same three services and MongoDB, run together on one machine.

```mermaid
flowchart LR
subgraph "Single Server"
All[Issuer + Registry<br/>Embedded MongoDB]
All[apigw + issuer + registry<br/>+ MongoDB]
end

TestWallet[Test Wallet]
Expand All @@ -479,7 +469,7 @@ flowchart LR

| Aspect | Details |
|--------|---------|
| **Setup** | Minutes – single Docker container |
| **Setup** | Minutes – one Docker Compose file |
| **Maintenance** | Minimal |
| **Data location** | Local |
| **Customization** | Development/testing |
Expand Down
Loading
Loading