oidc-lib
Version:
A library for creating OIDC Service Providers
969 lines (836 loc) • 31.9 kB
JavaScript
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;
}
*/