Skip to content

Commit

Permalink
feat: added token verification and updated contract storage in store …
Browse files Browse the repository at this point in the history
…backend
  • Loading branch information
Dexagod committed Apr 25, 2024
1 parent 3ad81dd commit 4f4d130
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 119 deletions.
7 changes: 6 additions & 1 deletion Requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@
- [X] The interaction is automatic after a WebID button is clicked to show what is happening.
- [ ] The system allows the store to prove that they were allowed to perform the age verification.
- [X] A backend storage must be in place for the store
- [ ] The store website must forward data storage and checks to the backend
- [X] The store website must forward data storage and checks to the backend
- [ ] The system allows the person to check that their data was used correctly.
- [X] An auditing routine must be built in the store backend
- [ ] An auditing routine must be built as a frontend interface
- [ ] The Government VC Service
- [X] Must be able to create a VC
- [X] VC must be transfered to demo pod storage -> Not required for Demo because of fixed keypair seed
- [ ] VCs can be validated on the backend of the store
- [ ] The Auditing use-case
- [X] The store backend provides the option to retrieve all required data to audit
- [ ] This can be represented in an auditing browser app that shows colors when verified (token + VC)


Small note with using the UMA server token signature as the contract signature.
We can only trace this back to the UMA Server, and cannot reliably check the connection between the WebID and the UMA Server


## Demonstrator requirements
Expand Down
2 changes: 1 addition & 1 deletion demo/backend/gov-vc-issuer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ app.use(cors(config.cors))
app.use(bodyParser.json())
app.use((req, res, next) => {
const {method, url} = req
console.log(`${method}\t${url}`)
console.log(`[${config.name}] ${method}\t${url}`)
next()
})

Expand Down
2 changes: 2 additions & 0 deletions demo/backend/store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
"typescript": "^5.4.5"
},
"dependencies": {
"cors": "^2.8.5",
"express": "^4.19.2",
"jose": "^5.2.4",
"jwt-encode": "^1.0.1",
"n3": "^1.17.3",
"rdf-parse": "^2.3.3",
Expand Down
92 changes: 47 additions & 45 deletions demo/backend/store/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,69 @@
import express, { Request, Response, response } from 'express';
import { processAgeResult, retrieveData, terms } from './util'
import BackendStore, { Contract, Retrieval } from './storage';
import { processAgeResult, retrieveData, terms, verifyJwtToken } from './util'
import BackendStore, { Contract, Embedded, Retrieval } from './storage';
import cors from "cors"

const app = express()
const port = 5123
app.use(cors());
app.use(express.json())
app.use((req, res, next) => {
const {method, url} = req
console.log(`[store-backend] ${method}\t${url}`)
next()
})

const storage = new BackendStore();



// Verification Interface

app.get('/verify', async (req: Request, res: Response) => {
// const { webId } = req.body
const webId = 'http://localhost:3000/ruben/profile/card#me'

let { webid } = req.query
const webId = webid as string;
console.log(`[store-backend] processing verification request for ${webId}`)

// todo: make this take the correct webid and make the age credential to be found from the WebID?

const credentialURL = terms.views['age-credential'] // todo: fix this

// 1 negotiate access to age credential
const ageData = await retrieveData(credentialURL, webId);
const { data, token } = await retrieveData(credentialURL, webId);

// 2 store signed token for ag
let payload
try {
payload = await verifyJwtToken(token, webId);
} catch (e) {
const warning = 'Data unusable, as token could not be verified!'
console.warn(warning)
res.statusCode = 200;
res.send({
"verified": false, // todo: more info & credential verification result
"message": `verification failed: ${warning}`
})
}

// 3 Log token, contract (in token), webId (token verification check) and data as single unit
const contract = payload.contract as Contract
const embedded: Embedded = {
contract,
token,
webId,
data,
resourceId: credentialURL,
timestamp: new Date()
}
storage.storeEmbedded(embedded)

// 2 store signed token for age credential location
// 4 verify age credential signature
// todo: signature verification!!!!!!!!!!!!!!!!!!!!!!


// 3 verify age credential signature
const decision = await processAgeResult(ageData, webId)
// 5 check age
const decision = await processAgeResult(data, webId)

// 4 return decision
// 6 return decision
if (decision) {
res.statusCode = 200;
res.send({
Expand All @@ -38,50 +73,17 @@ app.get('/verify', async (req: Request, res: Response) => {
res.statusCode = 200;
res.send({
"verified": false, // todo: more info & credential verification result
"message": "verification failed"
})
}
})


// Logging interface

/**
* POST body of type Contract
*/
app.post('/contract', (req: Request, res: Response) => {
const contract = req.body as Contract
storage.storeContract(contract)
res.status(200);
res.send({status: 'ok'})
})

/**
* POST body of type Request
*/
app.post('/data', (req: Request, res: Response) => {
const bodyJSON = req.body;
const retrieval: Retrieval = {
timestamp : new Date(),
resourceId: bodyJSON.resourceId,
data: bodyJSON.data
}
storage.storeRetrieval(retrieval)
res.status(200);
res.send({status: 'ok'})
})

app.get('/audit', (req: Request, res: Response) => {
const result = storage.getLogs();
res.status(200)
res.send(result)
})




app.listen(port, () => {
console.log(`[store-backend] Store backend listening on port ${port}`)
})



46 changes: 14 additions & 32 deletions demo/backend/store/src/storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { randomUUID } from "crypto";


export type ODRLConstraint = {
leftOperand: any,
Expand All @@ -12,6 +10,15 @@ export type ODRLPermission = {
constraint: ODRLConstraint[]
}

export type Embedded = {
contract: Contract,
token: string,
webId: string // todo:: this should not be required
data: Data,
timestamp: Date,
resourceId: ResourceId,
}

export type Contract = {
"@context": string,
"@type": string,
Expand All @@ -31,47 +38,22 @@ export type Permission = {
resource_scopes: string[],
};


export type AccessToken = {
permissions: Permission[],
contractId?: string,
}

export type Retrieval = {
timestamp: Date,
resourceId: ResourceId,
data: Data,
}


export default class BackendStore {
// Map<id, Contract>
private contracts = new Array<Contract>();
private retrievals = new Array<Retrieval>();
// Maps retrieval Id on Contract Id
private mapping = new Map<string, string>();


storeContract(contract: Contract) {
this.contracts.push(contract);
}

private retrievals = new Array<Embedded>();

storeRetrieval(retrieval: Retrieval) {
this.retrievals.push(retrieval)

let relevantContract = this.contracts.find(contract => contract.target === retrieval.resourceId)
if(relevantContract) this.mapping.set(retrieval.resourceId, relevantContract.uid)
storeEmbedded(embedded: Embedded) {
this.retrievals.push(embedded)
}

getLogs() {
let results: {retrieval: Retrieval, contract: Contract}[] = []
for (let retrieval of this.retrievals) {
results.push({
retrieval,
contract: this.contracts.filter(c => c.uid === this.mapping.get(retrieval.resourceId as string))[0]
})
}
return results
return this.retrievals
}
}

Expand Down
Loading

0 comments on commit 4f4d130

Please sign in to comment.