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

@affinidi/affinidi-did-auth-lib

v2.9.1

Published

Affinidi Did Auth Helpers

Downloads

83

Readme

Affinidi Did Auth Helpers

Usage:

This library allows proving that the client/user is the owner of provided DID. Based on this proof App builders can give the client/user access to the appropriate resources.

Did-Auth flow is similar to the Sharing VC flow, but without VC exchanges, since DID itself should be enough info from the client/user to authenticate. Flow diagram.

The high-level perspective of Did-Auth flow looks like requestresponsevalidation of response, where a response is a response(signed with the private key) to the challenge from the request.

For simplicity DID methods provided by SDK can be used for the implementation Did-Auth flow:

Implementation of DID Auth flow using AffinidiDidAuthService class

Implementation might be consists of two parts - the client side and the service side. The service side should provide some endpoints or trigger methods for the client to start the auth flow. Both clients and service can set a token timeout, during validation min(service token timeout, client token timeout) will be used.

Initiate AffinidiDidAuthService class (service side)

Did auth flow should be implemented e.g. on the service side on which the user wants to log in. Simplest way is to initialize AffinidiDidAuthService class with encryptedSeed and encryptionKey options. For the client-side could be used the same approach, but with the client encryptedSeed and encryptionKey.

/**
 * sericeEncryptedSeed {String} (required) - encrypted seed, previously generated for service
 * serviceEncryptionKey {String} (required) - encreption key, previously generated for service
  */
const affinidiDidAuthService = new AffinidiDidAuthService({
  encryptedSeed: sericeEncryptedSeed,
  encryptionKey: serviceEncryptionKey,
})

Also AffinidiDidAuthService could be created using did, keyId, keyVault as options. This approach is for batter backward compatibility. If needed more low-level implementation could be created with the DidAuthServerService class for the service side and the DidAuthClientService class for the client side.

import { KeysService, KeyVault, DidDocumentService, LocalKeyVault } from '@affinidi/common'

const keyService = new KeysService(sericeEncryptedSeed, serviceEncryptionKey)
const documentService = DidDocumentService.createDidDocumentService(keyService)
/**
 * did {String} - anchored did of the local entity
 * keyId {String} - key id used for signing, defaults to `<did>#primary`
 * keyVault {KeyVault} - key store backed implementation when the seed is known, holding signing keys
 */
const affinidiDidAuthService = new AffinidiDidAuthService({
  did: documentService.getMyDid(),
  keyId: documentService.getKeyId(),
  keyVault: new LocalKeyVault(keyService),
})

Creation of the request token(service side)

Client DID(ownership of which client is proved) might be provided. Use createDidAuthRequestToken method to create Did-Auth request token(expires in 1 minute by default). That request token is a JWT signed with the service's public key. It should be sent back to the client for further steps.

/**
 * audienceDid {String} - audience DID of generated JWT request token (the one's DID to whom the token was sent)
 * expiresAt {Number} (optional) - expires (at specified time) of generated JWT request token (1 minute by default)
 */
const authDidRequestToken = await affinidiDidAuthService.createDidAuthRequestToken(audienceDid, expiresAt)

Building of the response token(client side)

To create a response token it is recommended to initialize the AffinidiDidAuthService class with encryptedSeed and encryptionKey options on the client side in the same way as for the service side. Use createDidAuthResponseToken method to create Did-Auth response token(expires in 12 hours by default). It is necessary to have a fresh request token from the service.

/**
 * authDidRequestToken {String} - signed JWT request token from the service
 * options {Object} (optional) - key value object with additional options
 * options.maxTokenValidInMs {Number} (optional) - maximum token validity period in milliseconds(12 hours by default)
 */
const responseToken = await affinidiDidAuthService.createDidAuthResponseToken(authDidRequestToken, options)

A Response token (JWT) should be used to authenticate the user at the service side.

As a best practice, it is recommended to use the LocalExpiringDidAuthResponseToken utility class that can encapsulate a responseToken. And keep track of its expiry time according to the local time of the client. This is needed in case the local time of the client can diverge from the service time.

/**
 * tokenRequestTime {Number} - token request time, recommended to use Date.now(), could be created before calling `createDidAuthRequestToken` method
 * responseToken {String} - response token (JWT) built on the client side
 */
const auth = LocalExpiringDidAuthResponseToken.initialize(tokenRequestTime, responseToken)
// Check if token is expired.
auth.isExpiredAt(Date.now())

