# Signing with Sphereon Wallet
### Links
* Tools
* Base64 decoding: https://www.base64decode.org/
* URL encoding: https://www.url-encode-decode.com/
* JWT decoding: https://jwt.io/
* Sphereon Repos
* https://github.com/Sphereon-Opensource/mobile-wallet
* https://github.com/Sphereon-Opensource/OID4VCI/tree/feature/CWALL-174_impl-draft13_with-199
* OpenID Specs
* Overview: https://openid.net/sg/openid4vc/specifications/
* SIOPv2: [Editors Draft](https://openid.github.io/SIOPv2/openid-connect-self-issued-v2-wg-draft.html)
* OID4VCI: [Editors Draft](https://openid.github.io/OpenID4VCI/openid-4-verifiable-credential-issuance-wg-draft.html)
* OID4VP: [Editors Draft](https://openid.github.io/OpenID4VP/openid-4-verifiable-presentations-wg-draft.html)
* Other
* https://www.authlete.com/developers/oid4vci/
## Details
* Issuer DID: [`did:oyd:zQmYSydHP5A1nRuqMcAoxpb971mfJrKJxpGJPEsxc5mw5Wt`](https://dev.uniresolver.io/#did:oyd:zQmYSydHP5A1nRuqMcAoxpb971mfJrKJxpGJPEsxc5mw5Wt)
public key: `zHdefgtaw8fAaf31sU14bC9nn1mnVTdUj7D4nw4MsAx9r`
private key: `z1S5REkWJmkWk6LmsJsucUwiVeTcPEZf6s9ACrLarLDo4j12`
* Issuer Host: https://signing-demo.data-container.net/signing
## Sequence
### 1. **User scans QR code**
```
openid-credential-offer://?credential_offer=URL_encode(payload.to_json)
```
payload:
```json
{
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": SecureRandom.urlsafe_base64,
"user_pin_required": false
}
},
"credentials": [ "OydCredential" ],
"credential_issuer": "https://signing-demo.data-container.net/oyd-sign"
}
```
### 2. **Wallet queries Credential Information**
`GET credential_issuer + /.well-known/openid-credential-issuer`
here: https://signing-demo.data-container.net/oyd-sign/.well-known/openid-credential-issuer
We add a new object `credential_subject_issuance` to both the authorization request and the credential issuer metadata. The metadata version is only there to signal to a wallet beforehand that it will request a signed/issued credential from the wallet. The auth request ensures the wallet will get the information. This object contains 2 parameters for now:
`subject_proof_mode`, with values: `proof_chain`, `proof_set`, `proof_replace`. If not specified it will behave like the default, meaning the wallet should not attempt to sign.
Otherwise these are the values (we will only implement `replace` for now):
* `proof_chain`: Add an additional signature over the whole issued VC, including existing signature(s). Throw an error in case of a (SD-)JWT as that can only have one signature
* `proof_set`: Add a signature over the crdential minus the existing signature(s). Throw an error in case of a (SD-)JWT as that can only have one signature.
* `proof_replace`: Replace the signature. Strip of any existing signature(s) and provide a new signature (this is the only valid value for a (SD-)JWT).
Add another new optional array param called `notification_events_supported`, with zero or more values. Not providing this parameter means the wallet can decide what to do. If the parameter is provided the wallet can choose from the options provided, or if it doesn't like the options, return an error using the notication endpoint:
* `credential_accepted_holder_signed`: Asks the wallet to store the credential after signing similar like the `credential_accepted` in the VCI specification. The newly signed credential will be added to the `credential_accepted_holder_signed` event the wallet will send to the Issuer (see last step of this document)
* `credential_deleted_holder_signed`: Ask the wallet to not store the credential after signing. Similar to the `credential_deleted` event of the VCI specification. The newly signed credential will be added to the `credential_deleted_holder_signed` event the wallet will send to the Issuer (see last step of this document). Note this is not an error case. It simply means the holder did sign the credential, but decided not to store the credential in their wallet.
* `credential_accepted`: Asks the wallet/holder to sign, but do not return the signed credential to the issuer as a result
The wallet will interpret the above values and act accordingly in its UI.
The issuer should store the above values in its session, or get them from the metadata so it can expect a rerutned result. The issuer should only expect one of the events listed above, indicating a success with a holder signed credential.
Please note that the event is at the very end of the protocol. The issuer can do nothing when the wallet acts differently from the allowed values, except to flag/log it internally. A user could alway decide to not sign at all. In that case the issuer would get the regular `credential_deleted` event.
Response:
```json
{
"credential_issuer": "https://signing-demo.data-container.net/oyd-sign",
"credential_endpoint": "https://signing-demo.data-container.net/oyd-sign/credentials",
"token_endpoint": "https://signing-demo.data-container.net/oyd-sign/token",
"notification_endpoint": "https://signing-demo.data-container.net/oyd-sign/notification",
"display": [
{
"name": "OwnYourData",
"description": "OYD Signing Demo"
}
],
"credentials_supported": [
{
"display": [
{
"name": "Signging Demo",
"description": "demonstrate signing with Sphereon Wallet",
"text_color": "#FFFFFF",
"background_color": "#000000",
"logo":{
"url": "https://www.ownyourdata.eu/wp-content/uploads/2021/01/oyd_logo_transparent.png",
"alt_text": "OYD logo"
}
}
],
"id": "OydCredentialJwt",
"types": [
"VerifiableCredential",
"OydCredential"
],
"format": "jwt_vc_json",
"cryptographic_binding_methods_supported": [
"did:oyd"
],
"cryptographic_suites_supported": [
"EdDSA"
],
"credential_subject_issuance": {
"subject_proof_mode": "proof_replace",
"notification_events_supported": [
"credential_deleted_holder_signed"
]
}
}
]
}
```
possible values for `credential_subject_issuance`:
```json
"credential_subject_issuance": {
"subject_proof_mode": "proof_replace",
"notification_events_supported": [
"credential_accepted_holder_signed", // wallet should return credential and store
"credential_deleted_holder_signed", // wallet should return credential and not store
"credential_accepted" // wallet
]
}
```
### 3. **Wallet requests Bearer Token**
`POST credential_issuer + /token`
here: https://signing-demo.data-container.net/oyd-sign/token
Body: use `pre-authorized_code` from step #1
Response:
```json
{
"access_token": JWT.encode(payload, secp256k1 key, "ES256K", {alg: "ES256k", typ: "JWT"})
"token_type": "bearer",
"expires_in": 86400,
"c_nonce": SecureRandom.alphanumeric(10),
"c_nonce_expires_in": 86400
}
```
payload:
```json
payload = {
"iat": Time.now.utc.to_i,
"exp": 300,
"iss": "https://signing-demo.data-container.net/oyd-sign",
"preAuthorizedCode": preauth_code
}
```
### 4. **Wallet requests Verifiable Credential**
`POST credential_issuer + /credentials`
here: https://signing-demo.data-container.net/oyd-sign/credentials
Body: c_nonce (in jwt) from step #3
as well as the credential_subject_issuance value from the metadata
```json
{
...
"credential_subject_issuance": {
"subject_proof_mode": "proof_replace",
"notification_events_supported": [
"credential_deleted_holder_signed"
]
}
}
```
We include the `credential_subject_issuance` object in the response, so the Issuer knows the wallet actually supports (re)signing the credential presented. This allows the issuer to make a choice at this point if the wallet would not support signing the VC.
Response:
```json
{
"format": "ldp_vc",
"credential": JWT.encode(payload, private_key_issuer, "EdDSA", {alg: "EdDSA", kid: DID, typ: "JWT"}),
"c_nonce": nonce,
"c_nonce_expires_in": 86400,
"notification_id": "random_string",
"credential_subject_issuance": {
"subject_proof_mode": "proof_replace",
"notification_events_supported": [
"credential_deleted_holder_signed"
]
}
}
```
payload: Verifiable Credential
The `credential_subject_issuance` is included in the response, to signing the wallet it should sign the credential. If this value is ommited in the response, but present in the metadata it means the wallet should **not** sign the VC.
### 5. **Wallet sends to Notification Endpoint**
`POST credential_issuer + /oyd-sign/notification`
here: https://signing-demo.data-container.net/oyd-sign/notification
(Spec: [Notification Endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-notification-endpoint))
Event: Credential signed
Response:
```json
{
"notification_id": "string",
"credential": "JWT-encoded-string",
"event":"credential_deleted_holder_signed"
}
```