UNPKG

oidc-lib

Version:

A library for creating OIDC Service Providers

969 lines (836 loc) 31.9 kB
module.exports = { init: init }; var rwlQueue = {}; const MQTT_MONIKER = 'n/a'; function init(global_pk, client_module_name, client_name, config){ var instance = new OIDC_RP(global_pk, client_module_name, client_name, config); return instance; } class OIDC_RP { constructor(global_pk, client_module_name, client_name, config){ this.pk = global_pk; this.client_module_name = client_module_name; this.requests = {}; this.viewPath = pk.path.join(process.cwd(), client_module_name, 'views'); this.response_mode = 'fragment'; if (config.response_mode){ this.response_mode = config.response_mode; } this.redirect_uri = pk.util.httpsServerUrls['CLIENT_API'].href + this.client_module_name + "/authresp/" + client_name; this.init_uri = pk.util.httpsServerUrls['CLIENT_API'].href + this.client_module_name + "/oidc_init/" + client_name; this.integrateClaims_uri = pk.util.httpsServerUrls['CLIENT_API'].href + this.client_module_name + "/integrateClaims/" + client_name; this.config = config; this.cookieId = client_module_name + '_token'; } get_redirect_uri(){ return this.redirect_uri; } redirect_if_initiation_request(req, res, client){ if (req.query.iss){ var parsedUrl = pk.util.url(req.originalUrl); var redirect = pk.util.httpsServerUrls['CLIENT_API'].href + this.client_module_name + "/oidc_init/" + client + parsedUrl.search; res.setHeader("Location", redirect); res.statusCode = 302; res.end(); return true; } return false; } async login_initiation(req, res){ this.pk.util.log_debug('--- OIDC_RP: LOGIN INITIATION -----'); pk.util.log_protocol('LOGIN INITIATION - Api Request', 'client_api', { name: '{OIDC_CORE}', url: '#ThirdPartyInitiatedLogin' }, pk.util.inbound_wire_content(req)); var payload = await this.getIdToken(req, res); if (payload && payload.id_token){ var client = client_from_request(req); var target_link_uri = pk.util.fullyDecodeURIComponent(req.query.target_link_uri); var link_uri_params = this.config.link_uri_params; link_uri_params = link_uri_params ? link_uri_params : { iss: payload.id_token.issuer, login_hint: payload.state.login_hint }; pk.util.log_debug('Adding parameters to the link_uri', link_uri_params); target_link_uri += pk.util.createParameterString(link_uri_params); pk.util.log_debug("Token existed so redirecting to: ", target_link_uri); res.setHeader("Location", target_link_uri); res.statusCode = 302; res.end(); pk.util.log_protocol('LOGIN INITIATION - Api Response', 'client_api', { name: '{OIDC_CORE}', url: '#SelfIssuedRequest' }, pk.util.outbound_wire_content(res, '')); } } register_metadata(){ var overRides = { 'redirect_uri': this.redirect_uri } return overRides; }; async getIdToken(req, res){ this.pk.util.log_debug('--- OIDC_RP: GET ID TOKEN -----'); // var payload = await this.getTokenFromCookie(req, res); var payload = false; if (payload === false){ var client = client_from_request(req); console.log("No token exists in a cookie at " + client); // no cookie - initiate login var iss = req.query.iss; if (!iss){ this.fatal_error(res, "You need to scan this qrcode with a DID wallet", "Bad parameters calling login_initiation", "SIOP iss (issuer) parameter is missing"); } var target_link_uri = pk.util.fullyDecodeURIComponent(req.query.target_link_uri); target_link_uri = target_link_uri ? target_link_uri : iss; var login_hint = req.query.login_hint; var li_parameters; if (req.query.li_parameters){ li_parameters = JSON.parse(pk.base64url.decode(pk.base64url.fromBase64(req.query.li_parameters))); } await this.process_login_initiation(req, res, iss, client, target_link_uri, login_hint, li_parameters); } else{ console.log("Token retrieved from cookie"); return payload; } } async getTokenFromCookie(req, res){ var client = client_from_request(req); var cookie = req.cookies[client + "_token"]; if (cookie){ var payload = await pk.simple_crypto.decrypt_symmetric(cookie); if (payload){ // return payload.id_token return payload; } } return false; } async process_login_initiation(req, res, iss, client, target_link_uri, login_hint, li_parameters){ this.pk.util.log_debug('--- OIDC_RP: PROCESS LOGIN INITIATION -----'); this.pk.util.log_debug('redirecting to SIOP (' + client + ') to obtain token'); var serviceUrl = this.pk.util.httpsServerUrls['CLIENT_API'].href; var redirectUrl = this.redirect_uri; console.log('The issuer will return the token to ' + redirectUrl + '.'); var company_logo = this.config.company_logo; if (company_logo){ company_logo = company_logo.startsWith('http') ? this.config.company_logo : pk.util.httpsServerUrls['CLIENT_API'].href + this.config.company_logo.substr(1) } var registration = { client_id: redirectUrl, redirect_uri: redirectUrl, client_name: this.config.company_name, client_uri: pk.util.httpsServerUrls['CLIENT_API'].href + this.config.client, company_logo: company_logo }; var nonce = await this.pk.simple_crypto.createNumericCode(9); this.requests[nonce] = registration; var registrationString = JSON.stringify(registration); var state_parameters = { target_link_uri: target_link_uri, } // the li_parameters are put onto the state // when returned bu issuer they are put back into li_parameters if (li_parameters){ if (li_parameters.rwl){ li_parameters.wallet = iss; } for (var k in li_parameters){ state_parameters[k] = li_parameters[k]; } } var state = pk.util.createParameterString(state_parameters).substr(1); console.log("state generated in process_login_initiation", state); var requested_id_token_claims = { "vc": { types: [ this.config.input_credential_type ]} } var ancillaryClaims = this.config.ancillary_claims; if (ancillaryClaims){ var acArray = ancillaryClaims.split(' '); for (var i=0; i< acArray.length; i++){ var name = acArray[i]; var obj = null; requested_id_token_claims[name] = obj; } } var requestedClaimsParam = JSON.stringify({ "id_token": requested_id_token_claims }); var prompt = this.config.prompt; var response_type = this.config.response_type ? this.config.response_type : "id_token"; if (li_parameters){ prompt = li_parameters.prompt ? li_parameters.prompt : prompt; } var oidcRequest = { client_id: registration.client_id, redirect_uri: registration.redirect_uri, response_type: response_type, prompt: prompt, state: state, nonce: nonce, claims: requestedClaimsParam, scope: this.config.scope, login_hint: login_hint, registration: registrationString } if (li_parameters && li_parameters.response_mode){ oidcRequest['response_mode'] = li_parameters.response_mode; } else if (this.response_mode !== 'fragment'){ oidcRequest['response_mode'] = this.response_mode; } var outbound_content; const SELFISSUED = 'https://self-issued.me'; if (iss === SELFISSUED){ res.json(oidcRequest); outbound_content = oidcRequest; } else{ var requestParams = pk.util.createParameterString(oidcRequest); var loginRedirect = iss + requestParams; outbound_content = loginRedirect; console.log("SIOP parameters: ", requestParams); res.redirect(loginRedirect); } pk.util.log_protocol('LOGIN INITIATION - Api Response', 'client_api', { name: '{OIDC_CORE}', url: '#SelfIssuedRequest' }, pk.util.outbound_wire_content(res, outbound_content)); } processAuthResponse(req, res){ this.pk.util.log_debug('--- OIDC_RP: PROCESS AUTH RESPONSE'); var protocolArray = [ { title: '', value: pk.util.inbound_wire_content(req) }, { title: 'Hash containing response', value: 'The browser prevents access to the fragment for security reasons.\r\nIt is resubmitted through a POST as explained in the spec Fragment Notes.' } ] pk.util.log_protocol('AUTHENTICATION RESPONSE - Api Request via hash', 'client_api', { name: '{OIDC_CORE}', url: '#FragmentNotes' }, protocolArray); pk.util.log_detail("The SIOP has responded.") if (req.query.error){ this.displayError(req, res); return; } pk.util.log_detail("The browser will submit the token to the implicitPost endpoint."); res.render(this.viewPath + "/implicit", { layout: 'main', }); pk.util.log_protocol('AUTHENTICATION RESPONSE - Api Response', 'client_api', pk.util.outbound_wire_content(res, '(redirect to the post endpoint)')); } displayError(req, res){ pk.util.log_protocol('ERROR - Api Request via hash', 'client_api', pk.util.inbound_wire_content(req)); res.render(this.viewPath + '/error_received', req.query); pk.util.log_protocol('ERROR - Api Response', 'client_api', pk.util.outbound_wire_content(res, req.query)); } async processImplicitPost(req, res, defaultResponseMode){ this.pk.util.log_debug('--- OIDC_RP: PROCESS IMPLICIT POST'); pk.util.log_debug("The browser has posted the response for processing.") // this.pk.util.log_detail('body params', req.body); var client = client_from_request(req); var contentObject = req.body; if (Object.keys(contentObject).length === 0){ var content = { error: 'invalid_token', error_description: 'token is empty' } res.render(this.viewPath + '/error_received', content); pk.util.log_protocol('AUTHENTICATION RESPONSE - Api Response', 'client_api', pk.util.outbound_wire_content(res, content)); return; } if (contentObject.error !== undefined){ var state = pk.util.parseParameterString(contentObject.state); var content = { result: "post", newUrl: state.target_link_uri, error: contentObject.error, error_description: contentObject.error_description, statusCode: contentObject.statusCode } res.send(content); res.end(); pk.util.log_protocol('AUTHENTICATION RESPONSE - Response', 'client_api', pk.util.outbound_wire_content(res, content)); return; } if (!contentObject.id_token){ throw ("this oidc_rp requires an implicit post to include a SELFISSUED token"); } var components = contentObject.id_token.split('.'); var idTokenString = Buffer.from(components[1], 'base64').toString('utf8'); // var idTokenString = new Buffer(components[1], 'base64').toString('utf8'); var idToken = JSON.parse(idTokenString); if (!idToken.nonce){ throw ("this oidc_rp requires that its nonce is present in the token it receives"); } // now we've decoded the token decoded we can properly log the inbound protocol log_authResponseProtocol(req, 'AUTHENTICATION RESPONSE - Api Request via post', idToken); var requestInfo = this.requests[idToken.nonce]; if (!requestInfo){ throw ("the nonce presented is not valid"); } var client_id = requestInfo.client_id; if (idToken.aud !== client_id){ throw('Invalid Token - bad audience'); } // if (idToken.iss !== SELFISSUED){ if (!idToken.sub_jwk){ throw ("this oidc_rp only accepts SELFISSUED tokens"); } if (idToken.sub.startsWith('did:')){ await pk.token.validate_did_key(idToken.sub, idToken.sub_jwk); } else if (idToken.sub.startsWith('urn:uuid:')){ await pk.token.validate_urn_key(idToken.sub, idToken.sub_jwk); } // special actions with state. // the uri '/debug' means don't redirect - just debug // In our implementation the did is represented in a new persona // associated with the issuer by having a kid using the redirect url // of the claims issuer client) // parameters of login initiation endpoint are left as is // other parameters are reassembled into li_parameters - opposite // of what happens in process_login_initiation var state = pk.util.parseParameterString(contentObject.state); var target_uri = state.target_link_uri; if (target_uri === "/debug"){ target_uri = undefined; } var li_parameters = {}; for (var k in state){ switch (k){ case "iss": case "login_hint": case "target_link_uri": break; default: li_parameters[k] = state[k]; } } console.log("state", state); /* if (!state.login_hint){ state["login_hint"] = idToken.sub; } */ var verifiedResult = await this.pk.simple_crypto.verify_signature(contentObject.id_token, idToken.sub_jwk); if (!verifiedResult){ throw "Signature verification failure"; } idToken = await this.getIntegratedClaims(idToken); if (idToken.vp && idToken.vp.verifiableCredential){ idToken = await this.insertPresentedVcs(idToken); } var idTokenString = JSON.stringify(idToken); if (target_uri){ if (!this.isValidTargetLinkUri(target_uri)){ throw "Invalid target_link_uri presented"; } if (!li_parameters.noRedirectOnPost){ if (li_parameters && li_parameters.rwl !== 'false'){ console.log('The token is being added to rwl queue'); await this.addToRwlQueue(li_parameters.rwl, target_uri, idTokenString); var content = { result: "redirect", newUrl: li_parameters.wallet } res.send(content); res.end(); pk.util.log_protocol('APPLICATION AUTHORIZATION RESPONSE - Api Response (browser will post to app with rwl)', 'client_api', { name: '{TRYBE_OIDC}', url:'#RwlQueue'}, pk.util.outbound_wire_content(res, content)); } else if (target_uri.endsWith('.html')){ console.log('The browser will be instructed to redirect to html: ', target_uri); var content = { result: "redirect", newUrl: target_uri + '#' + JSON.stringify(idToken.presentedVcs[0].vc) } res.send(content); res.end(); } else{ console.log('The browser will be instructed to post to: ', target_uri); var content = { result: "post", newUrl: target_uri, id_token: idTokenString } if (li_parameters && li_parameters.response_mode === 'form_post'){ res.render(this.viewPath + "/post_to_application", content); } else{ content.li_parameters = li_parameters; res.send(content); res.end(); } pk.util.log_protocol('APPLICATION AUTHENTICATION RESPONSE - Api Response (browser will post to target_link_uri)', 'client_api', { name: '{TRYBE_OIDC}', url:'#DefaultResponseMode'}, pk.util.outbound_wire_content(res, content)); } } else{ var url = target_uri; var id_token = idTokenString; var postData = pk.querystring.stringify( { id_token: id_token, li_parameters: li_parameters }); var options = { url: url, method: 'POST', headers: [ { name: 'Content-type', value: 'application/x-www-form-urlencoded' } ], // headers: [ { name: 'Content-type', value: 'application/json' } ], postData: postData }; res.statusCode = 200; res.end(); var result = await pk.util.jsonHttpData(options); pk.util.log_protocol('APPLICATION AUTHENTICATION RESPONSE - Api Response (via post - non-standard response_mode)', 'client_api', { name: '{TRYBE_OIDC}', url:'#NonStandardResponseMode'}, pk.util.outbound_wire_content(res, postData)); } return; } var tokenAsArray = tokenToArray(idToken, this.pk); var issuerMessage = idToken.iss ? 'Token received from ' + idToken.iss : 'Token received'; var codeMessage = contentObject.code ? 'Code: ' + contentObject.code : ''; var access_tokenMessage = contentObject.access_token ? 'Access Token: ' + contentObject.access_token : ''; var tokenTypeMessage = contentObject.token_type ? 'Token Type: ' + contentObject.token_type : ''; var payload = { issuerMessage: issuerMessage, tokenAsArray: tokenAsArray, codeMessage: codeMessage + '<br/>', access_tokenMessage: access_tokenMessage + '<br/>', tokenTypeMessage: tokenTypeMessage + '<br/>' }; requestInfo.payload = payload; this.requests[idToken.nonce] = requestInfo; var content = { result: "tokenResult", nonce: idToken.nonce } res.send(content); res.end(); pk.util.log_protocol('AUTHENTICATION RESPONSE - Response (via post - no target_link_uri)', 'client_api', pk.util.outbound_wire_content(res, content)); function log_authResponseProtocol(req, protocolTitle, idToken){ var protocolArray = [ { title: '', value: pk.util.inbound_wire_content(req) }, { title: 'Decoded id_token', value: idToken } ] if (idToken.vp && idToken.vp.verifiableCredential){ var compactCred = idToken.vp.verifiableCredential[0]; if (compactCred){ var vcComponents = compactCred.split('.'); var vcTokenString = Buffer.from(vcComponents[1], 'base64').toString('utf8'); // var idTokenString = new Buffer(components[1], 'base64').toString('utf8'); var vcToken = JSON.parse(vcTokenString); protocolArray.push({ title: 'Decoded verifiableCredential', value: vcToken}); } } pk.util.log_protocol(protocolTitle, 'client_api', { name: '{VC_DATA_MODEL_1}', url: '#json-web-token' }, protocolArray); } } isValidTargetLinkUri(target_link_uri){ target_link_uri = pk.util.fullyDecodeURIComponent(target_link_uri); var retVal = true; var paramsAt = target_link_uri.indexOf('?'); var tlu = paramsAt < 0 ? target_link_uri : target_link_uri.substr(0, paramsAt); var validUris = this.config.valid_target_link_uris; if (!validUris || validUris.indexOf(tlu) < 0){ console.log("invalid target_link_uri presented: " + tlu); console.log("valid_target_link_uris in configuration: " + this.config.valid_target_link_uris); console.log("Fix your code or your configuration"); retVal = false; } return retVal; } async addToRwlQueue(rwl, target_uri, idTokenString){ var b64String = pk.base64url.encode(idTokenString); var record = { id: rwl, target_uri: target_uri, b64String: b64String } await pk.dbs['client_api'].provider.createOrUpdateDocument(pk.dbs['client_api'], 'rwlqueue', record); pk.util.log_debug("added rwl to database"); try { pk.util.log_detail('Publishing to mqtt'); var url = 'mqtts://humanpresent.com'; var client = pk.mqtt.connect(url); var result = await this.publishToMqtt(client, rwl, rwl); client.end(); } catch(err){ pk.util.log_error('Error in publishToMqtt', err); } } publishToMqtt(client, topic, message){ return new Promise((resolve, reject) => { try{ client.on('error', function (error) { reject ("MQTT CONNECT ERROR: " + JSON.stringify( error)); }); client.on('connect', function () { pk.util.log_detail("connected - publishing"); client.publish(topic, message); resolve('mqtt published (' + topic + '/' + message + ')'); }); client.on('disconnect', function () { pk.util.log_detail("disconnect event"); reject("weird - disconnect"); }); client.on('close', function () { pk.util.log_detail("close event - client ending"); reject("client closed"); }); client.on('offline', function () { pk.util.log_detail("offline event"); reject("client offline"); }) } catch(err){ pk.util.log_detail ("Error thrown", err); reject(err); } }); } async processRemoteWalletToken(req, res){ pk.util.log_protocol('REMOTE WALLET TOKEN - Api Request', 'client_api', pk.util.inbound_wire_content(req)); try{ var rwl = req.query.rwl; if (!rwl || rwl === 'false'){ throw ('rwl not specified'); } var info = await pk.dbs['client_api'].provider.getDocument(pk.dbs['client_api'], 'rwlqueue', rwl); if (!info){ throw('rwl not found'); } await pk.dbs['client_api'].provider.deleteDocument(pk.dbs['client_api'], 'rwlqueue', rwl); var idTokenString = pk.base64url.decode(info.b64String); var content = { layout: 'main', target_link_uri: info.target_uri, idTokenString: idTokenString } res.render(this.viewPath + "/rwl", content); pk.util.log_protocol('REMOTE WALLET TOKEN - Api Response (browser posts form)', 'client_api', pk.util.outbound_wire_content(res, content)); } catch(err){ var content = { layout: 'main', error: err } res.render(this.viewPath + "/rwl", content); pk.util.log_protocol('REMOTE WALLET TOKEN - Api Response (error)', 'client_api', pk.util.outbound_wire_content(res, content)); } } get_client_credential_request_url(client, iss, target_link_uri, li_parameters){ if (!target_link_uri){ target_link_uri = '/debug'; } var params = {}; if (iss){ params['iss'] = iss; } if (target_link_uri){ if (!this.isValidTargetLinkUri(target_link_uri)){ throw('invalid target_link_uri: ' + target_link_uri); } params['target_link_uri'] = target_link_uri; } if (li_parameters){ params['li_parameters'] = li_parameters; } var didClientEndpoint = this.init_uri + pk.util.createParameterString(params, false); return didClientEndpoint; } async get_client_credential_request_qrcode(client, target_link_uri, li_parameters){ var iss; // undefined for time being in qr code... var trimmed_li_parameters; if (li_parameters && li_parameters.rwl){ trimmed_li_parameters = { rwl: li_parameters.rwl } } var didClientEndpoint = this.get_client_credential_request_url(client, iss, target_link_uri, trimmed_li_parameters); return await pk.util.generateUrlQrCode(didClientEndpoint); } // for iss pass req.header('x-did-openid') or req.query.did if the header is absent async get_client_qr_code_and_url(client, iss, target_link_uri, response_mode, li_parameters) { try{ if (target_link_uri && target_link_uri.startsWith('/')){ target_link_uri = pk.util.httpsServerUrls['CLIENT_API'].href + target_link_uri.substring(1); } var qr_code = await this.get_client_credential_request_qrcode(client, target_link_uri, li_parameters); var credential_request_url; if (iss){ credential_request_url = this.get_client_credential_request_url(client, iss, target_link_uri, li_parameters); } var artifacts = { qr_code: qr_code, credential_request_url: credential_request_url, li_parameters: li_parameters, client: client } return artifacts; } catch(err){ return { error: err } } } get_issuer_credential_request_url(issuer, next_step){ var pwa = pk.util.httpsServerUrls['WALLET'].href + pk.util.WALLET_ENDPOINT.substr(1); if (!issuer.startsWith('https')){ var cred_issuer = pk.util.httpsServerUrls['ISSUER_HOST'].href + issuer; } var installPlusCredUrl = pwa + pk.util.createParameterString( { req_cred: cred_issuer, next_step: next_step }); return installPlusCredUrl; } async get_issuer_credential_request_qrcode(issuer, next_step){ var installPlusCredUrl = this.get_issuer_credential_request_url(issuer, next_step); return await pk.util.generateUrlQrCode(installPlusCredUrl); } fatal_error(res, info, error, error_description){ res.render(this.viewPath + "/error_received", { layout: 'main', info: info, error: error, error_description: error_description }) } processTokenResult(req, res){ pk.util.log_protocol('TOKEN RESULT - Api Request', 'client_api', pk.util.inbound_wire_content(req)); var qparams = req.query; var nonce = qparams.nonce; var requestInfo = this.requests[nonce]; var payload = requestInfo.payload; payload['layout'] = 'main'; res.render(this.viewPath + '/tokenResult', payload); pk.util.log_protocol('TOKEN RESULT - Api Response', 'client_api', pk.util.outbound_wire_content(res, payload)); } /* async processClaimSource(req, res){ var potential_err; try{ // TODO: add client validation var adcParams64 = req.query.adc_params; if (!adcParams64){ throw "No b64 adc_params found in call to get_distributed_claim"; } var adcParamsString = pk.base64url.decode(adcParams64); var location_details = JSON.parse(adcParamsString); var jwt; var name_and_source; potential_err = 'Error requesting distributed claim'; var distributedClaims = await pk.token.requestDistributedClaims(location_details); jwt = distributedClaims.JWT; var contentObject = { id_token: jwt } potential_err = 'Error requesting distributed claim' var vcToken = await pk.token.processOPTokenResult(contentObject); if (distributedClaims.info === undefined){ distributedClaims.info = 'This Verifiable Credential was validated by the OIDC client.'; } var responseObj = { vc_token: vcToken, distributedClaims: distributedClaims, status: 'Token signature is valid' }; res.set ({ 'Cache-Control': 'no-cache', 'Pragma': 'no-cache', 'Content-Type': 'application/json' }); res.json(responseObj); } catch(err){ var err_message = potential_err + err; console.log(err_message); } } */ async insertPresentedVcs(idToken){ try{ var potential_err = 'undefined error'; var presentedVcs = []; for (var i=0; i < idToken.vp.verifiableCredential.length; i++){ var JWT = idToken.vp.verifiableCredential[i]; var contentObject = { id_token: JWT } potential_err = 'Error requesting distributed claim' var vcToken = await pk.token.processOPTokenResult(contentObject); var claimHashes = vcToken.ancillaryClaimHashes; if (claimHashes){ var alg = claimHashes.algorithm; var salt = claimHashes.salt; for (var claimName in claimHashes){ var claimVal = idToken[claimName]; if (!claimVal){ continue; } var claimHash = pk.simple_crypto.claimHash(alg, salt, claimVal); if (claimHash !== claimHashes[claimName]){ console.log("ERROR: tampered ancillary claim: " + claimName + ' - ' + claimVal); idToken[claimName] = "TAMPERED VALUE"; } } } presentedVcs[i] = vcToken; } idToken.presentedVcs = presentedVcs; return idToken; } catch (err){ console.log('getPresentedVcs - ' + potential_err); console.log(err); } } async getIntegratedClaims(idToken){ try{ var potential_err = ''; var jwt; var vcs = {}; for (var source in idToken._claim_sources){ var location_details = idToken._claim_sources[source]; potential_err = 'Error requesting distributed claim'; var distributedClaims = await pk.token.requestDistributedClaims(location_details); jwt = distributedClaims.JWT; var contentObject = { id_token: jwt } potential_err = 'Error requesting distributed claim' var vcToken = await pk.token.processOPTokenResult(contentObject); vcs[source] = vcToken; } for (var claimName in idToken._claim_names){ var source = idToken._claim_names[claimName]; idToken[claimName] = vcs[source][claimName]; } for (var source in idToken._claim_sources){ var vcToken = vcs[source]; var claimHashes = vcToken.persentationClaimHashes; if (claimHashes){ var alg = claimHashes.algorithm; var salt = claimHashes.salt; for (var claimName in claimHashes){ var claimVal = idToken[claimName]; if (!claimVal){ continue; } var claimHash = pk.simple_crypto.claimHash(alg, salt, claimVal); if (claimHash !== claimHashes[claimName]){ idToken[claimName] = "Tampered Value"; } } } } return idToken; } catch (err){ throw (err); } } getAppConfig(req, res){ res.json(this.config); res.end(); } } /*****************************************************/ function tokenToArray(token, pk){ var arr = []; for (var key in token){ var val = token[key]; if (typeof val === 'object'){ val = '<pre>' + JSON.stringify(val, null, " ") + '</pre>'; } if (key === '_claim_sources'){ var buttonHtml = '<table>'; var claim_sources = token[key]; for (var claim_source_id in claim_sources){ var location_details = token._claim_sources[claim_source_id]; var fetcherParam = pk.base64url.encode(JSON.stringify(location_details)); var rowId = 'claim_sources_' + claim_source_id; buttonHtml += '<tr id="' + rowId + '">'; buttonHtml += '<td><button class="btn btn-primary btn-sm" \ onclick="vfetch(\'' + fetcherParam + '\', \'' + rowId + '\');">' + 'Validate ' + claim_source_id + '</button>' buttonHtml += '</td></tr>\r\n'; } val += buttonHtml + '</table>' } if (key === 'vp'){ var buttonHtml = '<table>'; var jwtArray = token[key].verifiableCredential; for (var i=0; i < jwtArray.length; i++){ var location_details = { "JWT": jwtArray[i] }; var fetcherParam = pk.base64url.encode(JSON.stringify(location_details)); var rowId = 'vp_source'; buttonHtml += '<tr id="' + rowId + '">'; buttonHtml += '<td><button class="btn btn-primary btn-sm" \ onclick="vfetch(\'' + fetcherParam + '\', \'' + rowId + '\');">Validate Credential</button>' buttonHtml += '</td></tr>\r\n'; } val += buttonHtml + '</table>' } var claim = { name: key, value: val }; arr.push(claim); } return arr; } // code remnant from when init was called from // a the erifier itself. Need to see if I can // eliminate this function client_from_request(req){ var client = req.params.client; if (!client){ client = req.path.substring(1); } return (client); } function replaceHandlebar(content, sources){ var regex = /(\{\{((?:[a-z]|[A-Z]|[0-9]|[_\-.@#$])+)\}\})/g; var result = content; var groups; while (groups = regex.exec(content)){ var comps = groups[2].split("."); // Below, {{token.sub}} should give sources[token][sub] // so as to pluck value from right place var replaceWith = sources[comps[0]][comps[1]]; result = result.replace(groups[1], replaceWith); } return result; } /* function populateParametersFromResponse(parameterObject, token, state){ var sources = {token: token, state: state}; for (var key in parameterObject){ var value = replaceHandlebar(parameterObject[key], sources); parameterObject[key] = value; } return parameterObject; } */