Validating of the response token(service side)

It is recommended to implement the Authorization header for every endpoint and expect to receive the response token from the client side. Each time given response token might be validated by the service using the verifyDidAuthResponseToken method

/**
 * responseToken {String} - response token (JWT) built on the client side.
 * environment {Stirng} (required) - environment, could be `dev`, `staging` or `prod`
 * accessApiKey {Stirng} (required) - client access api key(uuid) 
 */
const isValid = await affinidiDidAuthService.verifyDidAuthResponseToken(responseToken, {
  environment: environment,
  accessApiKey: accessApiKey,
})

Generate accessApiKey. If the response token is not valid service should throw an error, otherwise, the request should proceed.

Implementation of the service side part of DID Auth flow using DidAuthServerService class

DidAuthServerService is introduced for implementation of the DID-Auth flow on the service side. The benefit of using DidAuthServerService is - it provides only two methods that might be in use for the service:

  • to create a request token to challenge the client App (createDidAuthRequestToken)
  • to validate the response token during the authentication process (verifyDidAuthResponseToken).

This class could be useful if you would like to implement your own did-auth service as an additional layer at api-gateway.

Example:

import { DidAuthServerService, Signer } from '@affinidi/affinidi-did-auth-lib'
import { KeysService, DidDocumentService, LocalKeyVault } from '@affinidi/common'
import { parse } from 'did-resolver'

// `serviceEncryptedSeed` and `serviceEncryptionKey` should be taken from the wallet.
const keyService = new KeysService(serviceEncryptedSeed, serviceEncryptionKey)
const documentService = DidDocumentService.createDidDocumentService(keyService)
// Initialize `Signer` class instance.
const signer = new Signer({
  did: documentService.getMyDid(),
  keyId: documentService.getKeyId(),
  keyVault: new LocalKeyVault(keyService),
})
// Parsing the service DID.
const verifierDid = parse(documentService.getMyDid()).did
// Initialize `DidAuthServerService` class.
const serverService = new DidAuthServerService(verifierDid, signer, null)
// Parsing the client DID (might come inside the client request as e.g. `audienceLongDid`).
const audienceDid = parse(audienceLongDid).did
const authDidRequestToken = await serverService.createDidAuthRequestToken(audienceDid, expiresAt)

Using DidAuthClientService class to implement the client side part of DID Auth flow

DidAuthClientService is introduced for implementation of DID-Auth flow on the client side. This class provides only createDidAuthResponseToken(create response token) which might be used only with LocalExpiringDidAuthResponseToken to prevent time inconsistent issues between service and client. It could be used as an alternative to the AffinidiDidAuthService class.

Example:

import { LocalExpiringDidAuthResponseToken, DidAuthClientService, Signer } from '@affinidi/affinidi-did-auth-lib'
import { KeysService, DidDocumentService, LocalKeyVault } from '@affinidi/common'

// `clientEncryptedSeed` and `clientEncryptionKey` should be taken from the wallet.
const keyService = new KeysService(clientEncryptedSeed, clientEncryptionKey)
const documentService = DidDocumentService.createDidDocumentService(keyService)
// Initialize `Signer` class instance.
const signer = new Signer({
  did: documentService.getMyDid(),
  keyId: documentService.getKeyId(),
  keyVault: new LocalKeyVault(keyService),
})
// Initialize `DidAuthClientService` class instance.
const didAuthClientService = new DidAuthClientService(signer)

let auth: LocalExpiringDidAuthResponseToken
// Example of the method that could be implemented to prepare a response token
// to authenticate clients on the service side using the `Did-Auth` flow.
const getAuth = async (maxTokenValidInMs: number): Promise<LocalExpiringDidAuthResponseToken> => {
  // Check if the token exists and if the token is expired.
  if (auth && !auth.isExpiredAt(Date.now())) {
    return auth
  }
  // Setting token request time.
  const tokenRequestTime = Date.now()
  // Example of method that pulls request token from the service side.
  const requestToken = await pullDidAuthRequestToken({})
  const responseToken = await didAuthClientService.createDidAuthResponseToken(requestToken, { maxTokenValidInMs })
  
  auth = LocalExpiringDidAuthResponseToken.initialize(tokenRequestTime, responseToken)
  
  return auth
}

Testing

For unit tests we are using Mocha and Chai.

npm run test