import base64url from 'base64url' ; import { cryptoClients , SECP256K1Client } from './cryptoClients' ; import { MissingParametersError } from './errors' ; import { Json } from './decode' ; import { hashSha256 , hashSha256Async } from './cryptoClients/sha256' ; function createSigningInput ( payload , header ) { const tokenParts = [ ] ; const encodedHeader = base64url . encode ( JSON . stringify ( header ) ) ; tokenParts . push ( encodedHeader ) ; const encodedPayload = base64url . encode ( JSON . stringify ( payload ) ) ; tokenParts . push ( encodedPayload ) ; const signingInput = tokenParts . join ( '.' ) ; return signingInput ; } export function createUnsecuredToken ( payload ) { const header = { typ : 'JWT' , alg : 'none' } ; return createSigningInput ( payload , header ) + '.' ; } export interface SignedToken { header : string [ ] ; payload : string ; signature : string [ ] ; } export class TokenSigner { tokenType : string ; cryptoClient : typeof SECP256K1Client ; rawPrivateKey : string ; constructor ( signingAlgorithm , rawPrivateKey ) { if ( ! ( signingAlgorithm && rawPrivateKey ) ) { throw new MissingParametersError ( 'a signing algorithm and private key are required' ) ; } if ( typeof signingAlgorithm !== 'string' ) { throw new Error ( 'signing algorithm parameter must be a string' ) ; } signingAlgorithm = signingAlgorithm . toUpperCase ( ) ; if ( ! cryptoClients . hasOwnProperty ( signingAlgorithm ) ) { throw new Error ( 'invalid signing algorithm' ) ; } this . tokenType = 'JWT' ; this . cryptoClient = cryptoClients [ signingAlgorithm ] ; this . rawPrivateKey = rawPrivateKey ; } header ( header = { } ) { const defaultHeader = { typ : this . tokenType , alg : this . cryptoClient . algorithmName } ; return Object . assign ( { } , defaultHeader , header ) ; } sign ( payload ) ; sign ( payload , expanded , customHeader ? ) ; sign ( payload , expanded , customHeader ? ) ; sign ( payload , expanded = false , customHeader = { } ) { const header = this . header ( customHeader ) ; const signingInput = createSigningInput ( payload , header ) ; const signingInputHash = hashSha256 ( signingInput ) ; return this . createWithSignedHash ( payload , expanded , header , signingInput , signingInputHash ) ; } signAsync ( payload ) ; signAsync ( payload , expanded , customHeader ? ) ; signAsync ( payload , expanded , customHeader ? ) ; async signAsync ( payload , expanded = false , customHeader = { } ) { const header = this . header ( customHeader ) ; const signingInput = createSigningInput ( payload , header ) ; const signingInputHash = await hashSha256Async ( signingInput ) ; return this . createWithSignedHash ( payload , expanded , header , signingInput , signingInputHash ) ; } createWithSignedHash ( payload , expanded , header , signingInput , signingInputHash ) { const signature = this . cryptoClient . signHash ( signingInputHash , this . rawPrivateKey ) ; if ( expanded ) { const signedToken = { 'header' : [ base64url . encode ( JSON . stringify ( header ) ) ] , 'payload' : JSON . stringify ( payload ) , 'signature' : [ signature ] } ; return signedToken ; } else { return [ signingInput , signature ] . join ( '.' ) ; } } }