This repository has been archived by the owner on Dec 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add KCC prototype to PFI Exemplar (#39)
* Adding KCC prototype * complete kcc integration * Update the readme * set deployment hosts for DIDs * update offerings protocol and KCC requirement * revert manual protocol specification * Update KCC crede t
- Loading branch information
Showing
20 changed files
with
526 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,19 @@ | ||
#!/bin/bash | ||
set -euo pipefail | ||
|
||
export DATABASE_URL="postgres://${SEC_DB_USER}:${SEC_DB_PASSWORD}@${SEC_DB_HOST}:${SEC_DB_PORT}/${SEC_DB_NAME}" | ||
|
||
set -x | ||
export DBMATE_MIGRATIONS_DIR="./db/migrations" | ||
|
||
# Log before running dbmate | ||
echo "Running dbmate to apply migrations..." | ||
dbmate --wait --wait-timeout=60s up | ||
|
||
# Log before starting the server | ||
echo "Starting the Node.js server..." | ||
|
||
npm run example-create-issuer | ||
npm run seed-offerings | ||
# npm run seed-offerings | ||
npm run server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
javascript/tbdex-pfi-exemplar/src/example/idv-vendor/server.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import express from 'express' | ||
|
||
const app = express() | ||
const port = 3002 | ||
|
||
app.use(express.json()) | ||
|
||
app.get('/idv-form', (req, res) => { | ||
// todo this is where an HTML page could be returned | ||
res.status(200).send('GET request to the /idv-form endpoint') | ||
}) | ||
|
||
app.post('/idv-form', async (req, res) => { | ||
const r = await fetch('https://issuer-pfiexemplar.tbddev.org/idv/result', { | ||
method: 'POST', | ||
headers: {'content-type': 'application/json'}, | ||
body: JSON.stringify({applicantDid: req.body.didUri}) | ||
}) | ||
if (r.status !== 200) { | ||
res.status(500).end() | ||
return | ||
} | ||
|
||
res.status(201).end() | ||
}) | ||
|
||
let server | ||
|
||
export function startIDVServer() { | ||
server = app.listen(port, () => { | ||
console.log(`IDV server listening at http://localhost:${port}`) | ||
}) | ||
} | ||
|
||
export function stopIDVServer() { | ||
if(server) { | ||
server.close(() => { | ||
console.log('IDV server stopped') | ||
}) | ||
} else { | ||
console.log('IDV server not running') | ||
} | ||
} |
218 changes: 218 additions & 0 deletions
218
javascript/tbdex-pfi-exemplar/src/example/issuer/server.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
import { createOrLoadDid } from '../utils.js' | ||
import express from 'express' | ||
import fs from 'fs' | ||
import crypto from 'crypto' | ||
import {Jwt, VerifiableCredential} from '@web5/credentials' | ||
// | ||
// We need to create an issuer, which will issue VCs to the customer, and is trusted by the PFI. | ||
// This issuer DID has the IDV type service. | ||
const issuerDid = await createOrLoadDid('issuer.json') | ||
|
||
const app = express() | ||
const PORT = 3001 | ||
|
||
console.log('\nIssuer did:', issuerDid.uri) | ||
// write issuer.uri to issuer.txt | ||
fs.writeFileSync('issuerDid.txt', issuerDid.uri) | ||
|
||
|
||
app.use(express.json()) | ||
|
||
let idvResults = {} | ||
let payload | ||
|
||
/** called by wallet/2-request-siopv2-request.js */ | ||
app.get('/siopv2/auth-request', (_, res) => { | ||
const siopv2AuthRequest = { | ||
client_id: issuerDid.uri, | ||
response_type: 'id_token', // NOTE: we don't support vp_token in this proto-exemplar | ||
nonce: crypto.randomBytes(16).toString('hex'), | ||
scope: 'openid', | ||
response_mode: 'direct_post', | ||
response_uri: `http://localhost:${PORT}/siopv2/auth-response`, | ||
client_metadata: { | ||
subject_syntax_types_supported: 'did:dht did:jwk did:web' | ||
} | ||
} | ||
|
||
res.status(200).json(siopv2AuthRequest) | ||
}) | ||
|
||
/** called by wallet/3-submit-siopv2-response.js */ | ||
app.post('/siopv2/auth-response', async (req, res) => { | ||
try { | ||
({payload} = await Jwt.verify({ jwt: req.body.id_token })) | ||
if (!payload.nonce) { | ||
// todo implement your custom nonce verification logic | ||
console.error('Nonce invalid') | ||
res.status(403).end() | ||
return | ||
} | ||
} catch { | ||
res.status(401).json({ | ||
error: 'invalid_token', | ||
error_description: 'Token verification failed' | ||
}) | ||
return | ||
} | ||
|
||
const credentialOffer = { | ||
credential_issuer: `http://localhost:${PORT}`, | ||
credential_configuration_ids: ['KnownCustomerCredential'], | ||
grants: { | ||
'urn:ietf:params:oauth:grant-type:pre-authorized_code': { | ||
'pre-authorized_code': crypto.randomBytes(16).toString('hex') | ||
} | ||
} | ||
} | ||
|
||
idvResults[payload.iss] = 'idv-pending' | ||
|
||
const idvRequest = { | ||
credential_offer: credentialOffer, | ||
url: 'https://idv-pfiexemplar.tbddev.org/idv-form' // the url for the idv-vendor/ project | ||
} | ||
res.status(201).json(idvRequest) | ||
}) | ||
|
||
|
||
app.get('/.well-known/openid-credential-issuer', (_, res) => { | ||
const credentialIssuerMetadata = { | ||
credential_issuer: `http://localhost:${PORT}`, | ||
credential_endpoint: `http://localhost:${PORT}/oid4vci/credential`, | ||
credential_configurations_supported: { | ||
format: 'jwt_vc_json', | ||
cryptographic_binding_methods_supported: ['did:web', 'did:jwk', 'did:dht'], | ||
credential_signing_alg_values_supported: ['EdDSA', 'ES256K'], | ||
proof_types_supported: {jwt: {proof_signing_alg_values_supported: ['EdDSA', 'ES256K']}} | ||
} | ||
} | ||
|
||
res.status(200).json(credentialIssuerMetadata) | ||
}) | ||
|
||
app.get('/.well-known/oauth-authorization-server', (_, res) => { | ||
const authorizationServerMetadata = { | ||
issuer: `http://localhost:${PORT}`, | ||
token_endpoint: `http://localhost:${PORT}/oid4vci/token` | ||
} | ||
|
||
res.status(200).json(authorizationServerMetadata) | ||
}) | ||
|
||
app.post('/idv/result', (req, res) => { | ||
idvResults[req.body.applicantDid] = 'idv-completed' | ||
res.status(201).end() | ||
}) | ||
|
||
app.post('/oid4vci/token', async (req, res) => { | ||
if (req.body.grant_type !== 'urn:ietf:params:oauth:grant-type:pre-authorized_code') { | ||
res.status(400).json({ | ||
error: 'unsupported_grant_type', | ||
error_description: 'The authorization grant type is not supported by the authorization server', | ||
}) | ||
return | ||
} | ||
|
||
// todo verify pre-auth code | ||
// todo it should be validated against the provided client_id | ||
if (req.body['pre-authorized_code'] === '') { | ||
res.status(400).json({ | ||
'error': 'invalid_grant', | ||
'error_description': 'The provided pre-auth code is invalid', | ||
}) | ||
return | ||
} | ||
|
||
if (idvResults[req.body.client_id] === 'idv-pending') { | ||
res.status(428).json({ | ||
'error': 'authorization_pending', | ||
'error_description': 'Still waiting to hear back from the IDV submission', | ||
}) | ||
return | ||
} | ||
|
||
const exp = Math.floor(Date.now() / 1000) + (30 * 60) // plus 30 minutes | ||
const claims = { | ||
iss: issuerDid.uri, | ||
sub: req.body.client_id, | ||
iat: Math.floor(Date.now() / 1000), | ||
exp | ||
} | ||
// TODO the JWT typ header will not be properly set | ||
const accessTokenJwt = await Jwt.sign({signerDid: issuerDid, payload: claims}) | ||
|
||
res.status(200).json({ | ||
access_token: accessTokenJwt, | ||
token_type: 'bearer', | ||
expires_in: exp, | ||
c_nonce: crypto.randomBytes(16).toString('hex'), | ||
c_nonce_expires_in: 30 * 60 // 30 minutes | ||
}) | ||
}) | ||
|
||
app.post('/oid4vci/credential', async (req, res) => { | ||
try { | ||
const accessToken = req.headers['authorization'].split(' ')[1] | ||
await Jwt.verify({jwt: accessToken}) | ||
} catch { | ||
res.status(401).end() | ||
return | ||
} | ||
|
||
let applicantDid | ||
try { | ||
const {payload} = await Jwt.verify({jwt: req.body.proof.jwt}) | ||
applicantDid = payload.iss | ||
if (!payload.nonce) { | ||
// todo implement your custom nonce verification logic | ||
console.error('Nonce invalid') | ||
res.status(403).end() | ||
return | ||
} | ||
} catch (e) { | ||
res.status(401).end() | ||
return | ||
} | ||
|
||
const vc = await VerifiableCredential.create({ | ||
type : 'KnownCustomerCredential', | ||
issuer : issuerDid.uri, | ||
subject : applicantDid, | ||
data : { | ||
something: 'relevant' | ||
}, | ||
}) | ||
const vcJwt = await vc.sign({did: issuerDid}) | ||
|
||
res.status(200).json({credential: vcJwt}) | ||
}) | ||
|
||
let server | ||
|
||
export function startIssuerServer() { | ||
server = app.listen(PORT, () => { | ||
console.log(`Issuer server listening at http://localhost:${PORT}`) | ||
}) | ||
} | ||
|
||
export function stopIssuerServer() { | ||
if(server) { | ||
server.close(() => { | ||
console.log('Issuer server stopped') | ||
}) | ||
} else { | ||
console.log('Issuer server not running') | ||
} | ||
} | ||
|
||
console.log(` | ||
now run: | ||
> npm run seed-offerings | ||
to seed the PFI with the list of offerings along with this sanctions issuer did. | ||
This will ensure that the PFI will trust SanctionsCredentials issued by this issuer for RFQs against those offerings. | ||
`) |
7 changes: 7 additions & 0 deletions
7
javascript/tbdex-pfi-exemplar/src/example/kcc-wallet/0-create-did-dht.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import {DidDht} from '@web5/dids' | ||
import fs from 'fs' | ||
|
||
const bearerDid = await DidDht.create() | ||
const portableDid = await bearerDid.export() | ||
|
||
fs.writeFileSync('./portable-did.json', JSON.stringify(portableDid, null, 2)) |
12 changes: 12 additions & 0 deletions
12
javascript/tbdex-pfi-exemplar/src/example/kcc-wallet/1-resolve-idv-service-endpoint.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import {DidDht} from '@web5/dids' | ||
|
||
const didUri = process.argv[2] | ||
|
||
const resolution = await DidDht.resolve(didUri) | ||
if (resolution.didResolutionMetadata.error) { | ||
console.error(`Resolution failed ${JSON.stringify(resolution.didResolutionMetadata, null, 2)}`) | ||
process.exit(1) | ||
} | ||
|
||
const idvService = resolution.didDocument.service.find(x => x.type === "IDV") | ||
console.log(idvService.serviceEndpoint[0]) |
Oops, something went wrong.