Skip to content

First crack #1

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
30 changes: 15 additions & 15 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ jobs:
run: npm run test-node
env:
CI: true
test-karma:
runs-on: ubuntu-latest
# test-karma:
# runs-on: ubuntu-latest
# needs: [lint]
timeout-minutes: 10
strategy:
matrix:
node-version: [18.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- name: Run karma tests
run: npm run test-karma
# timeout-minutes: 10
# strategy:
# matrix:
# node-version: [18.x]
# steps:
# - uses: actions/checkout@v2
# - name: Use Node.js ${{ matrix.node-version }}
# uses: actions/setup-node@v1
# with:
# node-version: ${{ matrix.node-version }}
# - run: npm install
# - name: Run karma tests
# run: npm run test-karma
lint:
runs-on: ubuntu-latest
strategy:
Expand Down
60 changes: 51 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Example Isomorphic TS/JS Lib Template _(@digitalcredentials/isomorphic-lib-template)_
# Learner Credential Wallet Mock _(@digitalcredentials/lcw-mock)_

[![Build status](https://img.shields.io/github/actions/workflow/status/digitalcredentials/isomorphic-lib-template/main.yml?branch=main)](https://github.com/digitalcredentials/isomorphic-lib-template/actions?query=workflow%3A%22Node.js+CI%22)
[![NPM Version](https://img.shields.io/npm/v/@digitalcredentials/isomorphic-lib-template.svg)](https://npm.im/@digitalcredentials/isomorphic-lib-template)
[![Build status](https://img.shields.io/github/actions/workflow/status/digitalcredentials/lcw-mock/main.yml?branch=main)](https://github.com/digitalcredentials/lcw-mock/actions?query=workflow%3A%22Node.js+CI%22)
[![NPM Version](https://img.shields.io/npm/v/@digitalcredentials/lcw-mock.svg)](https://npm.im/@digitalcredentials/lcw-mock)

> A Typescript/Javascript isomorphic library template, for use in the browser, Node.js, and React Native.
> A Typescript/Javascript library that plays the part normally played by the [Learner Credential Wallet (LCW)](https://lcw.app) in the exchange between a wallet and an issuer like [DCC Exchange Coordinator](https://github.com/digitalcredentials/workflow-coordinator).

> Given a deeplink that normally opens the LCW, the lcw-mock will parse the link, construct a DIDAuth containing the value of the 'challenge' parameter from the deeplink along with the holder's DID, sign the DIDAuth, and POST it to the value of the `vc_request_url` from the deeplink.
>
> For use in the browser and Node.js.

## Table of Contents

Expand All @@ -16,7 +20,7 @@

## Background

TBD
Makes it easier to test credential issuance - specifically the DIDAuth exchange between the LCW and the issuer - without having to use the LCW on a real phone or an emulator.

## Security

Expand All @@ -31,22 +35,60 @@ TBD
To install via NPM:

```
npm install @digitalcredentials/isomorphic-lib-template
npm install @digitalcredentials/lcw-mock
```

### Development

To install locally (for development):

```
git clone https://github.com/digitalcredentials/isomorphic-lib-template.git
cd isomorphic-lib-template
git clone https://github.com/digitalcredentials/lcw-mock.git
cd lcw-mock
npm install
```

## Usage

TBD
There are two methods available:


#### mockLCW(deeplink)

1. Parses the deeplink, which should look similar to:

``` https://lcw.app/request.html?issuer=issuer.example.com&auth_type=bearer&challenge=f78e1e08-a2df-46ca-bb0b-6b08e3036815&vc_request_url=http://issuer.dcconsortium.org/exchange/68546cc1-9220-42c1-b0a5-78d429ed289b/f78e1e08-a2df-46ca-bb0b-6b08e3036815```

2. Using the value of the `challenge` query parameter from the deeplink, and a holder DID (did:key:TODO add here and should match those below) constructs a signed DIDAuth like so:

```
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"type": [
"VerifiablePresentation"
],
"holder": "did:key:z6MkqJAUa299LgRoKiaHtFG6KWVK1iUddAM5oUHsGRto9hfz",
"proof": {
"type": "Ed25519Signature2020",
"created": "2024-02-23T21:27:59Z",
"verificationMethod": "did:key:z6MkqJAUa299LgRoKiaHtFG6KWVK1iUddAM5oUHsGRto9hfz#z6MkqJAUa299LgRoKiaHtFG6KWVK1iUddAM5oUHsGRto9hfz",
"proofPurpose": "authentication",
"challenge": "f78e1e08-a2df-46ca-bb0b-6b08e3036815",
"proofValue": "z5J8qBwNT3dC7kuqAkXFEDWgx1NeCcd2Rcn5dyNbhWsZdJQVsUgGDfd8ZdUo2S3maEucuJ2vqgCDJRB6fqSxhd6jM"
}
}
```

3. Posts the signed DIDAuth to the value of the `vc_request_url` query parameter from the deeplink.

#### getDIDAuth(deeplink)

Same as `mockLCW` except it doesn't post the DIDAuth the vc_request_url, and instead simply returns it.

This DIDAuth can then be separately posted to the issuer exchange endpoint (from the value of the vc_request_url query parameter).

## Contribute

Expand Down
16 changes: 11 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@digitalcredentials/isomorphic-lib-template",
"description": "A Typescript/Javascript isomorphic library template, for use in the browser, Node.js, and React Native.",
"name": "@digitalcredentials/lcw-mock",
"description": "Mocks the part of the [Learner Credential Wallet (LCW)](https://lcw.app) in the exchange between a wallet and an issuer .",
"version": "0.0.1",
"scripts": {
"build": "npm run clear && tsc -d && tsc -p tsconfig.esm.json",
Expand All @@ -22,6 +22,11 @@
"browser": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"dependencies": {
"@digitalcredentials/ed25519-signature-2020": "^7.0.0",
"@digitalcredentials/ed25519-verification-key-2020": "^5.0.0-beta.1",
"@digitalcredentials/security-document-loader": "^8.0.0",
"@digitalcredentials/vc": "^10.0.0",
"axios": "^1.6.7"
},
"resolutions": {
"@typescript-eslint/typescript-estree": "^6.1.6"
Expand All @@ -32,6 +37,7 @@
"@types/node": "^20.4.6",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"axios-mock-adapter": "^1.22.0",
"chai": "^4.3.7",
"cross-env": "^7.0.3",
"eslint": "^8.46.0",
Expand Down Expand Up @@ -76,8 +82,8 @@
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/digitalcredentials/isomorphic-lib-template"
"url": "https://github.com/digitalcredentials/lcw-mock"
},
"homepage": "https://github.com/digitalcredentials/isomorphic-lib-template",
"bugs": "https://github.com/digitalcredentials/isomorphic-lib-template/issues"
"homepage": "https://github.com/digitalcredentials/lcw-mock",
"bugs": "https://github.com/digitalcredentials/lcw-mock/issues"
}
8 changes: 0 additions & 8 deletions src/Example.ts

This file was deleted.

44 changes: 44 additions & 0 deletions src/LCWMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import axios from 'axios'
import { signPresentation, createPresentation } from '@digitalcredentials/vc'
import { Ed25519VerificationKey2020 } from '@digitalcredentials/ed25519-verification-key-2020'
import { Ed25519Signature2020 } from '@digitalcredentials/ed25519-signature-2020'
import { securityLoader } from '@digitalcredentials/security-document-loader'

const holderDID = 'did:key:z6MkvL5yVCgPhYvQwSoSRQou6k6ZGfD5mNM57HKxufEXwfnP'

const documentLoader = securityLoader().build()
let suite: any = null

const getSuite = async (): Promise<any> => {
if (suite === null) {
const key = await Ed25519VerificationKey2020.generate(
{
seed: new Uint8Array([
217, 87, 166, 30, 75, 106, 132, 55,
32, 120, 171, 23, 116, 73, 254, 74,
230, 16, 127, 91, 2, 252, 224, 96,
184, 172, 245, 157, 58, 217, 91, 240
]),
controller: holderDID
}
)
suite = new Ed25519Signature2020({ key })
}
return suite
}

export const getDIDAuth = async (challenge: string): Promise<any> => {
const presentation = createPresentation({ holderDID })
const suite = await getSuite()
const didAuth = await signPresentation({ presentation, suite, challenge, documentLoader })
return didAuth
}

export const mockLCW = async (deepLink: string): Promise<any> => {
const parsedDeepLink = new URL(deepLink)
const vcRequestUrl = parsedDeepLink.searchParams.get('vc_request_url') as string // should be http://localhost:4004/exchange?challenge=VOclS8ZiMs&auth_type=bearer
const challenge = parsedDeepLink.searchParams.get('challenge') as string // the challenge that the exchange service generated
const didAuth = await getDIDAuth(challenge)
const response = await axios.post(vcRequestUrl, didAuth)
return response.data
}
3 changes: 2 additions & 1 deletion src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
// declare module 'jsonld'
declare module '@digitalcredentials/ed25519-signature-2020'
declare module '@digitalcredentials/vc'
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*!
* Copyright (c) 2023 Digital Credentials Consortium. All rights reserved.
* Copyright (c) 2024 Digital Credentials Consortium. All rights reserved.
*/
export { Example } from './Example'
export * from './LCWMock'
9 changes: 0 additions & 9 deletions test/Example.spec.ts

This file was deleted.

37 changes: 37 additions & 0 deletions test/LCWMock.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { expect } from 'chai'
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'

import { mockLCW, getDIDAuth } from '../src'

const mock = new MockAdapter(axios)

const challenge = 'f78e1e08-a2df-46ca-bb0b-6b08e3036815'
const vcRequestURL = 'https://issuer.dcconsortium.org/exchange/68546cc1-9220-42c1-b0a5-78d429ed289b/f78e1e08-a2df-46ca-bb0b-6b08e3036815'
const deeplink = `https://lcw.app/request.html?issuer=issuer.example.com&auth_type=bearer&challenge=${challenge}&vc_request_url=${vcRequestURL}`

describe('LCWMock', () => {
describe('getDIDAuth', () => {
it('generates didAuth', async () => {
const didAuth = await getDIDAuth(challenge)
expect(didAuth.proof.proofValue.length).to.be.greaterThan(3)
expect(didAuth.proof.challenge).to.equal(challenge)
expect(didAuth.type).to.eql(['VerifiablePresentation'])
})
})

describe('mockLCW', () => {
it.only('calls the deeplink with a DIDAuth', async () => {
mock
.onPost(vcRequestURL, {
asymmetricMatch: function (didAuth: any) {
return didAuth.proof.challenge === challenge &&
didAuth.proof.proofValue != null
}
})
.reply(200, { name: 'thisWouldBeTheReturnedVC' })
const response = await mockLCW(deeplink)
expect(response.name).to.equal('thisWouldBeTheReturnedVC')
})
})
})
Loading