npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

did-siop

v2.0.8

Published

A library containing core functionality for DID based Self Issued OpenID Connect authentication. Contains both Relying Party and Provider components.

Downloads

170

Readme

did-siop

Overview

This implements Self Issued OpenId Connect Provider (SIOP) V2. The library contains two components, RP (Relying Party) and Provider. Provider is intended to be use inside any piece of software which will provide DID SIOP authentication (Identity Wallet) and RP can be used by relying parties (client applications) to employ DID SIOP authentication. This library can be used in both client-side (browser) and server-side (Node.js) applications.

Following are the primary specifications followed by this implementation.

Specifications

Availability

Documentation

Capabiltities

Static SIOP Discovery Metadata

By defalut, library uses specified Discovery metadata, but also provides a mechanism to use custom values.

Initialise RP with Metadata

        // get default set of metadata
        let temp_md:SiopMetadataSupported = {...SIOP_METADATA_SUPPORTED}
        // set scopes to "openid" and "did_authn"
        temp_md.scopes = ["openid" "did_authn"];

        let keyResolv2018 = new KeyDidResolver('key', CRYPTO_SUITES.Ed25519VerificationKey2018);
        let siop_rp = await RP.getRP(
            redirect_uri, // RP's redirect_uri
            DID_TEST_RESOLVER_DATA_NEW[3].did, // RP's did
            registration,
            temp_md, // use custom Metadata to initialise the RP
            [keyResolv2018]
        )

