Skip to content

Commit

Permalink
feat: add a normalizer method to an unambiguous Verifiable<Credential>
Browse files Browse the repository at this point in the history
  • Loading branch information
mirceanis committed Jun 25, 2020
1 parent adb27e9 commit ffbd67f
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 10 deletions.
79 changes: 79 additions & 0 deletions src/converters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
VerifiableCredential,
JWT,
JwtPresentationPayload,
JwtVerifiableCredentialPayload,
CredentialPayloadInput,
Credential,
Verifiable
} from './types'
import { decodeJWT } from 'did-jwt'
import { VerifiableCredentialPayload } from 'src'

function asArray(input: any) {
return Array.isArray(input) ? input : [input]
}

function normalizeJwtCredentialPayload(input: Partial<JwtVerifiableCredentialPayload>): Credential {
let result: Partial<CredentialPayloadInput> = { ...input }

result.credentialSubject = { ...result.credentialSubject, ...result.vc?.credentialSubject }
result.credentialSubject.id = result.credentialSubject.id || result.sub
delete result.sub

result.issuer = typeof result.issuer === 'object' ? { ...result.issuer, id: result.iss } : { id: result.iss }
delete result.iss

result.id = result.id || result.jti
delete result.jti

result.type = [...asArray(result.type), ...asArray(result.vc.type)]
result['@context'] = [...asArray(result.context), ...asArray(result['@context']), ...asArray(result.vc['@context'])]
delete result.context
delete result.vc

//TODO: test parsing Date strings into Date objects
if (result.iat || result.nbf) {
result.issuanceDate = result.issuanceDate || new Date(result.nbf || result.iat).toISOString()
delete result.nbf
delete result.iat
}

if (result.exp) {
result.expirationDate = result.expirationDate || new Date(result.exp).toISOString()
delete result.exp
}

return result as Credential
}

function normalizeJwtCredential(input: JWT): Verifiable<Credential> {
return {
...normalizeJwtCredentialPayload(decodeJWT(input)),
proof: {
type: 'JwtProof2020',
jwt: input
}
}
}

/**
* Normalizes a credential payload into an unambiguous W3C credential data type
* @param input either a JWT or JWT payload, or a VerifiableCredential
*/
export function normalizeCredential(
input: Partial<VerifiableCredential> | Partial<JwtVerifiableCredentialPayload>
): Verifiable<Credential> {
if (typeof input === 'string') {
//FIXME: attempt to deserialize as JSON before assuming it is a JWT
return normalizeJwtCredential(input)
} else if (input.proof?.jwt) {
//TODO: test that it correctly propagates app specific proof properties
return { ...normalizeJwtCredential(input.proof.jwt), proof: input.proof }
} else {
//TODO: test that it accepts JWT payload, CredentialPayload, VerifiableCredential
//TODO: test that it correctly propagates proof, if any
return { proof: {}, ...normalizeJwtCredentialPayload(input) }
}
}

6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import * as validators from './validators'
import { JwtVerifiableCredentialPayload, Issuer, JwtPresentationPayload, JWT } from './types'
import { DIDDocument } from 'did-resolver'

export { Issuer, JwtVerifiableCredentialPayload as VerifiableCredentialPayload, JwtPresentationPayload as PresentationPayload }
export {
Issuer,
JwtVerifiableCredentialPayload as VerifiableCredentialPayload,
JwtPresentationPayload as PresentationPayload
}

interface Resolvable {
resolve: (did: string) => Promise<DIDDocument>
Expand Down
36 changes: 27 additions & 9 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,20 @@ export interface JwtPresentationPayload {
[x: string]: any
}

export type IssuerType = { id: string; [x: string]: any } | string
export type DateType = string | Date
/**
* used as input when creating Verifiable Credentials
*/
export interface CredentialPayload {
export interface CredentialPayloadInput {
'@context': string[]
id?: string
type: string[]
issuer: string
issuanceDate: Date | string
expirationDate?: Date | string
issuer: IssuerType
issuanceDate: DateType
expirationDate?: DateType
credentialSubject: {
id: string,
id: string
[x: string]: any
}
credentialStatus?: CredentialStatus
Expand All @@ -56,8 +58,24 @@ export interface CredentialPayload {
}

/**
* used as input when creating Verifiable Presentations
*/
* This is meant to reflect unambiguous types for the properties in `CredentialPayloadInput`
*/
interface NarrowDefinitions {
issuer: Exclude<IssuerType, string>
issuanceDate: string
expirationDate?: string
}

/**
* Replaces the matching property types of T with the ones in U
*/
type Replace<T, U> = Omit<T, keyof U> & U

export type Credential = Replace<CredentialPayloadInput, NarrowDefinitions>

/**
* used as input when creating Verifiable Presentations
*/
export interface PresentationPayload {
'@context': string[]
type: string[]
Expand All @@ -71,11 +89,11 @@ export interface Proof {
[x: string]: any
}

export type Verifiable<T> = T & { proof: Proof }
export type Verifiable<T> = Readonly<T> & { proof: Proof }
export type JWT = string

export type VerifiablePresentation = Verifiable<PresentationPayload> | JWT
export type VerifiableCredential = Verifiable<CredentialPayload> | JWT
export type VerifiableCredential = Verifiable<CredentialPayloadInput> | JWT

export interface Issuer {
did: string
Expand Down

0 comments on commit ffbd67f

Please sign in to comment.