Skip to content

Commit

Permalink
converts Status List 2021 to Bitstring Status List; fixes tests; adds…
Browse files Browse the repository at this point in the history
… back ENABLE_HTTPS_FOR_DEV environment variable
  • Loading branch information
kezike committed Apr 23, 2024
1 parent 44661f2 commit e846573
Show file tree
Hide file tree
Showing 16 changed files with 315 additions and 166 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ CRED_STATUS_SERVICE=github
CRED_STATUS_DID_SEED=z1AackbUm8U69ohKnihoRRFkXcXJd4Ra1PkAboQ2ZRy1ngB
PORT=4008 # default port is 4008
ENABLE_ACCESS_LOGGING=true
ENABLE_HTTPS_FOR_DEV=false
ERROR_LOG_FILE=logs/error.log
ALL_LOG_FILE=logs/all.log
CONSOLE_LOG_LEVEL=silly # default is silly, i.e. log everything - see the README for allowed levels
Expand Down
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ IMPORTANT NOTE ABOUT VERSIONING: If you are using a Docker Hub image of this rep
- [DID Registries](#did-registries)
- [Usage](#usage)
- [Allocate Status Position](#allocate-status-position)
- [Revoke](#revoke)
- [Revocation and suspension](#revocation-and-suspension)
- [Versioning](#versioning)
- [Logging](#logging)
- [Log Levels](#log-levels)
Expand Down Expand Up @@ -49,6 +49,7 @@ This service provides support for managing credential status in a variety of Git
| `CRED_STATUS_DID_SEED` | seed used to deterministically generate DID | string | yes |
| `PORT` | HTTP port on which to run the express app | number | no (default: `4008`) |
| `ENABLE_ACCESS_LOGGING` | whether to enable access logging (see [Logging](#logging)) | boolean | no (default: `true`) |
| `ENABLE_HTTPS_FOR_DEV` | whether to enable HTTPS in a development instance of the app | boolean | no (default: `true`) |
| `ERROR_LOG_FILE` | log file for all errors (see [Logging](#logging)) | string | no |
| `ALL_LOG_FILE` | log file for everything (see [Logging](#logging)) | string | no |
| `CONSOLE_LOG_LEVEL` | console log level (see [Logging](#logging)) | `error` \| `warn`\| `info` \| `http` \| `verbose` \| `debug` \| `silly` | no (default: `silly`) |
Expand Down Expand Up @@ -89,7 +90,7 @@ curl --location 'http://localhost:4008/credentials/status/allocate' \
--header 'Content-Type: application/json' \
--data-raw '{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json"
],
"id": "urn:uuid:2fe53dc9-b2ec-4939-9b2c-0d00f6663b6c",
Expand All @@ -107,7 +108,7 @@ curl --location 'http://localhost:4008/credentials/status/allocate' \
"url": "https://dcconsortium.org",
"image": "https://user-images.githubusercontent.com/752326/230469660-8f80d264-eccf-4edd-8e50-ea634d407778.png"
},
"issuanceDate": "2023-08-02T17:43:32.903Z",
"validFrom": "2023-08-02T17:43:32.903Z",
"credentialSubject": {
"type": [
"AchievementSubject"
Expand Down Expand Up @@ -139,10 +140,8 @@ This should return the same credential but with an allocated status. It should l
```
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json",
"https://w3id.org/vc/status-list/2021/v1",
"https://w3id.org/security/suites/ed25519-2020/v1"
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json"
],
"id": "urn:uuid:2fe53dc9-b2ec-4939-9b2c-0d00f6663b6c",
"type": [
Expand All @@ -159,7 +158,7 @@ This should return the same credential but with an allocated status. It should l
"url": "https://dcconsortium.org",
"image": "https://user-images.githubusercontent.com/752326/230469660-8f80d264-eccf-4edd-8e50-ea634d407778.png"
},
"issuanceDate": "2023-08-02T17:43:32.903Z",
"validFrom": "2023-08-02T17:43:32.903Z",
"credentialSubject": {
"type": [
"AchievementSubject"
Expand All @@ -185,7 +184,7 @@ This should return the same credential but with an allocated status. It should l
},
"credentialStatus": {
"id": "https://jchartrand.github.io/status-test-three/DKSPRCX9WB#5",
"type": "StatusList2021Entry",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": 5,
"statusListCredential": "https://jchartrand.github.io/status-test-three/DKSPRCX9WB"
Expand All @@ -197,12 +196,12 @@ Now, your next step would be to sign this Verifiable Credential. You could pass

NOTE: CURL can get a bit clunky if you want to experiment more (e.g., by changing what goes into the VC before signing), so you might consider trying [Postman](https://www.postman.com/downloads) which makes it easier to construct and send HTTP calls.

### Revoke
### Revocation and suspension

Revocation is fully explained in the Bitstring Status List specification and our implemenations thereof, but effectively, it amounts to POSTing an object to the revocation endpoint, like so:
Revocation and suspension are fully explained in the [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/) specification and our implemenations thereof, but effectively, it amounts to POSTing an object to the revocation endpoint, like so:

```
{credentialId: '23kdr', credentialStatus: [{type: 'StatusList2021Credential', status: 'revoked'}]}
{credentialId: '23kdr', credentialStatus: [{type: 'BitstringStatusListCredential', status: 'revoked'}]}
```

Fundamentally, you are just posting up the ID of the credential.
Expand Down
21 changes: 21 additions & 0 deletions server-dev-only.cert
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDcjCCAloCCQC5dkGYdTI6MjANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJD
QTEQMA4GA1UECAwHT05UQVJJTzERMA8GA1UEBwwIQU5DQVNURVIxDDAKBgNVBAoM
A0RDQzESMBAGA1UEAwwJbG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZqYy5jaGFy
dHJhbmRAZ21haWwuY29tMB4XDTIzMDEzMTE1NDcwMFoXDTIzMDMwMjE1NDcwMFow
ezELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09OVEFSSU8xETAPBgNVBAcMCEFOQ0FT
VEVSMQwwCgYDVQQKDANEQ0MxEjAQBgNVBAMMCWxvY2FsaG9zdDElMCMGCSqGSIb3
DQEJARYWamMuY2hhcnRyYW5kQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALswlM6FgU6gPGIFhO0ohyRSJePtDpSLR0wnjyZobVzZjtd8
5HqbawRfE1zAPAyVmPZ1cUMPKc8owiF1PGdMP/q7Dsk9adUKkEipAHni84fOYgFk
+FxQwV58kjz5zqrCCnYQlLTTk2NtS/wTKl+5k3yXGrYlL41f7vMiWrEFTbvujWAz
Qa0GbcXdckRM3xt8HJb0iMAvOwAMi+gCDPXe3hqwu5AyzaN9tC3JwHLdVBwWtsGv
IlM49SYDnvOmEGxZDTLemhPCypkFWE/A+0VSDjMxs7BgtGfYxcV2grU7lE3bJOxT
fhzCtqtxaHkGSfr0MY+OplEED0foyRchzCyq+8sCAwEAATANBgkqhkiG9w0BAQsF
AAOCAQEAof2Yu6M74yyX8HIampxWE+NIHdRVoYksUYqfjIwEsdWkMzTvcLve5mQn
1PhlYk/5X8aFEMMwMrSZb5EFqV+fOktRymtrUDyYYdopAyct6OWutu4cih47wRdL
qd4dDale4OYJemD7m4VgXa7j0zcJfby8jqZPM+N3iNwLCDGWaGj6CsZDr0BR6xh0
YyhyZBVCseTLfZGyxmCTHnYg4/cGBoKm/I4CIpXmuLbDMv1n/+xlK9oVn84UvqkY
oydsBrg4gsxHVNMBK8/ftZzDOF5ZVHHNHx0Z37oRE18Al4+1vFzlzPzpKCG9U3Ss
u2NnWz9AjicouE3ikIDINMFctaywEw==
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions server-dev-only.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7MJTOhYFOoDxi
BYTtKIckUiXj7Q6Ui0dMJ48maG1c2Y7XfOR6m2sEXxNcwDwMlZj2dXFDDynPKMIh
dTxnTD/6uw7JPWnVCpBIqQB54vOHzmIBZPhcUMFefJI8+c6qwgp2EJS005NjbUv8
EypfuZN8lxq2JS+NX+7zIlqxBU277o1gM0GtBm3F3XJETN8bfByW9IjALzsADIvo
Agz13t4asLuQMs2jfbQtycBy3VQcFrbBryJTOPUmA57zphBsWQ0y3poTwsqZBVhP
wPtFUg4zMbOwYLRn2MXFdoK1O5RN2yTsU34cwrarcWh5Bkn69DGPjqZRBA9H6MkX
IcwsqvvLAgMBAAECggEBAKhF2d91gHJP9Tggwgf34NSzzEADAJJkSimZfkQGqBlJ
sfDg4vuc7y000tEUNmcRrDoSBUlFPk5t02YEX9J0ZydcNMSPIq5TGrVWx4jKjiXN
T6j1PZavOcVYspWB81jpqkHMUgHkGKDOxfnKuNLonj7oDykicIbkcIe8oE51+BUx
p4AX4yT4rZIzpQi6rRWAiaiWnKA5WZd75JqwvjTNXsrpsiHwHPLUtDJzzknHAK5q
7wPcSDQIgpqop3ScxI+T+x0jZbjGoy6q5R70u5IByYdm6eqYnqT37ly4qDiZwgAh
pVL/KaPLnhQAWYHFcBs9ZhrmuqYMBIJNRzJ9roWxoyECgYEA9J2bT+Z4y+1qQIf4
hlvluaQ9VXguYysk3JWTx9MfNOQ/Xi/fJTZgY21stGY9uyT8xuhK7QIYalyHRvYH
Wf48Aa85Jfo/Uj59QjCnWdwVNhOxRiR4Gn2piZ0Jkbt5TZqBdRaddqUyJYcPOni9
eaXaQOIaVgUEvBRIVX5X9+ZTwYkCgYEAw+bK7VBGRWQGZA++UjHjaBRSNbp3vY5m
b/b5LWClLHdQ+GAVFxayRLfzb22zbXQpRQgFDeLZO5NWbQ0hvHf3XjoRtYEuvJQA
niOlXWvJbQpj5A3beRVW9nk8Plw5TyX7ONcA+62qyYLf+o+LoL+OF+NtoeyzoI48
egdmInDoIbMCgYBPecmVay9CKpAECWlw1fjMmRUoaNTBeaoPVTXfjbOs7p+8DVe1
8nXcuBfCgRl7bWgHhD+bw7uFCy0UnCkFTznV3kV2FlluckkmMUKeSohFup41SPIQ
wVVNFc2fIMcntJRtI3zjqIajdL844zPEi2NfA1dFLXo9VWWvCU6xh48c6QKBgQCb
VqOswA2osmL67xzsUlDPU+XIYt7V+VezCrSVTeBLvSUAfjfbAg1DvlWTEvrHHOgo
q+5OD4ZP3koW2OXaa4pENmmaciAcOoOu4fcbd//Vrfp9eJuCjPBTKtkhXcG3yBdH
//zwlMorVdLC/RYr1hkXSijB0E6zTnYYEUvknYEETwKBgGQljAqFvIGpWTij8b6k
pwt0xymlOJjRvjTFyQMIQuJLhBG/ydQJhGQ5zvHQVY7S7CFM2QyheP25eqxETTXp
N5BQ854zBX4nqIe7NrY++ldwQg5FQgtR+8ZBiJQwHc5eo74D8acMu54oPbyig+nD
7L+JYn0WFx31GgoeLSmegA8x
-----END PRIVATE KEY-----
24 changes: 19 additions & 5 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { build } from './src/app.js'
import { getConfig, setConfig } from './src/config.js';
import { build } from './src/app.js';
import { getConfig } from './src/config.js';
import http from 'http';
import https from 'https';
import logger from './src/utils/logger.js';

const run = async () => {
await setConfig()
const { port } = getConfig();
const { port, enableHttpsForDev } = getConfig();
const app = await build();
http.createServer(app).listen(port, () => console.log(`Server running on port ${port}`));

if (enableHttpsForDev) {
https
.createServer(
{
key: fs.readFileSync('server-dev-only.key'),
cert: fs.readFileSync('server-dev-only.cert')
},
app
).listen(port, () => logger.info(`Server running on port ${port} with https`));
} else {
http
.createServer(app).listen(port, () => logger.info(`Server running on port ${port} with http`));
}
};

run();
11 changes: 0 additions & 11 deletions src/allocateStatus.js

This file was deleted.

80 changes: 45 additions & 35 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import express from 'express';
import cors from 'cors';
import status from './status.js';
import revoke from './revoke.js'
import allocateStatus from './allocateStatus.js'
import accessLogger from './middleware/accessLogger.js';
import errorHandler from './middleware/errorHandler.js';
import errorLogger from './middleware/errorLogger.js';
Expand All @@ -23,82 +21,94 @@ export async function build(opts = {}) {
res.send({ message: 'status-service-git server status: ok.' });
});

// Get status credential
app.get('/:statusCredentialId', async function (req, res, next) {
const statusCredentialId = req.params.statusCredentialId;
try {
const statusCredential = await status.getStatusCredential(statusCredentialId);
if (!statusCredential) {
next({
message: `Unable to find Status Credential with ID "${statusCredentialId}".`,
code: 404
});
}
return res.status(200).json(statusCredential);
} catch (error) {
next({
message: error.message,
code: error.code
});
}
});

// Allocate status
app.post('/credentials/status/allocate',
async function (req, res, next) {
try {
const vc = req.body;
if (!vc || !Object.keys(vc).length) {
next({
message: 'A verifiable credential must be provided in the body',
message: 'A Verifiable Credential must be provided in the body.',
code: 400
});
}
const vcWithStatus = await allocateStatus(vc);
const vcWithStatus = await status.allocateSupportedStatuses(vc);
return res.json(vcWithStatus);
} catch (e) {
// We catch the async errors and pass them to the error handler.
if (!e.message) {e.message = "Error when allocating status position."}
if (!e.message) {
e.message = 'Unable to allocate status position.'
}
// Note that if e contains a code property, the following spread of e will
// (correctly) overwrite the 500
next({code: 500, ...e});
next({ code: 500, ...e });
}
});

// Update status
// The body will look like:
// {credentialId: '23kdr', credentialStatus: [{type: 'StatusList2021Credential', status: 'revoked'}]}
// {credentialId: '23kdr', credentialStatus: [{type: 'BitstringStatusListCredential', status: 'revoked'}]}
app.post('/credentials/status',
async function (req, res, next) {
try {
const updateRequest = req.body;
if (!updateRequest || !updateRequest.credentialId || !updateRequest.credentialStatus) {
next({
message: 'A status update request must be provided in the body',
message: 'A status update request must be provided in the body.',
code: 400
});
}
const { credentialId, credentialStatus } = updateRequest;
const status = credentialStatus[0].status;
const statusId = credentialStatus[0].status;
const statusType = credentialStatus[0].type;

if (statusType !== 'StatusList2021Credential') {
if (statusType !== 'BitstringStatusListCredential') {
next({
message: 'StatusList2021Credential is the only supported status type.',
message: 'BitstringStatusListCredential is the only supported status type.',
code: 400
});
}
const statusResponse = await revoke(credentialId, status);
return res.status(statusResponse.code).send(statusResponse);
const updateStatusResponse = await status.updateStatus(credentialId, statusId);
return res.status(updateStatusResponse.code).json(updateStatusResponse);
} catch (e) {
// We catch the async errors and pass them to the error handler.
if (!e.message) {e.message = "Error updating credential status position."}
if (!e.message) {
e.message = 'Error updating credential status position.'
}
// Note that if e contains a code property, the following spread of e will
// (correctly) overwrite the 500
next({code: 500, ...e});
next({ code: 500, ...e });
}
});

// Get credential info
app.get('/credentials/:credentialId', async function (req, res, next) {
const credentialId = req.params.credentialId;
try {
const credentialInfo = await status.getCredentialInfo(credentialId);
return res.status(200).json(credentialInfo);
} catch (error) {
next({
message: error.message,
code: error.code
});
}
});

// Get status credential
app.get('/:statusCredentialId', async function (req, res, next) {
const statusCredentialId = req.params.statusCredentialId;
try {
const statusCredential = await status.getStatusCredential(statusCredentialId);
return res.status(200).json(statusCredential);
} catch (error) {
next({
message: error.message,
code: error.code
});
}
});

// Attach the error handling middleware calls, in the order that they should run
app.use(errorLogger);
app.use(errorHandler);
Expand Down
Loading

0 comments on commit e846573

Please sign in to comment.