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

Feat/jwt status list #225

Merged
merged 18 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {

public static DEFAULT_hashAlg = 'sha-256';

private userConfig: SDJWTConfig = {};
protected userConfig: SDJWTConfig = {};

constructor(userConfig?: SDJWTConfig) {
if (userConfig) {
Expand Down
94 changes: 94 additions & 0 deletions packages/jwt-status-list/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
![License](https://img.shields.io/github/license/openwallet-foundation-labs/sd-jwt-js.svg)
![NPM](https://img.shields.io/npm/v/%40sd-jwt%2Fhash)
![Release](https://img.shields.io/github/v/release/openwallet-foundation-labs/sd-jwt-js)
![Stars](https://img.shields.io/github/stars/openwallet-foundation-labs/sd-jwt-js)

# SD-JWT Implementation in JavaScript (TypeScript)

## jwt-status-list
An implementation of the [Token Status List](https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/) for a JWT representation, not for CBOR.
This library helps to verify the status of a specific entry in a JWT, and to generate a status list and pack it into a signed JWT. It does not provide any functions to manage the status list itself.



## Installation

To install this project, run the following command:

```bash
# using npm
npm install @sd-jwt/jwt-status-list

# using yarn
yarn add @sd-jwt/jwt-status-list

# using pnpm
pnpm install @sd-jwt/jwt-status-list
```

Ensure you have Node.js installed as a prerequisite.
## Usage

Creation of a JWT Status List:
```typescript
// pass the list as an array and the amount of bits per entry.
const list = new StatusList([1, 0, 1, 1, 1], 1);
const iss = 'https://example.com';
const payload: JWTPayload = {
iss,
sub: `${iss}/statuslist/1`,
iat: new Date().getTime() / 1000,
ttl: 3000, // time to live in seconds, optional
exp: new Date().getTime() / 1000 + 3600, // optional
};
const header: JWTHeaderParameters = { alg: 'ES256' };

const jwt = createHeaderAndPayload(list, payload, header);

// Sign the JWT with the private key, e.g. using the `jose` library
const jwt = await new SignJWT(values.payload)
.setProtectedHeader(values.header)
.sign(privateKey);

```

Interaction with a JWT status list on low level:
```typescript
//validation of the JWT is not provided by this library!!!

// jwt that includes the status list reference
const reference = getStatusListFromJWT(jwt);

// download the status list
const list = await fetch(reference.uri);

//TODO: validate that the list jwt is signed by the issuer and is not expired!!!

//extract the status list
const statusList = getListFromStatusListJWT(list);

//get the status of a specific entry
const status = statusList.getStatus(reference.idx);
```

### Integration into sd-jwt-vc
The status list can be integrated into the [sd-jwt-vc](../sd-jwt-vc/README.md) library to provide a way to verify the status of a credential. In the [test folder](../sd-jwt-vc/src/test/index.spec.ts) you will find an example how to add the status reference to a credential and also how to verify the status of a credential.

```typescript

### Caching the status list
Depending on the `ttl` field if provided the status list can be cached for a certain amount of time. This library has no internal cache mechanism, so it is up to the user to implement it for example by providing a custom `fetchStatusList` function.

## Development

Install the dependencies:

```bash
pnpm install
```

Run the tests:

```bash
pnpm test
```
57 changes: 57 additions & 0 deletions packages/jwt-status-list/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "@sd-jwt/jwt-status-list",
"version": "0.6.1",
"description": "Implementation based on https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"scripts": {
"build": "rm -rf **/dist && tsup",
"lint": "biome lint ./src",
"test": "pnpm run test:node && pnpm run test:browser && pnpm run test:cov",
"test:node": "vitest run ./src/test/*.spec.ts",
"test:browser": "vitest run ./src/test/*.spec.ts --environment jsdom",
"test:cov": "vitest run --coverage"
},
"keywords": ["sd-jwt-vc", "status-list", "sd-jwt"],
"engines": {
"node": ">=18"
},
"repository": {
"type": "git",
"url": "https://github.com/openwallet-foundation-labs/sd-jwt-js"
},
"author": "Mirko Mollik <[email protected]>",
"homepage": "https://github.com/openwallet-foundation-labs/sd-jwt-js/wiki",
"bugs": {
"url": "https://github.com/openwallet-foundation-labs/sd-jwt-js/issues"
},
"license": "Apache-2.0",
"devDependencies": {
"@types/pako": "^2.0.3",
"jose": "^5.2.2"
},
"dependencies": {
"@sd-jwt/types": "workspace:*",
"base64url": "^3.0.1",
"pako": "^2.1.0"
},
"publishConfig": {
"access": "public"
},
"tsup": {
"entry": ["./src/index.ts"],
"sourceMap": true,
"splitting": false,
"clean": true,
"dts": true,
"format": ["cjs", "esm"]
},
"gitHead": "ded40e4551bde7ae93083181bf26bd1b38bbfcfb"
}
3 changes: 3 additions & 0 deletions packages/jwt-status-list/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './status-list.js';
export * from './status-list-jwt.js';
export * from './types.js';
73 changes: 73 additions & 0 deletions packages/jwt-status-list/src/status-list-jwt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { JwtPayload } from '@sd-jwt/types';
import { StatusList } from './status-list.js';
import type {
JWTwithStatusListPayload,
StatusListJWTHeaderParameters,
StatusListEntry,
StatusListJWTPayload,
} from './types.js';
import base64Url from 'base64url';

/**
* Decode a JWT and return the payload.
* @param jwt JWT token in compact JWS serialization.
* @returns Payload of the JWT.
*/
function decodeJwt<T>(jwt: string): T {
const parts = jwt.split('.');
return JSON.parse(base64Url.decode(parts[1]));
}

/**
* Adds the status list to the payload and header of a JWT.
* @param list
* @param payload
* @param header
* @returns The header and payload with the status list added.
*/
export function createHeaderAndPayload(
list: StatusList,
payload: JwtPayload,
header: StatusListJWTHeaderParameters,
) {
// validate if the required fieds are present based on https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-02.html#section-5.1

if (!payload.iss) {
throw new Error('iss field is required');
}
if (!payload.sub) {
throw new Error('sub field is required');
}
if (!payload.iat) {
throw new Error('iat field is required');
}
//exp and tll are optional. We will not validate the business logic of the values like exp > iat etc.

header.typ = 'statuslist+jwt';
payload.status_list = {
bits: list.getBitsPerStatus(),
lst: list.compressStatusList(),
};
return { header, payload };
}

/**
* Get the status list from a JWT, but do not verify the signature.
* @param jwt
* @returns
*/
export function getListFromStatusListJWT(jwt: string): StatusList {
const payload = decodeJwt<StatusListJWTPayload>(jwt);
const statusList = payload.status_list;
return StatusList.decompressStatusList(statusList.lst, statusList.bits);
}

/**
* Get the status list entry from a JWT, but do not verify the signature.
* @param jwt
* @returns
*/
export function getStatusListFromJWT(jwt: string): StatusListEntry {
const payload = decodeJwt<JWTwithStatusListPayload>(jwt);
return payload.status.status_list;
}
Loading