You're Invited: Meet the Socket team at BSidesSF and RSAC - April 27 - May 1.RSVP
Socket
Sign inDemoInstall
Socket

uport

Package Overview
Dependencies
Maintainers
4
Versions
63
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

uport - npm Package Compare versions

Comparing version

to
0.7.0-alpha-10

dist/uport-credentials.js

611

lib/__tests__/Credentials-test.js

@@ -21,14 +21,2 @@ 'use strict';

var _tweetnacl = require('tweetnacl');
var _tweetnacl2 = _interopRequireDefault(_tweetnacl);
var _tweetnaclUtil = require('tweetnacl-util');
var _tweetnaclUtil2 = _interopRequireDefault(_tweetnaclUtil);
var _nock = require('nock');
var _nock2 = _interopRequireDefault(_nock);
var _mockdate = require('mockdate');

@@ -228,3 +216,3 @@

describe('requestDisclosure()', function () {
describe('createDisclosureRequest()', function () {
var createAndVerify = function () {

@@ -239,3 +227,3 @@ var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4() {

_context4.next = 2;
return uport.requestDisclosure(params);
return uport.createDisclosureRequest(params);

@@ -501,3 +489,3 @@ case 2:

describe('LEGACY createRequest()', function () {
describe('LEGACY createDisclosureRequest()', function () {
var createAndVerify = function () {

@@ -512,3 +500,3 @@ var _ref16 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee16() {

_context16.next = 2;
return uport.createRequest(params);
return uport.createDisclosureRequest(params);

@@ -572,3 +560,3 @@ case 2:

_context18.next = 2;
return uport.disclose(params);
return uport.createDisclosureResponse(params);

@@ -629,3 +617,3 @@ case 2:

_context20.next = 2;
return uport.requestDisclosure();
return uport.createDisclosureRequest();

@@ -650,39 +638,102 @@ case 2:

describe('attest()', function () {
describe('createSignVerificationRequest()', function () {
it('creates a valid JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee21() {
var jwt;
return _regenerator2.default.wrap(function _callee21$(_context21) {
while (1) {
switch (_context21.prev = _context21.next) {
case 0:
_context21.next = 2;
return uport.createSignVerificationRequest({ claim: { test: { prop1: 1, prop2: 2 } } }, 'did:uport:223ab45');
case 2:
jwt = _context21.sent;
_context21.t0 = expect;
_context21.next = 6;
return (0, _didJwt.verifyJWT)(jwt, { audience: did });
case 6:
_context21.t1 = _context21.sent;
return _context21.abrupt('return', (0, _context21.t0)(_context21.t1).toMatchSnapshot());
case 8:
case 'end':
return _context21.stop();
}
}
}, _callee21, undefined);
})));
});
describe('createVerification()', function () {
beforeAll(function () {
return mockresolver();
});
it('has correct payload in JWT for an attestation', function () {
return uport.attest({ sub: '0x112233', claim: { email: 'bingbangbung@email.com' }, exp: 1485321133 + 1 }).then(function (jwt) {
return expect((0, _didJwt.verifyJWT)(jwt)).toMatchSnapshot();
});
});
it('has correct payload in JWT for an attestation', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee23() {
return _regenerator2.default.wrap(function _callee23$(_context23) {
while (1) {
switch (_context23.prev = _context23.next) {
case 0:
return _context23.abrupt('return', uport.createVerification({ sub: 'did:uport:223ab45', claim: { email: 'bingbangbung@email.com' }, exp: 1485321133 + 1 }).then(function () {
var _ref23 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee22(jwt) {
var decoded;
return _regenerator2.default.wrap(function _callee22$(_context22) {
while (1) {
switch (_context22.prev = _context22.next) {
case 0:
_context22.next = 2;
return (0, _didJwt.verifyJWT)(jwt);
case 2:
decoded = _context22.sent;
return _context22.abrupt('return', expect(decoded).toMatchSnapshot());
case 4:
case 'end':
return _context22.stop();
}
}
}, _callee22, undefined);
}));
return function (_x6) {
return _ref23.apply(this, arguments);
};
}()));
case 1:
case 'end':
return _context23.stop();
}
}
}, _callee23, undefined);
})));
});
describe('authenticate()', function () {
describe('verifyDisclosureResponse()', function () {
var createShareResp = function () {
var _ref21 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee21() {
var _ref24 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee24() {
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var req;
return _regenerator2.default.wrap(function _callee21$(_context21) {
return _regenerator2.default.wrap(function _callee24$(_context24) {
while (1) {
switch (_context21.prev = _context21.next) {
switch (_context24.prev = _context24.next) {
case 0:
_context21.next = 2;
return uport.requestDisclosure({ requested: ['name', 'phone'] });
_context24.next = 2;
return uport.createDisclosureRequest({ requested: ['name', 'phone'] });
case 2:
req = _context21.sent;
return _context21.abrupt('return', uport.disclose((0, _extends3.default)({}, payload, { req: req })));
req = _context24.sent;
return _context24.abrupt('return', uport.createDisclosureResponse((0, _extends3.default)({}, payload, { req: req })));
case 4:
case 'end':
return _context21.stop();
return _context24.stop();
}
}
}, _callee21, this);
}, _callee24, this);
}));
return function createShareResp() {
return _ref21.apply(this, arguments);
return _ref24.apply(this, arguments);
};

@@ -692,31 +743,31 @@ }();

var createShareRespWithVerifiedCredential = function () {
var _ref22 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee22() {
var _ref25 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee25() {
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var req, attestation;
return _regenerator2.default.wrap(function _callee22$(_context22) {
return _regenerator2.default.wrap(function _callee25$(_context25) {
while (1) {
switch (_context22.prev = _context22.next) {
switch (_context25.prev = _context25.next) {
case 0:
_context22.next = 2;
return uport.requestDisclosure({ requested: ['name', 'phone'] });
_context25.next = 2;
return uport.createDisclosureRequest({ requested: ['name', 'phone'] });
case 2:
req = _context22.sent;
_context22.next = 5;
return uport.attest(claim);
req = _context25.sent;
_context25.next = 5;
return uport.createVerification(claim);
case 5:
attestation = _context22.sent;
return _context22.abrupt('return', uport.disclose((0, _extends3.default)({}, payload, { verified: [attestation], req: req })));
attestation = _context25.sent;
return _context25.abrupt('return', uport.createDisclosureResponse((0, _extends3.default)({}, payload, { verified: [attestation], req: req })));
case 7:
case 'end':
return _context22.stop();
return _context25.stop();
}
}
}, _callee22, this);
}, _callee25, this);
}));
return function createShareRespWithVerifiedCredential() {
return _ref22.apply(this, arguments);
return _ref25.apply(this, arguments);
};

@@ -732,18 +783,18 @@ }();

it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee23() {
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee26() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee23$(_context23) {
return _regenerator2.default.wrap(function _callee26$(_context26) {
while (1) {
switch (_context23.prev = _context23.next) {
switch (_context26.prev = _context26.next) {
case 0:
_context23.next = 2;
_context26.next = 2;
return createShareResp({ own: { name: 'Davie', phone: '+15555551234' } });
case 2:
jwt = _context23.sent;
_context23.next = 5;
return uport.authenticate(jwt);
jwt = _context26.sent;
_context26.next = 5;
return uport.verifyDisclosureResponse(jwt);
case 5:
profile = _context23.sent;
profile = _context26.sent;

@@ -754,24 +805,24 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context23.stop();
return _context26.stop();
}
}
}, _callee23, undefined);
}, _callee26, undefined);
})));
it('returns profile mixing public and private claims and verified credentials', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee24() {
it('returns profile mixing public and private claims and verified credentials', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee27() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee24$(_context24) {
return _regenerator2.default.wrap(function _callee27$(_context27) {
while (1) {
switch (_context24.prev = _context24.next) {
switch (_context27.prev = _context27.next) {
case 0:
_context24.next = 2;
_context27.next = 2;
return createShareRespWithVerifiedCredential({ own: { name: 'Davie', phone: '+15555551234' } });
case 2:
jwt = _context24.sent;
_context24.next = 5;
return uport.authenticate(jwt);
jwt = _context27.sent;
_context27.next = 5;
return uport.verifyDisclosureResponse(jwt);
case 5:
profile = _context24.sent;
profile = _context27.sent;

@@ -782,24 +833,24 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context24.stop();
return _context27.stop();
}
}
}, _callee24, undefined);
}, _callee27, undefined);
})));
it('returns profile with only public claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee25() {
it('returns profile with only public claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee28() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee25$(_context25) {
return _regenerator2.default.wrap(function _callee28$(_context28) {
while (1) {
switch (_context25.prev = _context25.next) {
switch (_context28.prev = _context28.next) {
case 0:
_context25.next = 2;
_context28.next = 2;
return createShareResp();
case 2:
jwt = _context25.sent;
_context25.next = 5;
return uport.authenticate(jwt);
jwt = _context28.sent;
_context28.next = 5;
return uport.verifyDisclosureResponse(jwt);
case 5:
profile = _context25.sent;
profile = _context28.sent;

@@ -810,24 +861,24 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context25.stop();
return _context28.stop();
}
}
}, _callee25, undefined);
}, _callee28, undefined);
})));
it('returns profile with private chain network id claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee26() {
it('returns profile with private chain network id claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee29() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee26$(_context26) {
return _regenerator2.default.wrap(function _callee29$(_context29) {
while (1) {
switch (_context26.prev = _context26.next) {
switch (_context29.prev = _context29.next) {
case 0:
_context26.next = 2;
_context29.next = 2;
return createShareResp({ nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m' });
case 2:
jwt = _context26.sent;
_context26.next = 5;
return uport.authenticate(jwt);
jwt = _context29.sent;
_context29.next = 5;
return uport.verifyDisclosureResponse(jwt);
case 5:
profile = _context26.sent;
profile = _context29.sent;

@@ -838,24 +889,24 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context26.stop();
return _context29.stop();
}
}
}, _callee26, undefined);
}, _callee29, undefined);
})));
it('returns profile with device key claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee27() {
it('returns profile with device key claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee30() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee27$(_context27) {
return _regenerator2.default.wrap(function _callee30$(_context30) {
while (1) {
switch (_context27.prev = _context27.next) {
switch (_context30.prev = _context30.next) {
case 0:
_context27.next = 2;
_context30.next = 2;
return createShareResp({ dad: '0xdeviceKey' });
case 2:
jwt = _context27.sent;
_context27.next = 5;
return uport.authenticate(jwt);
jwt = _context30.sent;
_context30.next = 5;
return uport.verifyDisclosureResponse(jwt);
case 5:
profile = _context27.sent;
profile = _context30.sent;

@@ -866,24 +917,24 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context27.stop();
return _context30.stop();
}
}
}, _callee27, undefined);
}, _callee30, undefined);
})));
it('returns pushToken if available', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee28() {
it('returns pushToken if available', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee31() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee28$(_context28) {
return _regenerator2.default.wrap(function _callee31$(_context31) {
while (1) {
switch (_context28.prev = _context28.next) {
switch (_context31.prev = _context31.next) {
case 0:
_context28.next = 2;
_context31.next = 2;
return createShareResp({ capabilities: ['PUSHTOKEN'] });
case 2:
jwt = _context28.sent;
_context28.next = 5;
return uport.authenticate(jwt);
jwt = _context31.sent;
_context31.next = 5;
return uport.verifyDisclosureResponse(jwt);
case 5:
profile = _context28.sent;
profile = _context31.sent;

@@ -894,32 +945,32 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context28.stop();
return _context31.stop();
}
}
}, _callee28, undefined);
}, _callee31, undefined);
})));
it('handles response with missing challenge', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee29() {
it('handles response with missing challenge', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee32() {
var jwt;
return _regenerator2.default.wrap(function _callee29$(_context29) {
return _regenerator2.default.wrap(function _callee32$(_context32) {
while (1) {
switch (_context29.prev = _context29.next) {
switch (_context32.prev = _context32.next) {
case 0:
_context29.next = 2;
return uport.disclose({ own: { name: 'bob' } });
_context32.next = 2;
return uport.createDisclosureResponse({ own: { name: 'bob' } });
case 2:
jwt = _context29.sent;
jwt = _context32.sent;
expect(uport.authenticate(jwt)).rejects.toMatchSnapshot();
expect(uport.verifyDisclosureResponse(jwt)).rejects.toMatchSnapshot();
case 4:
case 'end':
return _context29.stop();
return _context32.stop();
}
}
}, _callee29, undefined);
}, _callee32, undefined);
})));
});
describe('verifyProfile()', function () {
describe('verifyDisclosure()', function () {
beforeAll(function () {

@@ -931,18 +982,18 @@ return mockresolver({

});
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee30() {
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee33() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee30$(_context30) {
return _regenerator2.default.wrap(function _callee33$(_context33) {
while (1) {
switch (_context30.prev = _context30.next) {
switch (_context33.prev = _context33.next) {
case 0:
_context30.next = 2;
return uport.disclose({ own: { name: 'Davie', phone: '+15555551234' } });
_context33.next = 2;
return uport.createDisclosureResponse({ own: { name: 'Davie', phone: '+15555551234' } });
case 2:
jwt = _context30.sent;
_context30.next = 5;
return uport.verifyProfile(jwt);
jwt = _context33.sent;
_context33.next = 5;
return uport.verifyDisclosure(jwt);
case 5:
profile = _context30.sent;
profile = _context33.sent;

@@ -953,29 +1004,29 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context30.stop();
return _context33.stop();
}
}
}, _callee30, undefined);
}, _callee33, undefined);
})));
it('returns profile mixing public and private claims and verified credentials', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee31() {
it('returns profile mixing public and private claims and verified credentials', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee34() {
var attestation, jwt, profile;
return _regenerator2.default.wrap(function _callee31$(_context31) {
return _regenerator2.default.wrap(function _callee34$(_context34) {
while (1) {
switch (_context31.prev = _context31.next) {
switch (_context34.prev = _context34.next) {
case 0:
_context31.next = 2;
return uport.attest(claim);
_context34.next = 2;
return uport.createVerification(claim);
case 2:
attestation = _context31.sent;
_context31.next = 5;
return uport.disclose({ own: { name: 'Davie', phone: '+15555551234' }, verified: [attestation] });
attestation = _context34.sent;
_context34.next = 5;
return uport.createDisclosureResponse({ own: { name: 'Davie', phone: '+15555551234' }, verified: [attestation] });
case 5:
jwt = _context31.sent;
_context31.next = 8;
return uport.verifyProfile(jwt);
jwt = _context34.sent;
_context34.next = 8;
return uport.verifyDisclosure(jwt);
case 8:
profile = _context31.sent;
profile = _context34.sent;

@@ -986,24 +1037,24 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context31.stop();
return _context34.stop();
}
}
}, _callee31, undefined);
}, _callee34, undefined);
})));
it('returns profile with only public claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee32() {
it('returns profile with only public claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee35() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee32$(_context32) {
return _regenerator2.default.wrap(function _callee35$(_context35) {
while (1) {
switch (_context32.prev = _context32.next) {
switch (_context35.prev = _context35.next) {
case 0:
_context32.next = 2;
return uport.disclose();
_context35.next = 2;
return uport.createDisclosureResponse();
case 2:
jwt = _context32.sent;
_context32.next = 5;
return uport.verifyProfile(jwt);
jwt = _context35.sent;
_context35.next = 5;
return uport.verifyDisclosure(jwt);
case 5:
profile = _context32.sent;
profile = _context35.sent;

@@ -1014,24 +1065,24 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context32.stop();
return _context35.stop();
}
}
}, _callee32, undefined);
}, _callee35, undefined);
})));
it('returns profile with private chain network id claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee33() {
it('returns profile with private chain network id claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee36() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee33$(_context33) {
return _regenerator2.default.wrap(function _callee36$(_context36) {
while (1) {
switch (_context33.prev = _context33.next) {
switch (_context36.prev = _context36.next) {
case 0:
_context33.next = 2;
return uport.disclose({ nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m' });
_context36.next = 2;
return uport.createDisclosureResponse({ nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m' });
case 2:
jwt = _context33.sent;
_context33.next = 5;
return uport.verifyProfile(jwt);
jwt = _context36.sent;
_context36.next = 5;
return uport.verifyDisclosure(jwt);
case 5:
profile = _context33.sent;
profile = _context36.sent;

@@ -1042,24 +1093,24 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context33.stop();
return _context36.stop();
}
}
}, _callee33, undefined);
}, _callee36, undefined);
})));
it('returns pushToken if available', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee34() {
it('returns pushToken if available', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee37() {
var jwt, profile;
return _regenerator2.default.wrap(function _callee34$(_context34) {
return _regenerator2.default.wrap(function _callee37$(_context37) {
while (1) {
switch (_context34.prev = _context34.next) {
switch (_context37.prev = _context37.next) {
case 0:
_context34.next = 2;
return uport.disclose({ capabilities: ['PUSHTOKEN'] });
_context37.next = 2;
return uport.createDisclosureResponse({ capabilities: ['PUSHTOKEN'] });
case 2:
jwt = _context34.sent;
_context34.next = 5;
return uport.verifyProfile(jwt);
jwt = _context37.sent;
_context37.next = 5;
return uport.verifyDisclosure(jwt);
case 5:
profile = _context34.sent;
profile = _context37.sent;

@@ -1070,6 +1121,6 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context34.stop();
return _context37.stop();
}
}
}, _callee34, undefined);
}, _callee37, undefined);
})));

@@ -1085,23 +1136,23 @@ });

});
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee35() {
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee38() {
var req, jwt, profile;
return _regenerator2.default.wrap(function _callee35$(_context35) {
return _regenerator2.default.wrap(function _callee38$(_context38) {
while (1) {
switch (_context35.prev = _context35.next) {
switch (_context38.prev = _context38.next) {
case 0:
_context35.next = 2;
return uport.requestDisclosure({ requested: ['name', 'phone'] });
_context38.next = 2;
return uport.createDisclosureRequest({ requested: ['name', 'phone'] });
case 2:
req = _context35.sent;
_context35.next = 5;
return uport.disclose({ own: { name: 'Davie', phone: '+15555551234' }, req: req });
req = _context38.sent;
_context38.next = 5;
return uport.createDisclosureResponse({ own: { name: 'Davie', phone: '+15555551234' }, req: req });
case 5:
jwt = _context35.sent;
_context35.next = 8;
jwt = _context38.sent;
_context38.next = 8;
return uport.receive(jwt);
case 8:
profile = _context35.sent;
profile = _context38.sent;

@@ -1112,104 +1163,128 @@ expect(profile).toMatchSnapshot();

case 'end':
return _context35.stop();
return _context38.stop();
}
}
}, _callee35, undefined);
}, _callee38, undefined);
})));
});
describe('push', function () {
var PUTUTU_URL = 'https://pututu.uport.space'; // TODO - change to .me
var API_v1_PATH = '/api/v1/sns';
var API_v2_PATH = '/api/v2/sns';
var PUSHTOKEN = 'SECRETPUSHTOKEN';
var payload = { url: 'me.uport:me', message: 'a friendly message' };
var kp = _tweetnacl2.default.box.keyPair();
var pubEncKey = _tweetnaclUtil2.default.encodeBase64(kp.publicKey);
var secEncKey = kp.secretKey;
beforeEach(function () {
_nock2.default.disableNetConnect();
describe('txRequest()', function () {
beforeAll(function () {
return mockresolver();
});
afterEach(function () {
_nock2.default.enableNetConnect();
});
var abi = [{ "constant": false, "inputs": [{ "name": "status", "type": "string" }], "name": "updateStatus", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [{ "name": "addr", "type": "address" }], "name": "getStatus", "outputs": [{ "name": "", "type": "string" }], "payable": false, "type": "function" }];
var address = '0x70A804cCE17149deB6030039798701a38667ca3B';
var statusContract = uport.contract(abi).at(address);
it('pushes url to pututu', function () {
(0, _nock2.default)(PUTUTU_URL, {
reqheaders: {
'authorization': 'Bearer ' + PUSHTOKEN
it('creates a valid JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee39() {
var jwt, verified;
return _regenerator2.default.wrap(function _callee39$(_context39) {
while (1) {
switch (_context39.prev = _context39.next) {
case 0:
_context39.next = 2;
return statusContract.updateStatus('hello');
case 2:
jwt = _context39.sent;
_context39.next = 5;
return (0, _didJwt.verifyJWT)(jwt);
case 5:
verified = _context39.sent;
expect(verified.payload).toMatchSnapshot();
case 7:
case 'end':
return _context39.stop();
}
}
}).post(API_v2_PATH, function (body) {
var encObj = JSON.parse(body.message);
var box = _tweetnaclUtil2.default.decodeBase64(encObj.ciphertext);
var nonce = _tweetnaclUtil2.default.decodeBase64(encObj.nonce);
var from = _tweetnaclUtil2.default.decodeBase64(encObj.from);
var decrypted = _tweetnacl2.default.box.open(box, nonce, from, secEncKey);
var result = JSON.parse(_tweetnaclUtil2.default.encodeUTF8(decrypted));
}, _callee39, undefined);
})));
return result.url === payload.url && result.message === payload.message;
}).reply(200, { status: 'success', message: 'd0b2bd07-d49e-5ba1-9b05-ec23ac921930' });
it('encodes readable function calls including given args in function key of jwt', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee40() {
var jwt, verified;
return _regenerator2.default.wrap(function _callee40$(_context40) {
while (1) {
switch (_context40.prev = _context40.next) {
case 0:
_context40.next = 2;
return statusContract.updateStatus('hello');
return uport.push(PUSHTOKEN, pubEncKey, payload).then(function (response) {
return expect(response).toEqual({ status: 'success', message: 'd0b2bd07-d49e-5ba1-9b05-ec23ac921930' });
});
});
case 2:
jwt = _context40.sent;
_context40.next = 5;
return (0, _didJwt.verifyJWT)(jwt);
it('handles missing token', function () {
return uport.push(null, pubEncKey, payload).catch(function (error) {
return expect(error.message).toEqual('Missing push notification token');
});
});
case 5:
verified = _context40.sent;
it('handles missing pubEncKey', function () {
(0, _nock2.default)(PUTUTU_URL, {
reqheaders: {
'authorization': 'Bearer ' + PUSHTOKEN
expect(verified.payload.fn).toEqual("updateStatus(string 'hello')");
case 7:
case 'end':
return _context40.stop();
}
}
}).post(API_v1_PATH, function (body) {
return body.message === payload.message && body.url === payload.url;
}).reply(200, { status: 'success', message: 'd0b2bd07-d49e-5ba1-9b05-ec23ac921930' });
}, _callee40, undefined);
})));
console.error = jest.fn(function (msg) {
expect(msg).toEqual('WARNING: Calling push without a public encryption key is deprecated');
});
return uport.push(PUSHTOKEN, payload).then(function (response) {
return expect(response).toEqual({ status: 'success', message: 'd0b2bd07-d49e-5ba1-9b05-ec23ac921930' });
});
});
it('adds to key as contract address to jwt', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee41() {
var jwt, verified;
return _regenerator2.default.wrap(function _callee41$(_context41) {
while (1) {
switch (_context41.prev = _context41.next) {
case 0:
_context41.next = 2;
return statusContract.updateStatus('hello');
it('handles missing payload', function () {
return uport.push(PUSHTOKEN, pubEncKey, {}).catch(function (error) {
return expect(error.message).toEqual('Missing payload url for sending to users device');
});
});
case 2:
jwt = _context41.sent;
_context41.next = 5;
return (0, _didJwt.verifyJWT)(jwt);
it('handles invalid token', function () {
(0, _nock2.default)(PUTUTU_URL, {
reqheaders: {
'authorization': 'Bearer ' + PUSHTOKEN
case 5:
verified = _context41.sent;
expect(verified.payload.to).toEqual(address);
case 7:
case 'end':
return _context41.stop();
}
}
}).post(API_v2_PATH, function () {
return true;
}).reply(403, 'Not allowed');
}, _callee41, undefined);
})));
return uport.push(PUSHTOKEN, pubEncKey, payload).catch(function (error) {
return expect(error.message).toEqual('Error sending push notification to user: Invalid Token');
});
});
it('adds additional request options passed to jwt', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee42() {
var network_id, callbackUrl, jwt, verified;
return _regenerator2.default.wrap(function _callee42$(_context42) {
while (1) {
switch (_context42.prev = _context42.next) {
case 0:
network_id = '0x4';
callbackUrl = 'mydomain';
_context42.next = 4;
return statusContract.updateStatus('hello', { network_id: network_id, callbackUrl: callbackUrl });
it('handles random error', function () {
(0, _nock2.default)(PUTUTU_URL, {
reqheaders: {
'authorization': 'Bearer ' + PUSHTOKEN
case 4:
jwt = _context42.sent;
_context42.next = 7;
return (0, _didJwt.verifyJWT)(jwt);
case 7:
verified = _context42.sent;
expect(verified.payload.net).toEqual(network_id);
expect(verified.payload.callback).toEqual(callbackUrl);
case 10:
case 'end':
return _context42.stop();
}
}
}).post(API_v2_PATH, function () {
return true;
}).reply(500, 'Server Error');
return uport.push(PUSHTOKEN, pubEncKey, payload).catch(function (error) {
return expect(error.message).toEqual('Error sending push notification to user: 500 Server Error');
});
});
}, _callee42, undefined);
})));
});

