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

add status credential GET endpoint #26

Merged
merged 5 commits into from
Sep 6, 2024
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
5 changes: 4 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
**/*.env
node_modules
node_modules
compose-health-test.yaml
compose-v2-test.yaml
.env.healthcheck.testing
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,8 @@ dist
.tern-port

# vscode
.vscode
.vscode

compose-health-test.yaml
compose-v2-test.yaml
.env.healthcheck.testing
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# issuer-coordinator Changelog

## 0.3.0 - TBD
## 0.3.0 - 2024-09-06

### Changed
- Convert Status List 2021 to Bitstring Status List.
- Differentiate between database status service and Git status service.
- Rename environment variables.
- Update revocation and suspension instructions.
- Add endpoint to retrieve status list from underlying database status service.

## 0.2.0 - 2024-04-22

Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,16 @@ Use this service to issue [Verifiable Credentials](https://www.w3.org/TR/vc-data

Implements two [VC-API](https://w3c-ccg.github.io/vc-api/) HTTP endpoints:


* [POST /credentials/issue](https://w3c-ccg.github.io/vc-api/#issue-credential)
* [POST /credentials/status](https://w3c-ccg.github.io/vc-api/#update-status)

Also implements an endpoint that returns a status list if the underlying status service itself returns the list - essentially then just acts as a public proxy within the docker compose, forwarding the request for the list to the status service and returning the result. For the moment, our [mongo-backed status service](https://github.com/digitalcredentials/status-service-db) is the only status service (we know of) that returns a list. The endpoint then is:

* [POST /status/:listId](https://w3c-ccg.github.io/vc-api/#update-status)

Where the :listId is the identifier of the list

We've tried hard to make this simple to install and maintain, and correspondingly easy to evaluate and understand as you consider whether digital credentials are useful for your project, and whether this issuer would work for you.

In particular, we've separated the discrete parts of an issuer into smaller self-contained apps that are consequently easier to understand and evaluate, and easier to *wire* together to compose functionality. The apps are wired together in a simple Docker Compose network that pulls images from Docker Hub.
Expand Down Expand Up @@ -95,7 +102,7 @@ curl --location 'http://localhost:4005/instance/test/credentials/issue' \
--data-raw '{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json"
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json"
],
"id": "urn:uuid:2fe53dc9-b2ec-4939-9b2c-0d00f6663b6c",
"type": [
Expand Down Expand Up @@ -145,7 +152,7 @@ This should return a fully formed and signed credential printed to the terminal,
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "urn:uuid:2fe53dc9-b2ec-4939-9b2c-0d00f6663b6c",
Expand Down Expand Up @@ -403,7 +410,7 @@ curl --location 'http://localhost:4005/instance/econ101/credentials/issue' \
--data-raw '{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json"
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json"
],
"id": "urn:uuid:2fe53dc9-b2ec-4939-9b2c-0d00f6663b6c",
"type": [
Expand Down
16 changes: 16 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,22 @@ export async function build (opts = {}) {
}
})

app.get('/status/:statusCredentialId', async function (req, res, next) {
if (!enableStatusService) next({ code: 405, message: 'The status service has not been enabled.' })
const statusCredentialId = req.params.statusCredentialId
try {
const { data: statusCredential } = await axios.get(`http://${statusService}/${statusCredentialId}`)
return res.status(200).json(statusCredential)
} catch (error) {
if (error.response.status === 404) {
next({ code: 404, message: 'No status credential found for that id.' })
} else {
next(error)
}
}
return res.status(500).send({ message: 'Server error.' })
})

// Attach the error handling middleware calls, in order they should run
app.use(errorLogger)
app.use(errorHandler)
Expand Down
23 changes: 23 additions & 0 deletions src/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import protectedNock from './test-fixtures/nocks/protected_status_signing.js'
import unprotectedStatusUpdateNock from './test-fixtures/nocks/unprotected_status_update.js'
import unknownStatusIdNock from './test-fixtures/nocks/unknown_status_id_nock.js'
import protectedStatusUpdateNock from './test-fixtures/nocks/protected_status_update.js'
import unknownStatusListNock from './test-fixtures/nocks/unknown_status_list_nock.js'
import statusListNock from './test-fixtures/nocks/status_list_nock.js'

import { build } from './app.js'

Expand Down Expand Up @@ -227,4 +229,25 @@ describe('api', () => {
expect(response.status).to.equal(200)
})
})

describe('GET /status/:statusCredentialId', () => {
it('returns 404 for unknown status credential id', async () => {
unknownStatusListNock()
const response = await request(app)
.get('/status/9898u')
expect(response.header['content-type']).to.have.string('json')
expect(response.status).to.equal(404)
})

it('returns credential status list from status service', async () => {
statusListNock()
const response = await request(app)
.get('/status/slAwJe6GGR6mBojlGW5U')
expect(response.header['content-type']).to.have.string('json')
expect(response.status).to.equal(200)
const returnedList = JSON.parse(JSON.stringify(response.body))
// this proof value comes from the nock:
expect(returnedList.proof.proofValue).to.equal('z4y3GawinQg1aCqbYqZM8dmDpbmtFa3kE6tFefdXvLi5iby25dvmVwLNZrfcFPyhpshrhCWB76pdSZchVve3K1Znr')
})
})
})
34 changes: 34 additions & 0 deletions src/test-fixtures/nocks/status_list_nock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import nock from 'nock'

const theList = `{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "https://sincere-bonefish-currently.ngrok-free.app/slAwJe6GGR6mBojlGW5U",
"type": [
"VerifiableCredential",
"BitstringStatusListCredential"
],
"credentialSubject": {
"id": "https://sincere-bonefish-currently.ngrok-free.app/slAwJe6GGR6mBojlGW5U#list",
"type": "BitstringStatusList",
"encodedList": "uH4sIAAAAAAAAA-3BIQEAAAACICf4f60vTEADAAAAAAAAAAAAAADwN_wEBkHUMAAA",
"statusPurpose": "revocation"
},
"issuer": "did:key:z6Mkg165pEHaUPxkY4NxToor7suxzawEmdT1DEWq3e1Nr2VR",
"validFrom": "2024-09-03T15:24:19.685Z",
"proof": {
"type": "Ed25519Signature2020",
"created": "2024-09-03T15:24:19Z",
"verificationMethod": "did:key:z6Mkg165pEHaUPxkY4NxToor7suxzawEmdT1DEWq3e1Nr2VR#z6Mkg165pEHaUPxkY4NxToor7suxzawEmdT1DEWq3e1Nr2VR",
"proofPurpose": "assertionMethod",
"proofValue": "z4y3GawinQg1aCqbYqZM8dmDpbmtFa3kE6tFefdXvLi5iby25dvmVwLNZrfcFPyhpshrhCWB76pdSZchVve3K1Znr"
}
}`

export default () => {
nock('http://localhost:4008')
.get('/slAwJe6GGR6mBojlGW5U')
.reply(200, theList)
}
7 changes: 7 additions & 0 deletions src/test-fixtures/nocks/unknown_status_list_nock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import nock from 'nock'

export default () => {
nock('http://localhost:4008')
.get('/9898u')
.reply(404, { code: 404, message: 'No status credential found for that id.' })
}
Loading