Validate Metadata

        // get default set of metadata
        let temp_md:SiopMetadataSupported = {...SIOP_METADATA_SUPPORTED}

        // set scopes supported to "openid" only, so the request validate must have "openid" as scope
        temp_md.scopes = ["openid];

        // use the new metadata set to validate the request
        let validityPromise =  DidSiopRequest.validateRequest(requests.good.requestGoodEmbeddedJWT,temp_md);

DID Resolvers

Version 2 of did-siop provides mechanism to use custom DID Resolvers. Currently resolvers for following DID Methods are built in, but developers have the option to write thier own resolvers.

  • EthrDidResolver did:ethr
  • KeyDidResolver did:key
  • UniversalDidResolver did:* - DID Methods supported by https://dev.uniresolver.io/

To build a custom resolver to use with DID-SIOP, derive your custom resolver from DidResolver and override the resolveDidDocumet appropriately.

Working with Resolvers

Default behavior of this library does not need any Resolvers to be specified. In absence of an external Resolver, library uses UniversalDidResolver which relies on https://dev.uniresolver.io/ (Please note, availability of https://dev.uniresolver.io/ is not consistent and do not advise to use it in live applications).

At the time of creating a RP or Provider instance, along with the DID, it is possible to specify an array of Resolvers that should be used to resolve the given DID. These resolvers must be derived from DidResolver baseclass. Once specified a set of resolvers at the time of creating RP or Provider instance, subsequent requirements of DID resolutions in that instance will use the provided resolvers. In addition to this, it is possible to specify an array of resolvers in following methods which overrised the instance level resolver list.

  • RP:validateResponse
  • RP:validateResponseWithVPData
  • Provider:validateRequest

Crypto Suites

When using/building a resolver, the library provide the option of specifying a Crypto Suite to be used in resolving DIDs. Relevant Crypto Suite can be passed as an argument when constructing the DIDResolver.

let keyResolv2018 = new KeyDidResolver('key', CRYPTO_SUITES.Ed25519VerificationKey2018);
let siop_rp = await RP.getRP(
    redirect_uri, // RP's redirect_uri
    DID_TEST_RESOLVER_DATA_NEW[3].did, // RP's did
    registration,
    undefined,
    [keyResolv2018]
);

DID-SIOP has been tested the KeyDidResolver using following Crypto-Suites.

  • @digitalbazaar/ed25519-verification-key-2018
  • @digitalbazaar/ed25519-verification-key-2020

Special Data Structures

VPData

When generating a response with Provider.generateResponseWithVPData, matching Presentation Data for the vp_data parameter of Claims will be submitted using this data structure.

export interface VPData {
    vp_token: any; // JSON object with VP related data
    _vp_token: any; // JSON object wit VP request related info
}

SIOPTokensEcoded

When generating a response with Provider.generateResponseWithVPData , data is returned using this data structure. Both ID_Token and VP_Token are presended as Base64 encoded JWTs

export interface SIOPTokensEcoded {
    id_token: string; // Base64 encoded JWT
    vp_token: string; // Base64 encoded JWT
}

SIOPTokenObjects

When validating a response with Provider.validateResponseWithVPData , method returns using this data structure. Both ID_Token and VP_Token are presended as JWTs

export interface SIOPTokenObjects {
    id_token: any; // Decoded Object
    vp_token: any; // Decoded Object
}

Usage

Sample illustrating the usage of basic funcitons of did-siop library can be found here

RP

//Request Generation
var request;
siop_rp = await DID_SIOP.RP.getRP(
    'localhost:5001/home', // RP's redirect_uri
    'did:ethr:rinkeby:0x02be73dcaa2013a714b6745f54ff8576df151f8226cc3923538bfbfb9a014584fe', // RP's did
    {
        jwks_uri: 'https://uniresolver.io/1.0/identifiers/did:example:0xab;transform-keys=jwks',
        id_token_signed_response_alg: ['ES256K', 'EdDSA', 'RS256']
    }
);
console.log('Got RP instance ....');
siop_rp.addSigningParams('c4873e901915343baf7302b0b87bae70bf5726e9280d415b3f7fc85908cc9d5a'); // Private key

console.log('RP SigningParams added ...');
request = await siop_rp.generateRequest();

console.log('Request generated ...', request);

//Response validation
const validateResponse = async () => {
    console.log('onRP');
    let keyResolv2020 = new SIOP.Resolvers.KeyDidResolver(
        'key',
        '@digitalbazaar/x25519-key-agreement-key-2018'
    );
    let siop_rp = await SIOP.RP.getRP(
        'localhost:4200/home', // RP's redirect_uri
        'did:key:z6MkvEoFWxZ9B5RDGSTLo2MqE3YJTxrDfLLZyZKjFRtcUSyw', // RP's did
        {
            id_token_signed_response_alg: ['ES256K', 'ES256K-R', 'EdDSA', 'RS256']
        },
        undefined,
        [keyResolv2020]
    );

    console.log(siop_rp);

    siop_rp.addSigningParams(
        'zrv1xdp8ZsfXSDh4fQp8sE2VYPmLiCL3RssjKeXW7fYrRkxyWpWR5ugcC36WrCx9FizbJvxdwFmYcq7YxRVC2nVPFp5'
    ); // Private key

    console.log('RP SigningParams added ...');
    let valid = await siop_rp.validateResponse(props.response);
    console.log('Response validated ...', valid);
    setResponse(valid);
};

Provider

// Response Generation
const DID_SIOP = require('did-siop');

const generateResponse = async () => {
    let provider = await Provider.getProvider(
        'did:key:z6MkvEoFWxZ9B5RDGSTLo2MqE3YJTxrDfLLZyZKjFRtcUSyw'
    );
    console.log('Got Provider instance with User DID...');
    provider.addSigningParams(
        'zrv1xdp8ZsfXSDh4fQp8sE2VYPmLiCL3RssjKeXW7fYrRkxyWpWR5ugcC36WrCx9FizbJvxdwFmYcq7YxRVC2nVPFp5'
    ); // User's private key

    console.log('User SigningParams added ...');

    // Request validation and response generation
    console.log('props.request=>>', props.request);
    provider
        .validateRequest(props.request)
        .then(async (decodedRequest) => {
            console.log('Request validation completed ...');
            console.log('decodedRequest', decodedRequest);
            let jwtExpiration = 5000;
            try {
                await provider
                    .generateResponse(decodedRequest.payload, jwtExpiration)
                    .then((responseJWT) => {
                        console.log('Response generated ...');
                        console.log('responseJWT', responseJWT);
                    });
            } catch (err) {
                console.log('ERROR provider.generateResponse  ', err);
            }
        })
        .catch((err) => {
            console.log('ERROR invalid request', err);
        });
};

Supported Algorithms

Defined in src/core/globals.ts

  • RS256, RS384, RS512
  • PS256, PS384, PS512
  • ES256, ES384, ES512, ES256K, ES256K-R, EdDSA

Supported Key Formats

Defined in src/core/globals.ts

  • PKCS8_PEM, PKCS1_PEM
  • HEX, BASE58, BASE64

Verifiable Presentations (VP)

Request VPs

In the request, vp_token attribute could appear within the claims attribute. This should have have the presentation_definition attribute as a child element.

Sample vp_token (for requesting VPs) :

    "vp_token": {
        "presentation_definition": {
            "id": "vp token example",
            "input_descriptors": [
                {
                    "id": "id card credential",
                    "format": {
                        "ldp_vc": {
                            "proof_type": [
                                "Ed25519Signature2018"
                            ]
                        }
                    },
                    "constraints": {
                        "fields": [
                            {
                                "path": [
                                    "$.type"
                                ],
                                "filter": {
                                    "type": "string",
                                    "pattern": "IDCardCredential"
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }

VPs in Response

vp_token will be included in the same response as ID Token and will be a separate element in the same level as ID Token. In this implementation, vp_token is an Base64 encoded JWT. Along with vp_token, there coudl be a _vp_token attribute inside the id_token. Purpose of this is to give an indication about what VPs were requested at the request.

Sample _vp_token :

    "_vp_token": {
        "presentation_submission": {
            "id": "Selective disclosure example presentation",
            "definition_id": "Selective disclosure example",
            "descriptor_map": [
                {
                    "id": "ID Card with constraints",
                    "format": "ldp_vp",
                    "path": "$",
                    "path_nested": {
                        "format": "ldp_vc",
                        "path": "$.verifiableCredential[0]"
                    }
                }
            ]
        }
    }

Sample vp_token (in response) :

    id_token :{
      <id_token content>
    },
    vp_token : {
        "@context": [
            "https://www.w3.org/2018/credentials/v1"
        ],
        "type": [
            "VerifiablePresentation"
        ],
        "verifiableCredential": [
            {
                "@context": [
                    "https://www.w3.org/2018/credentials/v1",
                    "https://www.w3.org/2018/credentials/examples/v1"
                ],
                "id": "https://example.com/credentials/1872",
                "type": [
                    "VerifiableCredential",
                    "IDCardCredential"
                ],
                "issuer": {
                    "id": "did:example:issuer"
                },
                "issuanceDate": "2010-01-01T19:23:24Z",
                "credentialSubject": {
                    "given_name": "Fredrik",
                    "family_name": "Str&#246;mberg",
                    "birthdate": "1949-01-22"
                },
                "proof": {
                    "type": "Ed25519Signature2018",
                    "created": "2021-03-19T15:30:15Z",
                    "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..PT8yCqVjj5ZHD0W36zsBQ47oc3El07WGPWaLUuBTOT48IgKI5HDoiFUt9idChT_Zh5s8cF_2cSRWELuD8JQdBw",
                    "proofPurpose": "assertionMethod",
                    "verificationMethod": "did:example:issuer#keys-1"
                }
            }
        ],
        "id": "ebc6f1c2",
        "holder": "did:example:holder",
        "proof": {
            "type": "Ed25519Signature2018",
            "created": "2021-03-19T15:30:15Z",
            "challenge": "n-0S6_WzA2Mj",
            "domain": "https://client.example.org/cb",
            "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..GF5Z6TamgNE8QjE3RbiDOj3n_t25_1K7NVWMUASe_OEzQV63GaKdu235MCS3hIYvepcNdQ_ZOKpGNCf0vIAoDA",
            "proofPurpose": "authentication",
            "verificationMethod": "did:example:holder#key-1"
        }
    }

Code Samples

Find how to use SIOP with VP Data here

Test Cases

Test cases depend on the availabiltiy of https://dev.uniresolver.io/ (which goes offline time to time). If test cases are failing, check the availabiltiy of Uniresolver before troubshoot.

Tesing on Browser

test\browser-app has html and js files which runs without any other dependencies. These files should refer dist/browser/did-siop.min.js when implementing DID-SIOP related functions. puppeteer is used in headless mode with jest to run tests.

Use the following folder structure and naming covention when adding more tests.

test
|browser-e2e-minimum.spec.ts
|-- browser-app
    | e2e-minimum.html
    | e2e-minimum.js

Code Formatting

prettier is setup for code foramtting. This has been setup with husky & pretty-quick to do the formatting at each commit.

Hot Fix

This is necessarry only if you are building custom apps using did-siop-lib with older versions of TypeScript chaneg following line in node_modules/ethr-did-resolver/lib/index.js (ln1087)

    // blockTag = qParams.get('versionId') ?? blockTag;
    blockTag = ((qParams.get('versionId') == null) || qParams.get('versionId') == undefined) ? blockTag : qParams.get('versionId');