@@ -15,6 +15,2 @@ 'use strict';

var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify2 = _interopRequireDefault(_stringify);
var _regenerator = require('babel-runtime/regenerator');

@@ -44,53 +40,52 @@

var _elliptic = require('elliptic');
var _didJwt = require('did-jwt');
var _uportLite = require('uport-lite');
var _Digest = require('did-jwt/lib/Digest');
var _uportLite2 = _interopRequireDefault(_uportLite);
var _uportDidResolver = require('uport-did-resolver');
var _nets = require('nets');
var _uportDidResolver2 = _interopRequireDefault(_uportDidResolver);
var _nets2 = _interopRequireDefault(_nets);
var _muportDidResolver = require('muport-did-resolver');
var _tweetnacl = require('tweetnacl');
var _muportDidResolver2 = _interopRequireDefault(_muportDidResolver);
var _tweetnacl2 = _interopRequireDefault(_tweetnacl);
var _ethrDidResolver = require('ethr-did-resolver');
var _tweetnaclUtil = require('tweetnacl-util');
var _ethrDidResolver2 = _interopRequireDefault(_ethrDidResolver);
var _tweetnaclUtil2 = _interopRequireDefault(_tweetnaclUtil);
var _uportLite = require('uport-lite');
var _uportDidResolver = require('uport-did-resolver');
var _uportLite2 = _interopRequireDefault(_uportLite);
var _uportDidResolver2 = _interopRequireDefault(_uportDidResolver);
var _mnid = require('mnid');
var _ethrDidResolver = require('ethr-did-resolver');
var _Contract = require('./Contract.js');
var _ethrDidResolver2 = _interopRequireDefault(_ethrDidResolver);
var _Digest = require('did-jwt/lib/Digest');
var _elliptic = require('elliptic');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var MNID = require('mnid');
var secp256k1 = new _elliptic.ec('secp256k1');
/**
* The Credentials class allows you to easily create the signed payloads used in uPort inlcuding
* credentials and signed mobile app requests (ex. selective disclosure requests
* for private data). It also provides signature verification over signed payloads and
* allows you to send push notifications to users.
*/
var Credentials = function () {
var Types = {
SHARE_REQ: 'shareReq',
SHARE_RESP: 'shareResp',
VER_REQ: 'verReq',
ETH_TX: 'ethtx'
/**
* The Credentials class allows you to easily create the signed payloads used in uPort including
* credentials and signed mobile app requests (ex. selective disclosure requests
* for private data). It also provides signature verification over signed payloads.
*/
};
var Credentials = function () {
/**
* Instantiates a new uPort Credentials object
*
* The following example is just for testing purposes. You should never store a private key in source code.
*
* The following example is just for testing purposes. *You should never store a private key in source code.*
*
* @example
* import { Credentials } from 'uport'
* import { Credentials } from 'uport-credentials'
* const credentials = new Credentials({

@@ -100,7 +95,7 @@ * privateKey: '74894f8853f90e6e3d6dfdd343eb0eb70cca06e552ed8af80adadcc573b35da3'

*
* The above example derives the public key used to generate the did, so only a private key is needed.
* The above example derives the public key used to generate the did, so only a private key is needed.
* Generating a public key from a private key is slow. It is recommended to configure the `did` option as well.
*
*
* @example
* import { Credentials } from 'uport'
* import { Credentials } from 'uport-credentials'
* const credentials = new Credentials({

@@ -112,5 +107,5 @@ * did: 'did:ethr:0xbc3ae59bc76f894822622cdef7a2018dbe353840',

* It is recommended to store the address and private key in environment variables for your server application
*
*
* @example
* import { Credentials, SimpleSigner } from 'uport'
* import { Credentials, SimpleSigner } from 'uport-credentials'
* const credentials = new Credentials({

@@ -120,9 +115,9 @@ * did: process.env.APPLICATION_DID,

* })
*
* Instead of a private key you can pass in a [Signer Functions](https://github.com/uport-project/did-jwt#signer-functions) to
*
* Instead of a private key you can pass in a [Signer Functions](https://github.com/uport-project/did-jwt#signer-functions) to
* present UX or call a HSM.
*
* @example
* import { Credentials } from 'uport'
*
* import { Credentials } from 'uport-credentials'
*
* function mySigner (data) {

@@ -134,3 +129,3 @@ * return new Promise((resolve, reject) => {

* }
*
*
* const credentials = new Credentials({

@@ -146,2 +141,3 @@ * did: process.env.APPLICATION_DID,

* @param {Object} settings.ethrConfig Configuration object for ethr did resolver. See [ethr-did-resolver](https://github.com/uport-project/ethr-did-resolver)
* @param {Object} settings.muportConfig Configuration object for muport did resolver. See [muport-did-resolver](https://github.com/uport-project/muport-did-resolver)
* @param {Address} settings.address DEPRECATED your uPort address (may be the address of your application's uPort identity)

@@ -162,3 +158,4 @@ * @param {Object} settings.networks DEPRECATED networks config object, ie. { '0x94365e3b': { rpcUrl: 'https://private.chain/rpc', address: '0x0101.... }}

registry = _ref.registry,
ethrConfig = _ref.ethrConfig;
ethrConfig = _ref.ethrConfig,
muportConfig = _ref.muportConfig;

@@ -172,6 +169,7 @@ (0, _classCallCheck3.default)(this, Credentials);

}
if (did) {
this.did = did;
} else if (address) {
if (MNID.isMNID(address)) {
if ((0, _mnid.isMNID)(address)) {
this.did = 'did:uport:' + address;

@@ -194,6 +192,15 @@ }

(0, _ethrDidResolver2.default)(ethrConfig || {});
(0, _muportDidResolver2.default)(muportConfig || {});
}
/**
* generate a DID and private key
* Generate a DID and private key, effectively creating a new identity that can sign and verify data
*
* @example
* const {did, privateKey} = Credentials.createIdentity()
* const credentials = new Credentials({did, privateKey, ...})
*
* @returns {Object} keypair
* - {String} keypair.did An ethr-did string for the new identity
* - {String} keypair.privateKey The identity's private key, as a string
*/

@@ -203,3 +210,3 @@

(0, _createClass3.default)(Credentials, [{
key: 'requestDisclosure',
key: 'createDisclosureRequest',

@@ -228,3 +235,3 @@

*/
value: function requestDisclosure() {
value: function createDisclosureRequest() {
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

@@ -259,58 +266,125 @@ var expiresIn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 600;

}
return this.signJWT((0, _extends3.default)({}, payload, { type: 'shareReq' }), params.exp ? undefined : expiresIn);
return this.signJWT((0, _extends3.default)({}, payload, { type: Types.SHARE_REQ }), params.exp ? undefined : expiresIn);
}
/**
* Creates a [Selective Disclosure Request JWT](https://github.com/uport-project/specs/blob/develop/messages/sharereq.md)
* Create a credential (a signed JSON Web Token)
*
* @example
* const req = { requested: ['name', 'country'],
* callbackUrl: 'https://myserver.com',
* notifications: true }
* credentials.createRequest(req).then(jwt => {
* ...
* credentials.attest({
* sub: '5A8bRWU3F7j3REx3vkJ...', // uPort address of user, likely a MNID
* exp: <future timestamp>,
* claim: { name: 'John Smith' }
* }).then( credential => {
* ...
* })
*
* @param {Object} [params={}] request params object
* @param {Array} params.requested an array of attributes for which you are requesting credentials to be shared for
* @param {Array} params.verified an array of attributes for which you are requesting verified credentials to be shared for
* @param {Boolean} params.notifications boolean if you want to request the ability to send push notifications
* @param {String} params.callbackUrl the url which you want to receive the response of this request
* @param {String} params.network_id network id of Ethereum chain of identity eg. 0x4 for rinkeby
* @param {String} params.accountType Ethereum account type: "general", "segregated", "keypair", "devicekey" or "none"
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
* @deprecated
* @param {Object} [credential] a unsigned credential object
* @param {String} credential.sub subject of credential (a uPort address)
* @param {String} credential.claim claim about subject single key value or key mapping to object with multiple values (ie { address: {street: ..., zip: ..., country: ...}})
* @param {String} credential.exp time at which this claim expires and is no longer valid (seconds since epoch)
* @return {Promise<Object, Error>} a promise which resolves with a credential (JWT) or rejects with an error
*/
}, {
key: 'createRequest',
value: function createRequest() {
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
key: 'createVerification',
value: function createVerification(_ref2) {
var sub = _ref2.sub,
claim = _ref2.claim,
exp = _ref2.exp;
return this.requestDisclosure(params);
return this.signJWT({ sub: sub, claim: claim, exp: exp });
}
/**
* Creates a signed request for the user to attest a list of claims.
*
* @example
* const unsignedClaim = {
* claim: {
* "Citizen of city X": {
* "Allowed to vote": true,
* "Document": "QmZZBBKPS2NWc6PMZbUk9zUHCo1SHKzQPPX4ndfwaYzmPW"
* }
* },
* sub: "2oTvBxSGseWFqhstsEHgmCBi762FbcigK5u"
* }
* credentials.createVerificationRequest(unsignedClaim).then(jwt => {
* ...
* })
*
* @param {Object} unsignedClaim an object that is an unsigned claim which you want the user to attest
* @param {String} aud the DID of the identity you want to sign the attestation
* @param {String} sub the DID which the unsigned claim is about
* @param {String} callbackUrl the url which you want to receive the response of this request
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
*/
}, {
key: 'createSignVerificationRequest',
value: function createSignVerificationRequest(unsignedClaim, sub, callbackUrl, aud) {
return this.signJWT({ unsignedClaim: unsignedClaim, sub: sub, aud: aud, callback: callbackUrl, type: Types.VER_REQ });
}
/**
* Given a transaction object, similarly defined as the web3 transaction object,
* it creates a JWT transaction request and appends addtional request options.
*
* @example
* const txobject = {
* to: '0xc3245e75d3ecd1e81a9bfb6558b6dafe71e9f347',
* value: '0.1',
* fn: "setStatus(string 'hello', bytes32 '0xc3245e75d3ecd1e81a9bfb6558b6dafe71e9f347')",
* }
* connect.createTxRequest(txObject, {callbackUrl: 'http://mycb.domain'}).then(jwt => {
* ...
* })
*
* @param {Object} txObj
* @param {String} [id='addressReq'] string to identify request, later used to get response
* @return {String} a transaction request jwt
*/
}, {
key: 'createTxRequest',
value: function createTxRequest(txObj) {
var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
callbackUrl = _ref3.callbackUrl,
_ref3$exp = _ref3.exp,
exp = _ref3$exp === undefined ? 600 : _ref3$exp,
network_id = _ref3.network_id,
label = _ref3.label;
var payload = {};
if (callbackUrl) payload.callback = callbackUrl;
if (network_id) payload.net = network_id;
if (label) payload.label = label;
return this.signJWT((0, _extends3.default)({}, payload, txObj, { type: Types.ETH_TX }), exp);
}
/**
* Creates a [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md).
*
* This can either be used to share information about the signing identity or as the response to a
* [Selective Disclosure Flow](https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md), where it can be used to authenticate the identity.
*
* This can either be used to share information about the signing identity or as the response to a
* [Selective Disclosure Flow](https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md),
* where it can be used to verifyDisclosureResponse the identity.
*
* @example
* credentials.disclose({own: {name: 'Lourdes Valentina Gomez'}}).then(jwt => {
* credentials.createDisclosureResponse({own: {name: 'Lourdes Valentina Gomez'}}).then(jwt => {
* ...
* })
*
* @param {Object} [params={}] request params object
* @param {JWT} params.req A selective disclosure Request JWT if this is returned as part of an authentication flow
* @param {Object} params.own An object of self attested claims about the signer (eg. name etc)
* @param {Array} params.verified An array of attestation JWT's to include
* @param {MNID} params.nad An ethereum address encoded as an [MNID](https://github.com/uport-project/mnid)
* @param {Array} params.capabilities An array of capability JWT's to include
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
* @param {Object} [payload={}] request params object
* @param {JWT} payload.req A selective disclosure Request JWT if this is returned as part of an authentication flow
* @param {Object} payload.own An object of self attested claims about the signer (eg. name etc)
* @param {Array} payload.verified An array of attestation JWT's to include
* @param {MNID} payload.nad An ethereum address encoded as an [MNID](https://github.com/uport-project/mnid)
* @param {Array} payload.capabilities An array of capability JWT's to include
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
*/
}, {
key: 'disclose',
key: 'createDisclosureResponse',
value: function () {
var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() {
var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() {
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

@@ -339,3 +413,3 @@ var expiresIn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 600;

case 5:
return _context.abrupt('return', this.signJWT((0, _extends3.default)({}, payload, { type: 'shareResp' }), expiresIn));
return _context.abrupt('return', this.signJWT((0, _extends3.default)({}, payload, { type: Types.SHARE_RESP }), expiresIn));

@@ -350,16 +424,24 @@ case 6:

function disclose() {
return _ref2.apply(this, arguments);
function createDisclosureResponse() {
return _ref4.apply(this, arguments);
}
return disclose;
return createDisclosureResponse;
}()
/**
* Parse a selective disclosure response, and verify signatures on each included signed claim ("verification")
*
* @param {Object} response A selective disclosure response payload, with associated did doc
* @param {Object} response.doc
*/
}, {
key: 'processDisclosurePayload',
value: function () {
var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(_ref3) {
var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(_ref5) {
var _this2 = this;
var doc = _ref3.doc,
payload = _ref3.payload;
var doc = _ref5.doc,
payload = _ref5.payload;
var credentials, verified;

@@ -407,3 +489,3 @@ return _regenerator2.default.wrap(function _callee2$(_context2) {

function processDisclosurePayload(_x7) {
return _ref4.apply(this, arguments);
return _ref6.apply(this, arguments);
}

@@ -415,27 +497,27 @@

/**
* Authenticates [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md) from mobile
* app as part of the [Selective Disclosure Flow](https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md).
*
* It Verifies and parses the given response token and verifies the challenge response flow.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.authenticate(resToken).then(res => {
* const credentials = res.verified
* const name = res.name
* ...
* })
*
* @param {String} token a response token
* @param {String} [callbackUrl=null] callbackUrl
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
*/
* Authenticates [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md) from mobile
* app as part of the [Selective Disclosure Flow](https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md).
*
* It Verifies and parses the given response token and verifies the challenge response flow.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.verifyDisclosureResponse(resToken).then(res => {
* const credentials = res.verified
* const name = res.name
* ...
* })
*
* @param {String} token a response token
* @param {String} [callbackUrl=null] callbackUrl
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
*/
}, {
key: 'authenticate',
key: 'verifyDisclosureResponse',
value: function () {
var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(token) {
var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(token) {
var callbackUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var _ref6, payload, doc, challenge;
var _ref8, payload, doc, challenge;

@@ -450,8 +532,8 @@ return _regenerator2.default.wrap(function _callee3$(_context3) {

case 2:
_ref6 = _context3.sent;
payload = _ref6.payload;
doc = _ref6.doc;
_ref8 = _context3.sent;
payload = _ref8.payload;
doc = _ref8.doc;
if (!payload.req) {
_context3.next = 13;
_context3.next = 20;
break;

@@ -466,17 +548,28 @@ }

if (!(challenge.payload.iss === this.did && challenge.payload.type === 'shareReq')) {
_context3.next = 11;
if (!(challenge.payload.iss !== this.did)) {
_context3.next = 13;
break;
}
throw new Error('Challenge issuer does not match current identity: ' + challenge.payload.iss + ' !== ' + this.did);
case 13:
if (!(challenge.payload.type !== Types.SHARE_REQ)) {
_context3.next = 17;
break;
}
throw new Error('Challenge payload type invalid: ' + challenge.payload.type);
case 17:
return _context3.abrupt('return', this.processDisclosurePayload({ payload: payload, doc: doc }));
case 11:
_context3.next = 14;
case 18:
_context3.next = 21;
break;
case 13:
case 20:
throw new Error('Challenge was not included in response');
case 14:
case 21:
case 'end':

@@ -489,25 +582,25 @@ return _context3.stop();

function authenticate(_x9) {
return _ref5.apply(this, arguments);
function verifyDisclosureResponse(_x9) {
return _ref7.apply(this, arguments);
}
return authenticate;
return verifyDisclosureResponse;
}()
/**
* Receive signed response token from mobile app. Verifies and parses the given response token.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.receive(resToken).then(res => {
* const credentials = res.verified
const name = res.name
* ...
* })
*
* @param {String} token a response token
* @param {String} [callbackUrl=null] callbackUrl
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
* @deprecated
*/
* Receive signed response token from mobile app. Verifies and parses the given response token.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.receive(resToken).then(res => {
* const credentials = res.verified
* const name = res.name
* ...
* })
*
* @param {String} token a response token
* @param {String} [callbackUrl=null] callbackUrl
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
* @deprecated
*/

@@ -519,28 +612,28 @@ }, {

return this.authenticate(token, callbackUrl);
return this.verifyDisclosureResponse(token, callbackUrl);
}
/**
* Verify and return profile from a [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md).
*
* The main difference between this and `authenticate()` is that it does not verify the challenge. This can be used to verify user profiles that have been shared
* through other methods such as QR codes and messages.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.verifyProfile(resToken).then(profile => {
* const credentials = profile.verified
const name = profile.name
* ...
* })
*
* @param {String} token a response token
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
*/
* Verify and return profile from a [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md).
*
* The main difference between this and `verifyDisclosureResponse()` is that it does not verify the challenge.
* This can be used to verify user profiles that have been shared through other methods such as QR codes and messages.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.verifyDisclosure(resToken).then(profile => {
* const credentials = profile.verified
* const name = profile.name
* ...
* })
*
* @param {String} token a response token
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
*/
}, {
key: 'verifyProfile',
key: 'verifyDisclosure',
value: function () {
var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(token) {
var _ref8, payload, doc;
var _ref9 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(token) {
var _ref10, payload, doc;

@@ -555,5 +648,5 @@ return _regenerator2.default.wrap(function _callee4$(_context4) {

case 2:
_ref8 = _context4.sent;
payload = _ref8.payload;
doc = _ref8.doc;
_ref10 = _context4.sent;
payload = _ref10.payload;
doc = _ref10.doc;
return _context4.abrupt('return', this.processDisclosurePayload({ payload: payload, doc: doc }));

@@ -569,113 +662,30 @@

function verifyProfile(_x11) {
return _ref7.apply(this, arguments);
function verifyDisclosure(_x11) {
return _ref9.apply(this, arguments);
}
return verifyProfile;
return verifyDisclosure;
}()
/**
* Send a push notification to a user, consumes a token which allows you to send push notifications
* and a url/uri request you want to send to the user.
*
* @param {String} token a push notification token (get a pn token by requesting push permissions in a request)
* @param {Object} payload push notification payload
* @param {String} payload.url a uport request url
* @param {String} payload.message a message to display to the user
* @param {String} pubEncKey the public encryption key of the receiver, encoded as a base64 string
* @return {Promise<Object, Error>} a promise which resolves with successful status or rejects with an error
*/
* Builds and returns a contract object which can be used to interact with
* a given contract. Similar to web3.eth.contract but with promises. Once specifying .at(address)
* you can call the contract functions with this object. Each call will create a request.
*
* @param {Object} abi contract ABI
* @return {Object} contract object
*/
}, {
key: 'push',
value: function push(token, pubEncKey, payload) {
var PUTUTU_URL = 'https://pututu.uport.space'; // TODO - change to .me
return new _promise2.default(function (resolve, reject) {
var endpoint = '/api/v2/sns';
if (!token) {
return reject(new Error('Missing push notification token'));
}
//if (!pubEncKey) {
//return reject(new Error('Missing public encryption key of the receiver'))
//}
if (pubEncKey.url) {
console.error('WARNING: Calling push without a public encryption key is deprecated');
endpoint = '/api/v1/sns';
payload = pubEncKey;
} else {
if (!payload.url) {
return reject(new Error('Missing payload url for sending to users device'));
}
var plaintext = padMessage((0, _stringify2.default)(payload));
var enc = encryptMessage(plaintext, pubEncKey);
payload = { message: (0, _stringify2.default)(enc) };
}
key: 'contract',
value: function contract(abi) {
var _this3 = this;
(0, _nets2.default)({
uri: PUTUTU_URL + endpoint,
json: payload,
method: 'POST',
withCredentials: false,
headers: {
Authorization: 'Bearer ' + token
}
}, function (error, res, body) {
if (error) return reject(error);
if (res.statusCode === 200) {
resolve(body);
}
if (res.statusCode === 403) {
return reject(new Error('Error sending push notification to user: Invalid Token'));
}
reject(new Error('Error sending push notification to user: ' + res.statusCode + ' ' + body.toString()));
});
});
var txObjHandler = function txObjHandler(txObj, opts) {
if (txObj.function) txObj.fn = txObj.function;
delete txObj['function'];
return _this3.createTxRequest(txObj, opts);
};
return (0, _Contract.ContractFactory)(txObjHandler.bind(this))(abi);
}
/**
* Create a credential (a signed JSON Web Token)
*
* @example
* credentials.attest({
* sub: '5A8bRWU3F7j3REx3vkJ...', // uPort address of user, likely a MNID
* exp: <future timestamp>,
* claim: { name: 'John Smith' }
* }).then( credential => {
* ...
* })
*
* @param {Object} [credential] a unsigned credential object
* @param {String} credential.sub subject of credential (a uPort address)
* @param {String} credential.claim claim about subject single key value or key mapping to object with multiple values (ie { address: {street: ..., zip: ..., country: ...}})
* @param {String} credential.exp time at which this claim expires and is no longer valid (seconds since epoch)
* @return {Promise<Object, Error>} a promise which resolves with a credential (JWT) or rejects with an error
*/
}, {
key: 'attest',
value: function attest(_ref9) {
var sub = _ref9.sub,
claim = _ref9.claim,
exp = _ref9.exp;
return this.signJWT({ sub: sub, claim: claim, exp: exp });
}
// /**
// * Look up a profile in the registry for a given uPort address. Address must be MNID encoded.
// *
// * @example
// * credentials.lookup('5A8bRWU3F7j3REx3vkJ...').then(profile => {
// * const name = profile.name
// * const pubkey = profile.pubkey
// * ...
// * })
// *
// * @param {String} address a MNID encoded address
// * @return {Promise<Object, Error>} a promise which resolves with parsed profile or rejects with an error
// */
// lookup (address) {
// return this.settings.registry(address)
// }
}], [{

@@ -709,38 +719,2 @@ key: 'createIdentity',

/**
* Adds padding to a string
*
* @param {String} the message to be padded
* @return {String} the padded message
* @private
*/
var padMessage = function padMessage(message) {
var INTERVAL_LENGTH = 50;
var padLength = INTERVAL_LENGTH - message.length % INTERVAL_LENGTH;
return message + ' '.repeat(padLength);
};
/**
* Encrypts a message
*
* @param {String} the message to be encrypted
* @param {String} the public encryption key of the receiver, encoded as base64
* @return {String} the encrypted message, encoded as base64
* @private
*/
var encryptMessage = function encryptMessage(message, receiverKey) {
var tmpKp = _tweetnacl2.default.box.keyPair();
var decodedKey = _tweetnaclUtil2.default.decodeBase64(receiverKey);
var decodedMsg = _tweetnaclUtil2.default.decodeUTF8(message);
var nonce = _tweetnacl2.default.randomBytes(24);
var ciphertext = _tweetnacl2.default.box(decodedMsg, nonce, decodedKey, tmpKp.secretKey);
return {
from: _tweetnaclUtil2.default.encodeBase64(tmpKp.publicKey),
nonce: _tweetnaclUtil2.default.encodeBase64(nonce),
ciphertext: _tweetnaclUtil2.default.encodeBase64(ciphertext)
};
};
exports.default = Credentials;
{
"name": "uport",
"version": "0.7.0-alpha-1",
"version": "0.7.0-alpha-10",
"description": "Library for interacting with uport profiles and attestations",

@@ -19,3 +19,3 @@ "main": "lib/index.js",

"build:docs:html": "./node_modules/.bin/jsdoc src/* README.md -d docs",
"build:docs:md": "./node_modules/jsdoc-to-markdown/bin/cli.js src/* > DOCS.md",
"build:docs:md": "./node_modules/jsdoc-to-markdown/bin/cli.js -f src/* --template \"./template.hbs\" -c js2doc.conf > docs/reference/index.md; echo",
"build:docs": "npm run build:docs:html && npm run build:docs:md",

@@ -25,3 +25,3 @@ "prepublish": "yarn build"

"author": "Pelle Braendgaard <pelle.braendgaard@consensys.net>",
"license": "MIT",
"license": "Apache-2.0",
"dependencies": {

@@ -31,7 +31,6 @@ "did-jwt": "^0.0.7",

"ethjs-util": "^0.1.3",
"ethr-did-resolver": "^0.0.7",
"ethr-did-resolver": "^0.1.1",
"jsontokens": "^0.7.8",
"mnid": "^0.1.1",
"nets": "^3.2.0",
"tweetnacl": "^1.0.0",
"tweetnacl-util": "^0.15.0",
"muport-did-resolver": "^0.1.0",
"uport-did-resolver": "^0.0.2",

@@ -49,3 +48,3 @@ "uport-lite": "^1.0.2"

"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-core": "^6.26.3",
"babel-jest": "^22.4.3",

@@ -59,13 +58,12 @@ "babel-loader": "^7.1.4",

"babel-preset-env": "^1.6.1",
"body-parser": "^1.17.1",
"express": "^4.15.2",
"body-parser": "^1.18.3",
"express": "^4.16.3",
"jest": "^22.4.3",
"jsdoc": "^3.4.3",
"jsdoc-to-markdown": "^1.3.7",
"jsdoc-to-markdown": "^4.0.1",
"json-loader": "^0.5.4",
"mockdate": "^2.0.1",
"nock": "^9.0.6",
"webpack": "^4.6.0",
"nock": "^9.4.3",
"webpack": "^4.16.2",
"webpack-cli": "^2.0.15"
}
}

@@ -1,17 +0,9 @@

# uport-js
# uport-credentials
## Integrate uPort in your javascript application
uPort provides a simple way for your users to login to your website and provide private credentials such as identity information and contact details to you.
uPort provides a set of tools for creating and managing identities that conform to the [decentralized identifier (DID) specification](), and requesting and exchanging verified data between them. This library simplifies the process of identity creation within javascript applications, and allows apps to easily sign pieces of data and verify data signed by other identities to facilitate secure communication between parties. These pieces of data take the form of *signed [JSON web tokens](https://jwt.io/introduction/) (JWTs)* with specific fields designed for use with uPort clients, and described in the uPort [specifications](https://github.com/uport-project/specs), collectively referred to as *verifications*.
You can also “attest” credentials they provide to you or that you yourself have about them. This can be shared back to your customers so you can help them build their digital identity.
To allow maximum flexibility, this library only deals with creation and validation of verifications. To pass verifications between a javascript application and a user via the uPort mobile app, we have also developed the [uport-transports](https://github.com/uport-project/uport-transports) library, which may be used in conjunction with this one. For a simpler, out-of-the-box solution for interacting with a uPort mobile wallet from a web application, we have an easy to use browser library [uport-connect](https://github.com/uport-project/uport-connect).
Uport.js provides a simple way for you to integrate uport.js into your javascript application. You can also interact with your uPort users directly in the browser.
We have an easy to use browser library [uport-connect](https://github.com/uport-project/uport-connect) which can help you do so.
## Setup your uPort application identity
First make sure you have your uPort app installed and you've setup your own uPort identity.
### What is a uPort identity?

@@ -21,9 +13,7 @@

An identity has an identifier in the form of an [MNID](https://github.com/uport-project/mnid), a signing key, and a public key stored on the [uPort Registry](https://github.com/uport-project/uport-registry).
An identity can:
- Sign JWTs (JSON Web Tokens)
- [Authenticate themselves to a third party](messages/shareresp.md)
- [Disclose private information about themselves](messages/shareresp.md)
- [Authenticate themselves to a third party](messages/shareresp.md)
- [Disclose private information about themselves](messages/shareresp.md)
- [Receive requests for disclosure about themselves](messages/sharereq.md)

@@ -33,14 +23,14 @@ - [Receive and store signed third party verifications about themselves](flows/verification.md)

When interacting privately with a user you will be interchanging signed JWT([JSON Web Token](https://jwt.io/)). To verify the signature of the JWT you and your users will be fetching your public key from the public profile.
When interacting privately with a user you will be interchanging signed JWT([JSON Web Token](https://jwt.io/)). To verify the signature of the JWT you and your users will be fetching your public key from the public profile.
For details on uPort's underlying architecture, read our [spec repo](https://github.com/uport-project/specs) or check out the [uPort identity contracts] (https://github.com/uport-project/uport-identity).
For details on uPort's underlying architecture, read our [spec repo](https://github.com/uport-project/specs) or check out the [uPort identity contracts](https://github.com/uport-project/uport-identity).
## Configure your application
In your application you must first configure your uPort object.
In your application you must first configure your uPort object with an identifier and a private key (or signer function). There are several ways to instantiate a credentials object. The most common approach is to save a DID and private key on a server for your application, and create a credentials instance from your application's unique private key. Signed JWTs for requests and verifications can then be passed to a client-side application, and presented to a user as a QR code, or sent via a [transport](http://github.com/uport-project/uport-transports).
```javascript
import { Credentials } from 'uport'
import { Credentials } from 'uport-credentials'
// For new ethereum based addresses
// For ethereum based addresses (ethr-did)
const credentials = new Credentials({

@@ -51,15 +41,21 @@ appName: 'App Name',

})
```
// You can create a new identity
There may also be cases where you want identities to be created dynamically, either in the browser or on a server. This can be accomplished with the static `Credentials.createIdentity()` method, which generates an ethereum keypair, and returns an object containing the associated did and private key.
```
// Create a credentials object for a brand new identity
const {did, privateKey} = Credentials.createIdentity()
const credentials = new Credentials({
appName: 'App Name', did, privateKey
})
```
console.log(Credentials.createIdentity())
Finally, we continue to support older uport identities described by an [MNID](http://github.com/uport-project/mnid)-encoded ethereum address. These identifiers can be expressed as a did via the 'uport' did method: `did:uport:<mnid>`
```
// For legacy application identity created on App Manager
const credentials = new Credentials({
appName: 'App Name',
address: 'MNID Encoded uPort Address For Your App',
address: '2nQtiQG...', // MNID Encoded uPort Address For Your App
privateKey: process.env.PRIVATE_KEY
})
```

@@ -69,8 +65,6 @@

To request information from your user you create a Selective Disclosure Request JWT and present it to your user in the web browser.
To request information from your user you create a Selective Disclosure Request JWT. When this is presented to a user via a QR code or other [transport](https://github.com/uport-project/uport-transports), they will be prompted to approve sharing the request attributes. All requests will return a user's `did`.
The most basic request to get a users public uport identity details:
```javascript
credentials.requestDisclosure().then(requestToken => {
credentials.createDisclosureRequest().then(requestToken => {
// send requestToken to browser

@@ -80,17 +74,18 @@ })

You can ask for specific private data like this:
A selective disclosure request JWT can ask for specific private data, and/or provide a URL to which a mobile app should send a response.
```javascript
credentials.requestDisclosure({
requested: ['name','phone','identity_no'],
callbackUrl: 'https://....' // URL to send the response of the request to
}.then(requestToken => {
credentials.createDisclosureRequest().then({
requested: ['name', 'phone', 'identity_no'],
callbackUrl: 'https://....' // URL to send the response of the request to
}).then(requestToken => {
// send requestToken to browser
})
})
```
If you need to know the users address on a specific ethereum network, specify it's `network_id` (currently defaults to ropsten `0x3`). In this case be aware that the `address` returned will be the address on the public network (currently ropsten) for the users profile. The requested network address will be in the `networkAddress` field and will be MNID encoded.
If you need to know the users address on a specific ethereum network, specify it's `networkId` (currently defaults to mainnet `0x1`). In this case be aware that the `address` returned will be the address on the public network (currently mainnet) for the users profile. The requested network address will be in the `networkAddress` field and will be MNID encoded.
```javascript
credentials.requestDisclosure({network_id: '0x4'}).then(requestToken => {
// Request an address on Rinkeby
credentials.requestDisclosure({networkId: '0x4'}).then(requestToken => {
// send requestToken to browser

@@ -100,7 +95,7 @@ })

Back in your server code you receive the token:
When a response JWT is received, it can be parsed and verified via the `verifyDisclosureResponse()` method, which checks the validity of the signature on the JWT, as well as validity of the original dislcosure request, which is expected as part of the response.
```javascript
credentials.authenticate(responseToken).then(profile => {
// Store user profile
credentials.verifyDisclosureResponse(responseToken).then(verifiedData => {
// Do stuff with verified data
})

@@ -113,9 +108,7 @@ ```

The default verification rule is that the issuer of the embedded request must match the clientId in your Credentials object and that the original request has not yet expired.
The verification rule for the Selective Disclosure Response is that the issuer of the embedded request must match the did in your Credentials object and that the original request has not yet expired. This is to be sure that when requesting data from a user, only a response to your initial request will be accepted as valid. If you would like to consume an arbitrary signed JWT that is not part of a particular selective disclosure flow, you can use the `verifyDisclosure()` method to skip the challenge/response check.
Some applications that exclusively live in the browser are unable to sign the original request. In those cases the request token verification is ignored.
### Requesting Push notification tokens from your users
As part of the selective disclosure request you can ask for permission from your users to communicate directly with their app.
As part of the selective disclosure request you can also ask for permission from your users to communicate directly with their app. With a push token, you can configure a [transport](https://github.com/uport-project/uport-transports) to send JWTs via push.

@@ -130,10 +123,8 @@ ```javascript

```
If the user approves the use of push notifications, the selective disclosure response will contain a `pushToken` field, which can be saved when the response is received and verified.
Present it to the user like before. On the server you can receive the push token like this:
```javascript
credentials.receive(responseToken).then(profile => {
// Store user profile
credentials.verifyDisclosureResponse(responseToken).then(verifiedData => {
// Store push token securely
console.log(profile.pushToken)
doSomethingWith(verifiedData.pushToken)
})

@@ -143,18 +134,13 @@ ```

## Attesting information about your users
In addition to requesting and verifying information, you can also sign new data on behalf of your application and share it with your users in the form of _attestations_, also known as _verifications_. By presenting an attestation to a user, you are making a claim about them, and are _attesting_ to its truth with your application's signature. Exactly what information your app should attest to depends the context -- If you're a financial institution you may be able to attest to KYC related information such as national identity numbers. If you're an educational application you may want to attest to your users achievements in a way that they can securely share. Anyone with access to your application's `did` can verify that a particular attestation came from your app.
Attesting information about your users helps add real value to your application. Your users will use uport to build up their own digital identity and your business is an important part of this.
Attesting to information about your users helps add real value to your application, and your users will use uPort to build up their own digital identity.
If you're a financial institution you may be able to attest to KYC related information such as national identity numbers. If you're an educational application you may want to attest to your users achievements in a way that they can securely share.
### What are attestations
Attestations are shareable private information that one party can sign about another party. They are designed to be shared privately by you to your users and by them to other users.
### Creating an attestation
```javascript
credentials.attest({
credentials.createVerification({
sub: '0x...', // uport address of user
exp: <future timestamp>, // If your information is not permanent make sure to add an expires timestamp
claims: {name:'John Smith'}
claims: {name: 'John Smith'}
}).then(attestation => {

@@ -164,42 +150,19 @@ // send attestation to user

```
As with a verification request, you will want to send this JWT to your user. You can do this in the browser via QR, using push with a previously requested pushToken, or via another [transport](https://github.com/uport-project/uport-transports) of your choosing.
As before you will want to send this to your user. You can do this in the browser
```javascript
const connect = new uportconnect.Connect('app name')
connect.showRequest(attestation) // no response is needed for an attestation
```
If you requested a push notification token in the above selective disclosure step you can also send attestations directly to your users app in real time.
```javascript
credentials.push(pushToken, `me.uport:add?attestation=${attestationjwt}`, message).then(response => {
})
```
## Asking users to sign Ethereum transactions
Ethereum smart contracts live on the blockchain and at a certain address. The application interface is known as the abi and can be created by the Solidity compiler.
Finally, as uPort is based in the Ethereum blockchain, uPort Credentials can be used to request that a user call a particular Ethereum smart contract function. Smart contracts live on the blockchain at a certain address, and expose public functions that can be called according to their ABI. Using the `Credentials.contract` method, you can create an object from a contract abi that will create transaction request JWTs for each contract method, that can be presented to a user's mobile application like any other JWT described above. This is just a wrapper around `Credentials.createTxRequest()`, which generates the txObject for a particular contract method call.
Our Contract class will let you create a javascript object modelling the SmartContract allowing you to create uport uri's that you can send to the user.
```javascript
import { Contract } from 'uport'
const abi = // import from json or have directly in code
const contract = Contract(abi).at(contractAddress)
import abi from './myContractAbi.json'
const myContract = Credentials.contract(abi).at(contractAddress)
// creates a request for the user to call the transfer() function on the smart contract
const txRequest = tokenContract.transfer(....)
```
In your front end use 'uport-connect' to present it to your user either as a QR code or as a uport-button depending on whether they are on a desktop or mobile browser.
```javascript
const connect = new uportconnect.Connect('app name')
connect.sendTransaction(txRequest).then(txResponse => {
// send response back to server
const txRequest = myContract.transfer(...).then(txRequestToken => {
// send tx request token to user
})
```
---------------------------------------------------
Back in your server code you receive the `txResponse`. This is a standard ethereum transaction object that you can verify.
This library is part of a suite of tools maintained by the uPort Project, a Consensys formation. For more information on the project, visit [uport.me](https://uport.me)
import Credentials from '../Credentials'
import { SimpleSigner, createJWT, verifyJWT, decodeJWT } from 'did-jwt'
import nacl from 'tweetnacl'
import naclutil from 'tweetnacl-util'
import nock from 'nock'
import MockDate from 'mockdate'

@@ -64,3 +61,3 @@ import { registerMethod } from 'did-resolver'

})
describe('sets signer', () => {

@@ -84,3 +81,3 @@ describe('always uses signer if passed in', () => {

})
it('should require a registry address', () => {

@@ -90,3 +87,3 @@ const networks = {'0x94365e3b': { rpcUrl: 'https://private.chain/rpc' }}

})
it('should require a rpcUrl', () => {

@@ -96,3 +93,3 @@ const networks = {'0x94365e3b': { registry: '0x3b2631d8e15b145fd2bf99fc5f98346aecdc394c' }}

})
it('if networks key is passed in it must contain configuration object', () => {

@@ -119,3 +116,3 @@ const networks = {'0x94365e3b': 'hey'}

const { header } = decodeJWT(jwt)
expect(header.alg).toEqual('ES256K')
expect(header.alg).toEqual('ES256K')
})

@@ -129,3 +126,3 @@ })

const { header } = decodeJWT(jwt)
expect(header.alg).toEqual('ES256K-R')
expect(header.alg).toEqual('ES256K-R')
})

@@ -136,6 +133,6 @@ })

describe('requestDisclosure()', () => {
describe('createDisclosureRequest()', () => {
beforeAll(() => mockresolver())
async function createAndVerify (params={}) {
const jwt = await uport.requestDisclosure(params)
const jwt = await uport.createDisclosureRequest(params)
return await verifyJWT(jwt)

@@ -167,3 +164,3 @@ }

return expect(response).toMatchSnapshot()
})
})
}

@@ -173,3 +170,3 @@

expect(createAndVerify({accountType: 'gold'})).rejects.toMatchSnapshot()
})
})

@@ -202,6 +199,6 @@ it('ignores unsupported request parameters', async () => {

describe('LEGACY createRequest()', () => {
describe('LEGACY createDisclosureRequest()', () => {
beforeAll(() => mockresolver())
async function createAndVerify (params={}) {
const jwt = await uport.createRequest(params)
const jwt = await uport.createDisclosureRequest(params)
return await verifyJWT(jwt)

@@ -218,3 +215,3 @@ }

async function createAndVerify (params={}) {
const jwt = await uport.disclose(params)
const jwt = await uport.createDisclosureResponse(params)
return await verifyJWT(jwt, {audience: did})

@@ -229,3 +226,3 @@ }

it('creates a valid JWT for a disclosure', async () => {
const req = await uport.requestDisclosure()
const req = await uport.createDisclosureRequest()
const response = await createAndVerify({req})

@@ -236,7 +233,15 @@ return expect(response).toMatchSnapshot()

describe('attest()', () => {
describe('createSignVerificationRequest()', () => {
it('creates a valid JWT for a request', async () => {
const jwt = await uport.createSignVerificationRequest({claim: { test: {prop1: 1, prop2: 2}}}, 'did:uport:223ab45')
return expect(await verifyJWT(jwt, {audience: did})).toMatchSnapshot()
})
})
describe('createVerification()', () => {
beforeAll(() => mockresolver())
it('has correct payload in JWT for an attestation', () => {
return uport.attest({sub: '0x112233', claim: {email: 'bingbangbung@email.com'}, exp: 1485321133 + 1}).then((jwt) => {
return expect(verifyJWT(jwt)).toMatchSnapshot()
it('has correct payload in JWT for an attestation', async () => {
return uport.createVerification({sub: 'did:uport:223ab45', claim: {email: 'bingbangbung@email.com'}, exp: 1485321133 + 1}).then(async (jwt) => {
const decoded = await verifyJWT(jwt)
return expect(decoded).toMatchSnapshot()
})

@@ -246,3 +251,3 @@ })

describe('authenticate()', () => {
describe('verifyDisclosureResponse()', () => {
beforeAll(() => mockresolver({

@@ -254,10 +259,10 @@ name: 'Super Developer',

async function createShareResp (payload = {}) {
const req = await uport.requestDisclosure({requested: ['name', 'phone']})
return uport.disclose({...payload, req})
const req = await uport.createDisclosureRequest({requested: ['name', 'phone']})
return uport.createDisclosureResponse({...payload, req})
}
async function createShareRespWithVerifiedCredential (payload = {}) {
const req = await uport.requestDisclosure({requested: ['name', 'phone']})
const attestation = await uport.attest(claim)
return uport.disclose({...payload, verified: [attestation], req})
const req = await uport.createDisclosureRequest({requested: ['name', 'phone']})
const attestation = await uport.createVerification(claim)
return uport.createDisclosureResponse({...payload, verified: [attestation], req})
}

@@ -267,3 +272,3 @@

const jwt = await createShareResp({own: {name: 'Davie', phone: '+15555551234'}})
const profile = await uport.authenticate(jwt)
const profile = await uport.verifyDisclosureResponse(jwt)
expect(profile).toMatchSnapshot()

@@ -274,3 +279,3 @@ })

const jwt = await createShareRespWithVerifiedCredential({own: {name: 'Davie', phone: '+15555551234'}})
const profile = await uport.authenticate(jwt)
const profile = await uport.verifyDisclosureResponse(jwt)
expect(profile).toMatchSnapshot()

@@ -281,3 +286,3 @@ })

const jwt = await createShareResp()
const profile = await uport.authenticate(jwt)
const profile = await uport.verifyDisclosureResponse(jwt)
expect(profile).toMatchSnapshot()

@@ -288,3 +293,3 @@ })

const jwt = await createShareResp({nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m'})
const profile = await uport.authenticate(jwt)
const profile = await uport.verifyDisclosureResponse(jwt)
expect(profile).toMatchSnapshot()

@@ -295,19 +300,19 @@ })

const jwt = await createShareResp({dad: '0xdeviceKey'})
const profile = await uport.authenticate(jwt)
const profile = await uport.verifyDisclosureResponse(jwt)
expect(profile).toMatchSnapshot()
})
it('returns pushToken if available', async () => {
const jwt = await createShareResp({capabilities: ['PUSHTOKEN']})
const profile = await uport.authenticate(jwt)
const profile = await uport.verifyDisclosureResponse(jwt)
expect(profile).toMatchSnapshot()
})
it('handles response with missing challenge', async () => {
const jwt = await uport.disclose({own: {name: 'bob'}})
expect(uport.authenticate(jwt)).rejects.toMatchSnapshot()
const jwt = await uport.createDisclosureResponse({own: {name: 'bob'}})
expect(uport.verifyDisclosureResponse(jwt)).rejects.toMatchSnapshot()
})
})
describe('verifyProfile()', () => {
describe('verifyDisclosure()', () => {
beforeAll(() => mockresolver({

@@ -318,4 +323,4 @@ name: 'Bob Smith',

it('returns profile mixing public and private claims', async () => {
const jwt = await uport.disclose({own: {name: 'Davie', phone: '+15555551234'}})
const profile = await uport.verifyProfile(jwt)
const jwt = await uport.createDisclosureResponse({own: {name: 'Davie', phone: '+15555551234'}})
const profile = await uport.verifyDisclosure(jwt)
expect(profile).toMatchSnapshot()

@@ -325,11 +330,11 @@ })

it('returns profile mixing public and private claims and verified credentials', async () => {
const attestation = await uport.attest(claim)
const jwt = await uport.disclose({own: {name: 'Davie', phone: '+15555551234'}, verified: [attestation]})
const profile = await uport.verifyProfile(jwt)
const attestation = await uport.createVerification(claim)
const jwt = await uport.createDisclosureResponse({own: {name: 'Davie', phone: '+15555551234'}, verified: [attestation]})
const profile = await uport.verifyDisclosure(jwt)
expect(profile).toMatchSnapshot()
})
it('returns profile with only public claims', async () => {
const jwt = await uport.disclose()
const profile = await uport.verifyProfile(jwt)
const jwt = await uport.createDisclosureResponse()
const profile = await uport.verifyDisclosure(jwt)
expect(profile).toMatchSnapshot()

@@ -339,4 +344,4 @@ })

it('returns profile with private chain network id claims', async () => {
const jwt = await uport.disclose({nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m'})
const profile = await uport.verifyProfile(jwt)
const jwt = await uport.createDisclosureResponse({nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m'})
const profile = await uport.verifyDisclosure(jwt)
expect(profile).toMatchSnapshot()

@@ -346,4 +351,4 @@ })

it('returns pushToken if available', async () => {
const jwt = await uport.disclose({capabilities: ['PUSHTOKEN']})
const profile = await uport.verifyProfile(jwt)
const jwt = await uport.createDisclosureResponse({capabilities: ['PUSHTOKEN']})
const profile = await uport.verifyDisclosure(jwt)
expect(profile).toMatchSnapshot()

@@ -359,4 +364,4 @@ })

it('returns profile mixing public and private claims', async () => {
const req = await uport.requestDisclosure({requested: ['name', 'phone']})
const jwt = await uport.disclose({own: {name: 'Davie', phone: '+15555551234'}, req})
const req = await uport.createDisclosureRequest({requested: ['name', 'phone']})
const jwt = await uport.createDisclosureResponse({own: {name: 'Davie', phone: '+15555551234'}, req})
const profile = await uport.receive(jwt)

@@ -367,93 +372,35 @@ expect(profile).toMatchSnapshot()

describe('push', () => {
const PUTUTU_URL = 'https://pututu.uport.space' // TODO - change to .me
const API_v1_PATH = '/api/v1/sns'
const API_v2_PATH = '/api/v2/sns'
const PUSHTOKEN = 'SECRETPUSHTOKEN'
const payload = { url: 'me.uport:me', message: 'a friendly message' }
const kp = nacl.box.keyPair()
const pubEncKey = naclutil.encodeBase64(kp.publicKey)
const secEncKey = kp.secretKey
describe('txRequest()', () => {
beforeAll(() => mockresolver())
beforeEach(() => {
nock.disableNetConnect()
})
const abi = [{"constant":false,"inputs":[{"name":"status","type":"string"}],"name":"updateStatus","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"getStatus","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"}]
const address = '0x70A804cCE17149deB6030039798701a38667ca3B'
const statusContract = uport.contract(abi).at(address)
afterEach(() => {
nock.enableNetConnect()
it('creates a valid JWT for a request', async () => {
const jwt = await statusContract.updateStatus('hello')
const verified = await verifyJWT(jwt)
expect(verified.payload).toMatchSnapshot()
})
it('pushes url to pututu', () => {
nock(PUTUTU_URL, {
reqheaders: {
'authorization': `Bearer ${PUSHTOKEN}`
}
})
.post(API_v2_PATH, (body) => {
let encObj = JSON.parse(body.message)
const box = naclutil.decodeBase64(encObj.ciphertext)
const nonce = naclutil.decodeBase64(encObj.nonce)
const from = naclutil.decodeBase64(encObj.from)
const decrypted = nacl.box.open(box, nonce, from, secEncKey)
const result = JSON.parse(naclutil.encodeUTF8(decrypted))
return result.url === payload.url && result.message === payload.message
})
.reply(200, { status: 'success', message: 'd0b2bd07-d49e-5ba1-9b05-ec23ac921930' })
return uport.push(PUSHTOKEN, pubEncKey, payload).then(response => {
return expect(response).toEqual({ status: 'success', message: 'd0b2bd07-d49e-5ba1-9b05-ec23ac921930' })
})
it('encodes readable function calls including given args in function key of jwt', async () => {
const jwt = await statusContract.updateStatus('hello')
const verified = await verifyJWT(jwt)
expect(verified.payload.fn).toEqual("updateStatus(string 'hello')")
})
it('handles missing token', () => {
return uport.push(null, pubEncKey, payload).catch(error => expect(error.message).toEqual('Missing push notification token'))
it('adds to key as contract address to jwt', async () => {
const jwt = await statusContract.updateStatus('hello')
const verified = await verifyJWT(jwt)
expect(verified.payload.to).toEqual(address)
})
it('handles missing pubEncKey', () => {
nock(PUTUTU_URL, {
reqheaders: {
'authorization': `Bearer ${PUSHTOKEN}`
}
})
.post(API_v1_PATH, (body) => {
return body.message === payload.message && body.url === payload.url
})
.reply(200, { status: 'success', message: 'd0b2bd07-d49e-5ba1-9b05-ec23ac921930' })
console.error = jest.fn(msg => {
expect(msg).toEqual('WARNING: Calling push without a public encryption key is deprecated')
})
return uport.push(PUSHTOKEN, payload).then(response => {
return expect(response).toEqual({ status: 'success', message: 'd0b2bd07-d49e-5ba1-9b05-ec23ac921930' })
})
it('adds additional request options passed to jwt', async () => {
const network_id = '0x4'
const callbackUrl = 'mydomain'
const jwt = await statusContract.updateStatus('hello', {network_id, callbackUrl })
const verified = await verifyJWT(jwt)
expect(verified.payload.net).toEqual(network_id)
expect(verified.payload.callback).toEqual(callbackUrl)
})
it('handles missing payload', () => {
return uport.push(PUSHTOKEN, pubEncKey, {}).catch(error => expect(error.message).toEqual('Missing payload url for sending to users device'))
})
it('handles invalid token', () => {
nock(PUTUTU_URL, {
reqheaders: {
'authorization': `Bearer ${PUSHTOKEN}`
}
})
.post(API_v2_PATH, () => true)
.reply(403, 'Not allowed')
return uport.push(PUSHTOKEN, pubEncKey, payload).catch(error => expect(error.message).toEqual('Error sending push notification to user: Invalid Token'))
})
it('handles random error', () => {
nock(PUTUTU_URL, {
reqheaders: {
'authorization': `Bearer ${PUSHTOKEN}`
}
})
.post(API_v2_PATH, () => true)
.reply(500, 'Server Error')
return uport.push(PUSHTOKEN, pubEncKey, payload).catch(error => expect(error.message).toEqual('Error sending push notification to user: 500 Server Error'))
})
})

@@ -0,27 +1,35 @@

import { ec as EC } from 'elliptic'
import { createJWT, verifyJWT, SimpleSigner } from 'did-jwt'
import UportLite from 'uport-lite'
import nets from 'nets'
import nacl from 'tweetnacl'
import naclutil from 'tweetnacl-util'
const MNID = require('mnid')
import { toEthereumAddress } from 'did-jwt/lib/Digest'
import UportDIDResolver from 'uport-did-resolver'
import MuportDIDResolver from 'muport-did-resolver'
import EthrDIDResolver from 'ethr-did-resolver'
import { toEthereumAddress } from 'did-jwt/lib/Digest'
import { ec as EC } from 'elliptic'
import UportLite from 'uport-lite'
import { isMNID } from 'mnid'
import { ContractFactory } from './Contract.js'
const secp256k1 = new EC('secp256k1')
const Types = {
SHARE_REQ: 'shareReq',
SHARE_RESP: 'shareResp',
VER_REQ: 'verReq',
ETH_TX: 'ethtx'
}
/**
* The Credentials class allows you to easily create the signed payloads used in uPort inlcuding
* credentials and signed mobile app requests (ex. selective disclosure requests
* for private data). It also provides signature verification over signed payloads and
* allows you to send push notifications to users.
*/
* The Credentials class allows you to easily create the signed payloads used in uPort including
* credentials and signed mobile app requests (ex. selective disclosure requests
* for private data). It also provides signature verification over signed payloads.
*/
class Credentials {
/**
* Instantiates a new uPort Credentials object
*
* The following example is just for testing purposes. You should never store a private key in source code.
*
* The following example is just for testing purposes. *You should never store a private key in source code.*
*
* @example
* import { Credentials } from 'uport'
* import { Credentials } from 'uport-credentials'
* const credentials = new Credentials({

@@ -31,7 +39,7 @@ * privateKey: '74894f8853f90e6e3d6dfdd343eb0eb70cca06e552ed8af80adadcc573b35da3'

*
* The above example derives the public key used to generate the did, so only a private key is needed.
* The above example derives the public key used to generate the did, so only a private key is needed.
* Generating a public key from a private key is slow. It is recommended to configure the `did` option as well.
*
*
* @example
* import { Credentials } from 'uport'
* import { Credentials } from 'uport-credentials'
* const credentials = new Credentials({

@@ -43,5 +51,5 @@ * did: 'did:ethr:0xbc3ae59bc76f894822622cdef7a2018dbe353840',

* It is recommended to store the address and private key in environment variables for your server application
*
*
* @example
* import { Credentials, SimpleSigner } from 'uport'
* import { Credentials, SimpleSigner } from 'uport-credentials'
* const credentials = new Credentials({

@@ -51,9 +59,9 @@ * did: process.env.APPLICATION_DID,

* })
*
* Instead of a private key you can pass in a [Signer Functions](https://github.com/uport-project/did-jwt#signer-functions) to
*
* Instead of a private key you can pass in a [Signer Functions](https://github.com/uport-project/did-jwt#signer-functions) to
* present UX or call a HSM.
*
* @example
* import { Credentials } from 'uport'
*
* import { Credentials } from 'uport-credentials'
*
* function mySigner (data) {

@@ -65,3 +73,3 @@ * return new Promise((resolve, reject) => {

* }
*
*
* const credentials = new Credentials({

@@ -77,2 +85,3 @@ * did: process.env.APPLICATION_DID,

* @param {Object} settings.ethrConfig Configuration object for ethr did resolver. See [ethr-did-resolver](https://github.com/uport-project/ethr-did-resolver)
* @param {Object} settings.muportConfig Configuration object for muport did resolver. See [muport-did-resolver](https://github.com/uport-project/muport-did-resolver)
* @param {Address} settings.address DEPRECATED your uPort address (may be the address of your application's uPort identity)

@@ -83,3 +92,3 @@ * @param {Object} settings.networks DEPRECATED networks config object, ie. { '0x94365e3b': { rpcUrl: 'https://private.chain/rpc', address: '0x0101.... }}

*/
constructor ({did, address, privateKey, signer, networks, registry, ethrConfig} = {}) {
constructor ({did, address, privateKey, signer, networks, registry, ethrConfig, muportConfig} = {}) {
if (signer) {

@@ -89,8 +98,8 @@ this.signer = signer

this.signer = SimpleSigner(privateKey)
}
if (did) {
this.did = did
} else if (address) {
if (MNID.isMNID(address)) {
if (isMNID(address)) {
this.did = `did:uport:${address}`

@@ -111,6 +120,15 @@ }

EthrDIDResolver(ethrConfig || {})
MuportDIDResolver(muportConfig || {})
}
/**
* generate a DID and private key
* Generate a DID and private key, effectively creating a new identity that can sign and verify data
*
* @example
* const {did, privateKey} = Credentials.createIdentity()
* const credentials = new Credentials({did, privateKey, ...})
*
* @returns {Object} keypair
* - {String} keypair.did An ethr-did string for the new identity
* - {String} keypair.privateKey The identity's private key, as a string
*/

@@ -126,24 +144,24 @@ static createIdentity () {

/**
* Creates a [Selective Disclosure Request JWT](https://github.com/uport-project/specs/blob/develop/messages/sharereq.md)
*
* @example
* const req = { requested: ['name', 'country'],
* callbackUrl: 'https://myserver.com',
* notifications: true }
* credentials.requestDisclosure(req).then(jwt => {
* ...
* })
*
* @param {Object} [params={}] request params object
* @param {Array} params.requested an array of attributes for which you are requesting credentials to be shared for
* @param {Array} params.verified an array of attributes for which you are requesting verified credentials to be shared for
* @param {Boolean} params.notifications boolean if you want to request the ability to send push notifications
* @param {String} params.callbackUrl the url which you want to receive the response of this request
* @param {String} params.network_id network id of Ethereum chain of identity eg. 0x4 for rinkeby
* @param {String} params.accountType Ethereum account type: "general", "segregated", "keypair", "devicekey" or "none"
* @param {Number} expiresIn Seconds until expiry
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
*/
requestDisclosure (params = {}, expiresIn = 600) {
/**
* Creates a [Selective Disclosure Request JWT](https://github.com/uport-project/specs/blob/develop/messages/sharereq.md)
*
* @example
* const req = { requested: ['name', 'country'],
* callbackUrl: 'https://myserver.com',
* notifications: true }
* credentials.requestDisclosure(req).then(jwt => {
* ...
* })
*
* @param {Object} [params={}] request params object
* @param {Array} params.requested an array of attributes for which you are requesting credentials to be shared for
* @param {Array} params.verified an array of attributes for which you are requesting verified credentials to be shared for
* @param {Boolean} params.notifications boolean if you want to request the ability to send push notifications
* @param {String} params.callbackUrl the url which you want to receive the response of this request
* @param {String} params.network_id network id of Ethereum chain of identity eg. 0x4 for rinkeby
* @param {String} params.accountType Ethereum account type: "general", "segregated", "keypair", "devicekey" or "none"
* @param {Number} expiresIn Seconds until expiry
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
*/
createDisclosureRequest (params = {}, expiresIn = 600) {
const payload = {}

@@ -175,99 +193,160 @@ if (params.requested) {

}
return this.signJWT({...payload, type: 'shareReq'}, params.exp ? undefined : expiresIn)
return this.signJWT({...payload, type: Types.SHARE_REQ}, params.exp ? undefined : expiresIn)
}
/**
* Creates a [Selective Disclosure Request JWT](https://github.com/uport-project/specs/blob/develop/messages/sharereq.md)
*
* @example
* const req = { requested: ['name', 'country'],
* callbackUrl: 'https://myserver.com',
* notifications: true }
* credentials.createRequest(req).then(jwt => {
* ...
* })
*
* @param {Object} [params={}] request params object
* @param {Array} params.requested an array of attributes for which you are requesting credentials to be shared for
* @param {Array} params.verified an array of attributes for which you are requesting verified credentials to be shared for
* @param {Boolean} params.notifications boolean if you want to request the ability to send push notifications
* @param {String} params.callbackUrl the url which you want to receive the response of this request
* @param {String} params.network_id network id of Ethereum chain of identity eg. 0x4 for rinkeby
* @param {String} params.accountType Ethereum account type: "general", "segregated", "keypair", "devicekey" or "none"
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
* @deprecated
*/
createRequest (params = {}) {
return this.requestDisclosure(params)
/**
* Create a credential (a signed JSON Web Token)
*
* @example
* credentials.attest({
* sub: '5A8bRWU3F7j3REx3vkJ...', // uPort address of user, likely a MNID
* exp: <future timestamp>,
* claim: { name: 'John Smith' }
* }).then( credential => {
* ...
* })
*
* @param {Object} [credential] a unsigned credential object
* @param {String} credential.sub subject of credential (a uPort address)
* @param {String} credential.claim claim about subject single key value or key mapping to object with multiple values (ie { address: {street: ..., zip: ..., country: ...}})
* @param {String} credential.exp time at which this claim expires and is no longer valid (seconds since epoch)
* @return {Promise<Object, Error>} a promise which resolves with a credential (JWT) or rejects with an error
*/
createVerification ({sub, claim, exp}) {
return this.signJWT({sub: sub, claim, exp})
}
/**
* Creates a [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md).
*
* This can either be used to share information about the signing identity or as the response to a
* [Selective Disclosure Flow](https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md), where it can be used to authenticate the identity.
*
* @example
* credentials.disclose({own: {name: 'Lourdes Valentina Gomez'}}).then(jwt => {
* ...
* })
*
* @param {Object} [params={}] request params object
* @param {JWT} params.req A selective disclosure Request JWT if this is returned as part of an authentication flow
* @param {Object} params.own An object of self attested claims about the signer (eg. name etc)
* @param {Array} params.verified An array of attestation JWT's to include
* @param {MNID} params.nad An ethereum address encoded as an [MNID](https://github.com/uport-project/mnid)
* @param {Array} params.capabilities An array of capability JWT's to include
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
*/
async disclose (payload = {}, expiresIn = 600 ) {
if (payload.req) {
const verified = await verifyJWT(payload.req)
if (verified.issuer) {
payload.aud = verified.issuer
}
/**
* Creates a signed request for the user to attest a list of claims.
*
* @example
* const unsignedClaim = {
* claim: {
* "Citizen of city X": {
* "Allowed to vote": true,
* "Document": "QmZZBBKPS2NWc6PMZbUk9zUHCo1SHKzQPPX4ndfwaYzmPW"
* }
* },
* sub: "2oTvBxSGseWFqhstsEHgmCBi762FbcigK5u"
* }
* credentials.createVerificationRequest(unsignedClaim).then(jwt => {
* ...
* })
*
* @param {Object} unsignedClaim an object that is an unsigned claim which you want the user to attest
* @param {String} aud the DID of the identity you want to sign the attestation
* @param {String} sub the DID which the unsigned claim is about
* @param {String} callbackUrl the url which you want to receive the response of this request
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
*/
createSignVerificationRequest(unsignedClaim, sub, callbackUrl, aud) {
return this.signJWT({unsignedClaim, sub, aud, callback: callbackUrl, type: Types.VER_REQ})
}
return this.signJWT({...payload, type: 'shareResp'}, expiresIn)
}
async processDisclosurePayload ({doc, payload}) {
const credentials = {...doc.uportProfile || {}, ...(payload.own || {}), ...(payload.capabilities && payload.capabilities.length === 1 ? {pushToken: payload.capabilities[0]} : {}), address: payload.iss}
if (payload.nad) {
credentials.networkAddress = payload.nad
/**
* Given a transaction object, similarly defined as the web3 transaction object,
* it creates a JWT transaction request and appends addtional request options.
*
* @example
* const txobject = {
* to: '0xc3245e75d3ecd1e81a9bfb6558b6dafe71e9f347',
* value: '0.1',
* fn: "setStatus(string 'hello', bytes32 '0xc3245e75d3ecd1e81a9bfb6558b6dafe71e9f347')",
* }
* connect.createTxRequest(txObject, {callbackUrl: 'http://mycb.domain'}).then(jwt => {
* ...
* })
*
* @param {Object} txObj
* @param {String} [id='addressReq'] string to identify request, later used to get response
* @return {String} a transaction request jwt
*/
createTxRequest(txObj, { callbackUrl, exp = 600, network_id, label } = {}) {
const payload = {}
if (callbackUrl) payload.callback = callbackUrl
if (network_id) payload.net = network_id
if (label) payload.label = label
return this.signJWT({...payload, ...txObj, type: Types.ETH_TX}, exp )
}
if (payload.dad) {
credentials.deviceKey = payload.dad
/**
* Creates a [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md).
*
* This can either be used to share information about the signing identity or as the response to a
* [Selective Disclosure Flow](https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md),
* where it can be used to verifyDisclosureResponse the identity.
*
* @example
* credentials.createDisclosureResponse({own: {name: 'Lourdes Valentina Gomez'}}).then(jwt => {
* ...
* })
*
* @param {Object} [payload={}] request params object
* @param {JWT} payload.req A selective disclosure Request JWT if this is returned as part of an authentication flow
* @param {Object} payload.own An object of self attested claims about the signer (eg. name etc)
* @param {Array} payload.verified An array of attestation JWT's to include
* @param {MNID} payload.nad An ethereum address encoded as an [MNID](https://github.com/uport-project/mnid)
* @param {Array} payload.capabilities An array of capability JWT's to include
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error
*/
async createDisclosureResponse (payload = {}, expiresIn = 600 ) {
if (payload.req) {
const verified = await verifyJWT(payload.req)
if (verified.issuer) {
payload.aud = verified.issuer
}
}
return this.signJWT({...payload, type: Types.SHARE_RESP}, expiresIn)
}
if (payload.verified) {
const verified = await Promise.all(payload.verified.map(token => verifyJWT(token, {audience: this.did})))
return {...credentials, verified: verified.map(v => ({...v.payload, jwt: v.jwt}))}
} else {
return credentials
/**
* Parse a selective disclosure response, and verify signatures on each included signed claim ("verification")
*
* @param {Object} response A selective disclosure response payload, with associated did doc
* @param {Object} response.doc
*/
async processDisclosurePayload ({doc, payload}) {
const credentials = {...doc.uportProfile || {}, ...(payload.own || {}), ...(payload.capabilities && payload.capabilities.length === 1 ? {pushToken: payload.capabilities[0]} : {}), address: payload.iss}
if (payload.nad) {
credentials.networkAddress = payload.nad
}
if (payload.dad) {
credentials.deviceKey = payload.dad
}
if (payload.verified) {
const verified = await Promise.all(payload.verified.map(token => verifyJWT(token, {audience: this.did})))
return {...credentials, verified: verified.map(v => ({...v.payload, jwt: v.jwt}))}
} else {
return credentials
}
}
}
/**
* Authenticates [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md) from mobile
* app as part of the [Selective Disclosure Flow](https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md).
*
* It Verifies and parses the given response token and verifies the challenge response flow.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.authenticate(resToken).then(res => {
* const credentials = res.verified
* const name = res.name
* ...
* })
*
* @param {String} token a response token
* @param {String} [callbackUrl=null] callbackUrl
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
*/
async authenticate (token, callbackUrl = null) {
/**
* Authenticates [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md) from mobile
* app as part of the [Selective Disclosure Flow](https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md).
*
* It Verifies and parses the given response token and verifies the challenge response flow.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.verifyDisclosureResponse(resToken).then(res => {
* const credentials = res.verified
* const name = res.name
* ...
* })
*
* @param {String} token a response token
* @param {String} [callbackUrl=null] callbackUrl
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
*/
async verifyDisclosureResponse (token, callbackUrl = null) {
const { payload, doc } = await verifyJWT(token, {audience: this.did, callbackUrl, auth: true})
if(payload.req) {
if (payload.req) {
const challenge = await verifyJWT(payload.req)
if(challenge.payload.iss === this.did && challenge.payload.type === 'shareReq') {
if (challenge.payload.iss !== this.did) {
throw new Error(`Challenge issuer does not match current identity: ${challenge.payload.iss} !== ${this.did}`)
} else if (challenge.payload.type !== Types.SHARE_REQ) {
throw new Error(`Challenge payload type invalid: ${challenge.payload.type}`)
} else {
return this.processDisclosurePayload({payload, doc})

@@ -281,137 +360,59 @@ }

/**
* Receive signed response token from mobile app. Verifies and parses the given response token.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.receive(resToken).then(res => {
* const credentials = res.verified
const name = res.name
* ...
* })
*
* @param {String} token a response token
* @param {String} [callbackUrl=null] callbackUrl
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
* @deprecated
*/
* Receive signed response token from mobile app. Verifies and parses the given response token.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.receive(resToken).then(res => {
* const credentials = res.verified
* const name = res.name
* ...
* })
*
* @param {String} token a response token
* @param {String} [callbackUrl=null] callbackUrl
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
* @deprecated
*/
receive (token, callbackUrl = null) {
return this.authenticate(token, callbackUrl)
return this.verifyDisclosureResponse(token, callbackUrl)
}
/**
* Verify and return profile from a [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md).
*
* The main difference between this and `authenticate()` is that it does not verify the challenge. This can be used to verify user profiles that have been shared
* through other methods such as QR codes and messages.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.verifyProfile(resToken).then(profile => {
* const credentials = profile.verified
const name = profile.name
* ...
* })
*
* @param {String} token a response token
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
*/
async verifyProfile (token) {
const { payload, doc } = await verifyJWT(token, {audience: this.did})
return this.processDisclosurePayload({ payload, doc })
}
/**
* Send a push notification to a user, consumes a token which allows you to send push notifications
* and a url/uri request you want to send to the user.
*
* @param {String} token a push notification token (get a pn token by requesting push permissions in a request)
* @param {Object} payload push notification payload
* @param {String} payload.url a uport request url
* @param {String} payload.message a message to display to the user
* @param {String} pubEncKey the public encryption key of the receiver, encoded as a base64 string
* @return {Promise<Object, Error>} a promise which resolves with successful status or rejects with an error
*/
push (token, pubEncKey, payload) {
const PUTUTU_URL = 'https://pututu.uport.space' // TODO - change to .me
return new Promise((resolve, reject) => {
let endpoint = '/api/v2/sns'
if (!token) {
return reject(new Error('Missing push notification token'))
}
//if (!pubEncKey) {
//return reject(new Error('Missing public encryption key of the receiver'))
//}
if (pubEncKey.url) {
console.error('WARNING: Calling push without a public encryption key is deprecated')
endpoint = '/api/v1/sns'
payload = pubEncKey
} else {
if (!payload.url) {
return reject(new Error('Missing payload url for sending to users device'))
}
const plaintext = padMessage(JSON.stringify(payload))
const enc = encryptMessage(plaintext, pubEncKey)
payload = { message: JSON.stringify(enc) }
}
nets({
uri: PUTUTU_URL + endpoint,
json: payload,
method: 'POST',
withCredentials: false,
headers: {
Authorization: `Bearer ${token}`
}
},
(error, res, body) => {
if (error) return reject(error)
if (res.statusCode === 200) {
resolve(body)
}
if (res.statusCode === 403) {
return reject(new Error('Error sending push notification to user: Invalid Token'))
}
reject(new Error(`Error sending push notification to user: ${res.statusCode} ${body.toString()}`))
})
})
* Verify and return profile from a [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md).
*
* The main difference between this and `verifyDisclosureResponse()` is that it does not verify the challenge.
* This can be used to verify user profiles that have been shared through other methods such as QR codes and messages.
*
* @example
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....'
* credentials.verifyDisclosure(resToken).then(profile => {
* const credentials = profile.verified
* const name = profile.name
* ...
* })
*
* @param {String} token a response token
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error.
*/
async verifyDisclosure (token) {
const { payload, doc } = await verifyJWT(token, {audience: this.did})
return this.processDisclosurePayload({ payload, doc })
}
/**
* Create a credential (a signed JSON Web Token)
*
* @example
* credentials.attest({
* sub: '5A8bRWU3F7j3REx3vkJ...', // uPort address of user, likely a MNID
* exp: <future timestamp>,
* claim: { name: 'John Smith' }
* }).then( credential => {
* ...
* })
*
* @param {Object} [credential] a unsigned credential object
* @param {String} credential.sub subject of credential (a uPort address)
* @param {String} credential.claim claim about subject single key value or key mapping to object with multiple values (ie { address: {street: ..., zip: ..., country: ...}})
* @param {String} credential.exp time at which this claim expires and is no longer valid (seconds since epoch)
* @return {Promise<Object, Error>} a promise which resolves with a credential (JWT) or rejects with an error
*/
attest ({sub, claim, exp}) {
return this.signJWT({sub: sub, claim, exp})
/**
* Builds and returns a contract object which can be used to interact with
* a given contract. Similar to web3.eth.contract but with promises. Once specifying .at(address)
* you can call the contract functions with this object. Each call will create a request.
*
* @param {Object} abi contract ABI
* @return {Object} contract object
*/
contract (abi) {
const txObjHandler = (txObj, opts) => {
if (txObj.function) txObj.fn = txObj.function
delete txObj['function']
return this.createTxRequest(txObj, opts)
}
return ContractFactory(txObjHandler.bind(this))(abi)
}
// /**
// * Look up a profile in the registry for a given uPort address. Address must be MNID encoded.
// *
// * @example
// * credentials.lookup('5A8bRWU3F7j3REx3vkJ...').then(profile => {
// * const name = profile.name
// * const pubkey = profile.pubkey
// * ...
// * })
// *
// * @param {String} address a MNID encoded address
// * @return {Promise<Object, Error>} a promise which resolves with parsed profile or rejects with an error
// */
// lookup (address) {
// return this.settings.registry(address)
// }
}

@@ -433,38 +434,3 @@

/**
* Adds padding to a string
*
* @param {String} the message to be padded
* @return {String} the padded message
* @private
*/
const padMessage = (message) => {
const INTERVAL_LENGTH = 50
const padLength = INTERVAL_LENGTH - message.length % INTERVAL_LENGTH
return message + ' '.repeat(padLength)
}
/**
* Encrypts a message
*
* @param {String} the message to be encrypted
* @param {String} the public encryption key of the receiver, encoded as base64
* @return {String} the encrypted message, encoded as base64
* @private
*/
const encryptMessage = (message, receiverKey) => {
const tmpKp = nacl.box.keyPair()
const decodedKey = naclutil.decodeBase64(receiverKey)
const decodedMsg = naclutil.decodeUTF8(message)
const nonce = nacl.randomBytes(24)
const ciphertext = nacl.box(decodedMsg, nonce, decodedKey, tmpKp.secretKey)
return {
from: naclutil.encodeBase64(tmpKp.publicKey),
nonce: naclutil.encodeBase64(nonce),
ciphertext: naclutil.encodeBase64(ciphertext)
}
}
export default Credentials

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet