Comparing version 0.7.0-alpha-minor-12 to 0.7.0-alpha-minor-13
'use strict'; | ||
var _Contract = require('../Contract'); | ||
var _Contract = require('..//Contract'); | ||
var address = '0x41566e3a081f5032bdcad470adb797635ddfe1f0'; | ||
var address = '2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX'; | ||
var abiToken = [{ | ||
@@ -251,4 +251,10 @@ "constant": true, | ||
var uri = tokenContract.transfer('0x41566e3a081f5032bdcad470adb797635ddfe1f0', 10); | ||
expect(uri).toEqual("me.uport:0x41566e3a081f5032bdcad470adb797635ddfe1f0?function=transfer(address 0x41566e3a081f5032bdcad470adb797635ddfe1f0, uint256 10)"); | ||
expect(uri).toEqual("https://id.uport.me/2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX?function=transfer(address%200x41566e3a081f5032bdcad470adb797635ddfe1f0%2C%20uint256%2010)"); | ||
}); | ||
it('it add addtional params to request when passed as last arg', function () { | ||
var params = { callbackUrl: 'http://myswebsite.com', type: 'post' }; | ||
var uri = tokenContract.transfer('0x41566e3a081f5032bdcad470adb797635ddfe1f0', 10, params); | ||
expect(uri).toEqual("https://id.uport.me/2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX?function=transfer(address%200x41566e3a081f5032bdcad470adb797635ddfe1f0%2C%20uint256%2010)&callback_url=http%3A%2F%2Fmyswebsite.com&type=post"); | ||
}); | ||
}); | ||
@@ -255,0 +261,0 @@ |
@@ -19,2 +19,4 @@ 'use strict'; | ||
var _JWT = require('../JWT'); | ||
var _didJwt = require('did-jwt'); | ||
@@ -28,2 +30,14 @@ | ||
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); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -36,8 +50,8 @@ | ||
var address = '0xbc3ae59bc76f894822622cdef7a2018dbe353840'; | ||
var did = 'did:ethr:' + address; | ||
var mnid = '2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX'; | ||
var did = 'did:ethr:' + mnid; | ||
var claim = { sub: '0x112233', claim: { email: 'bingbangbung@email.com' }, exp: 1485321133 + 1 }; | ||
var uport = new _Credentials2.default({ privateKey: privateKey, did: did }); | ||
var uport = new _Credentials2.default({ signer: signer, address: mnid }); | ||
var uport2 = new _Credentials2.default({}); | ||
@@ -48,3 +62,3 @@ | ||
(0, _didResolver.registerMethod)('ethr', function () { | ||
(0, _didResolver.registerMethod)('uport', function () { | ||
var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(id, parsed) { | ||
@@ -62,2 +76,3 @@ var doc; | ||
type: 'Secp256k1VerificationKey2018', | ||
publicKeyHex: '048f71f156f9b489d8c566c9943585d4c255aa5d22924abe3b9f7997de46a378ac89a668eacb9053ceac72f6a0abdeee025d61984059a6732e6cb4f106ed281ffe', | ||
owner: id, | ||
@@ -94,28 +109,18 @@ ethereumAddress: parsed.id | ||
describe('sets did', function () { | ||
describe('`did` configured', function () { | ||
expect(new _Credentials2.default({ did: did }).did).toEqual(did); | ||
it('ethereum `address` configured', function () { | ||
expect(function () { | ||
return new _Credentials2.default({ address: address }); | ||
}).toThrowError('Only MNID app identities accepted'); | ||
}); | ||
describe('ethereum `address` configured', function () { | ||
expect(new _Credentials2.default({ address: address }).did).toEqual(did); | ||
it('mnid `address` configured', function () { | ||
expect(new _Credentials2.default({ address: mnid }).settings.did).toEqual('did:uport:' + mnid); | ||
}); | ||
describe('`privateKey` configured', function () { | ||
expect(new _Credentials2.default({ privateKey: privateKey }).did).toEqual(did); | ||
}); | ||
describe('mnid `address` configured', function () { | ||
expect(new _Credentials2.default({ address: mnid }).did).toEqual('did:uport:' + mnid); | ||
}); | ||
}); | ||
describe('sets signer', function () { | ||
describe('always uses signer if passed in', function () { | ||
it('always uses signer if passed in', function () { | ||
var signer = (0, _didJwt.SimpleSigner)(privateKey); | ||
expect(new _Credentials2.default({ signer: signer, privateKey: privateKey }).signer).toEqual(signer); | ||
expect(new _Credentials2.default({ signer: signer, mnid: mnid }).settings.signer).toEqual(signer); | ||
}); | ||
describe('sets signer if privateKey is passed in', function () { | ||
expect(new _Credentials2.default({ privateKey: privateKey }).signer).toBeDefined(); | ||
}); | ||
}); | ||
@@ -154,13 +159,2 @@ | ||
describe('createIdentity()', function () { | ||
it('creates Identity', function () { | ||
var _Credentials$createId = _Credentials2.default.createIdentity(), | ||
did = _Credentials$createId.did, | ||
privateKey = _Credentials$createId.privateKey; | ||
expect(did).toMatch(/^did:ethr:0x[0-9a-fA-F]{40}$/); | ||
expect(privateKey).toMatch(/^[0-9a-fA-F]{64}$/); | ||
}); | ||
}); | ||
describe('signJWT', function () { | ||
@@ -175,5 +169,5 @@ describe('uport method', function () { | ||
case 0: | ||
credentials = new _Credentials2.default({ address: mnid, privateKey: privateKey }); | ||
credentials = new _Credentials2.default({ address: mnid, signer: signer }); | ||
_context2.next = 3; | ||
return credentials.signJWT({ hello: 1 }); | ||
return (0, _JWT.createJWT)({ address: address, signer: signer }, { hello: 1 }); | ||
@@ -194,61 +188,34 @@ case 3: | ||
}); | ||
describe('ethr method', function () { | ||
it('uses ES256K-R algorithm', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3() { | ||
var credentials, jwt, _decodeJWT2, header; | ||
return _regenerator2.default.wrap(function _callee3$(_context3) { | ||
while (1) { | ||
switch (_context3.prev = _context3.next) { | ||
case 0: | ||
credentials = new _Credentials2.default({ did: did, privateKey: privateKey }); | ||
_context3.next = 3; | ||
return credentials.signJWT({ hello: 1 }); | ||
case 3: | ||
jwt = _context3.sent; | ||
_decodeJWT2 = (0, _didJwt.decodeJWT)(jwt), header = _decodeJWT2.header; | ||
expect(header.alg).toEqual('ES256K-R'); | ||
case 6: | ||
case 'end': | ||
return _context3.stop(); | ||
} | ||
} | ||
}, _callee3, undefined); | ||
}))); | ||
}); | ||
}); | ||
describe('requestDisclosure()', function () { | ||
describe('createRequest()', function () { | ||
var createAndVerify = function () { | ||
var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4() { | ||
var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3() { | ||
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var jwt; | ||
return _regenerator2.default.wrap(function _callee4$(_context4) { | ||
return _regenerator2.default.wrap(function _callee3$(_context3) { | ||
while (1) { | ||
switch (_context4.prev = _context4.next) { | ||
switch (_context3.prev = _context3.next) { | ||
case 0: | ||
_context4.next = 2; | ||
return uport.requestDisclosure(params); | ||
_context3.next = 2; | ||
return uport.createRequest(params); | ||
case 2: | ||
jwt = _context4.sent; | ||
_context4.next = 5; | ||
return (0, _didJwt.verifyJWT)(jwt); | ||
jwt = _context3.sent; | ||
_context3.next = 5; | ||
return (0, _JWT.verifyJWT)({ address: mnid, signer: signer }, jwt); | ||
case 5: | ||
return _context4.abrupt('return', _context4.sent); | ||
return _context3.abrupt('return', _context3.sent); | ||
case 6: | ||
case 'end': | ||
return _context4.stop(); | ||
return _context3.stop(); | ||
} | ||
} | ||
}, _callee4, this); | ||
}, _callee3, this); | ||
})); | ||
return function createAndVerify() { | ||
return _ref4.apply(this, arguments); | ||
return _ref3.apply(this, arguments); | ||
}; | ||
@@ -261,4 +228,25 @@ }(); | ||
it('creates a valid JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5() { | ||
it('creates a valid JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4() { | ||
var response; | ||
return _regenerator2.default.wrap(function _callee4$(_context4) { | ||
while (1) { | ||
switch (_context4.prev = _context4.next) { | ||
case 0: | ||
_context4.next = 2; | ||
return createAndVerify({ requested: ['name', 'phone'] }); | ||
case 2: | ||
response = _context4.sent; | ||
return _context4.abrupt('return', expect(response).toMatchSnapshot()); | ||
case 4: | ||
case 'end': | ||
return _context4.stop(); | ||
} | ||
} | ||
}, _callee4, undefined); | ||
}))); | ||
it('has correct payload in JWT for a plain request for public details', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5() { | ||
var response; | ||
return _regenerator2.default.wrap(function _callee5$(_context5) { | ||
@@ -269,3 +257,3 @@ while (1) { | ||
_context5.next = 2; | ||
return createAndVerify({ requested: ['name', 'phone'] }); | ||
return createAndVerify(); | ||
@@ -284,3 +272,3 @@ case 2: | ||
it('has correct payload in JWT for a plain request for public details', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6() { | ||
it('has correct payload in JWT requesting a specific network_id', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6() { | ||
var response; | ||
@@ -292,3 +280,3 @@ return _regenerator2.default.wrap(function _callee6$(_context6) { | ||
_context6.next = 2; | ||
return createAndVerify(); | ||
return createAndVerify({ network_id: '0x4' }); | ||
@@ -328,43 +316,22 @@ case 2: | ||
it('has correct payload in JWT requesting a specific network_id', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee8() { | ||
var response; | ||
return _regenerator2.default.wrap(function _callee8$(_context8) { | ||
while (1) { | ||
switch (_context8.prev = _context8.next) { | ||
case 0: | ||
_context8.next = 2; | ||
return createAndVerify({ network_id: '0x4' }); | ||
case 2: | ||
response = _context8.sent; | ||
return _context8.abrupt('return', expect(response).toMatchSnapshot()); | ||
case 4: | ||
case 'end': | ||
return _context8.stop(); | ||
} | ||
} | ||
}, _callee8, undefined); | ||
}))); | ||
var _loop = function _loop(accountType) { | ||
it('has correct payload in JWT requesting accountType of ' + accountType, (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee15() { | ||
it('has correct payload in JWT requesting accountType of ' + accountType, (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee14() { | ||
var response; | ||
return _regenerator2.default.wrap(function _callee15$(_context15) { | ||
return _regenerator2.default.wrap(function _callee14$(_context14) { | ||
while (1) { | ||
switch (_context15.prev = _context15.next) { | ||
switch (_context14.prev = _context14.next) { | ||
case 0: | ||
_context15.next = 2; | ||
_context14.next = 2; | ||
return createAndVerify({ accountType: accountType }); | ||
case 2: | ||
response = _context15.sent; | ||
return _context15.abrupt('return', expect(response).toMatchSnapshot()); | ||
response = _context14.sent; | ||
return _context14.abrupt('return', expect(response).toMatchSnapshot()); | ||
case 4: | ||
case 'end': | ||
return _context15.stop(); | ||
return _context14.stop(); | ||
} | ||
} | ||
}, _callee15, undefined); | ||
}, _callee14, undefined); | ||
}))); | ||
@@ -379,6 +346,6 @@ }; | ||
it('has correct payload in JWT requesting unsupported accountType', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee9() { | ||
return _regenerator2.default.wrap(function _callee9$(_context9) { | ||
it('has correct payload in JWT requesting unsupported accountType', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee8() { | ||
return _regenerator2.default.wrap(function _callee8$(_context8) { | ||
while (1) { | ||
switch (_context9.prev = _context9.next) { | ||
switch (_context8.prev = _context8.next) { | ||
case 0: | ||
@@ -389,2 +356,23 @@ expect(createAndVerify({ accountType: 'gold' })).rejects.toMatchSnapshot(); | ||
case 'end': | ||
return _context8.stop(); | ||
} | ||
} | ||
}, _callee8, undefined); | ||
}))); | ||
it('ignores unsupported request parameters', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee9() { | ||
var response; | ||
return _regenerator2.default.wrap(function _callee9$(_context9) { | ||
while (1) { | ||
switch (_context9.prev = _context9.next) { | ||
case 0: | ||
_context9.next = 2; | ||
return createAndVerify({ signing: true, sellSoul: true }); | ||
case 2: | ||
response = _context9.sent; | ||
return _context9.abrupt('return', expect(response).toMatchSnapshot()); | ||
case 4: | ||
case 'end': | ||
return _context9.stop(); | ||
@@ -396,3 +384,3 @@ } | ||
it('ignores unsupported request parameters', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee10() { | ||
it('has correct payload in JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee10() { | ||
var response; | ||
@@ -404,3 +392,3 @@ return _regenerator2.default.wrap(function _callee10$(_context10) { | ||
_context10.next = 2; | ||
return createAndVerify({ signing: true, sellSoul: true }); | ||
return createAndVerify({ requested: ['name', 'phone'] }); | ||
@@ -419,3 +407,3 @@ case 2: | ||
it('has correct payload in JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee11() { | ||
it('has correct payload in JWT for a request asking for verified credentials', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee11() { | ||
var response; | ||
@@ -427,3 +415,3 @@ return _regenerator2.default.wrap(function _callee11$(_context11) { | ||
_context11.next = 2; | ||
return createAndVerify({ requested: ['name', 'phone'] }); | ||
return createAndVerify({ requested: ['name', 'phone'], verified: ['name'] }); | ||
@@ -442,3 +430,3 @@ case 2: | ||
it('has correct payload in JWT for a request asking for verified credentials', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee12() { | ||
it('has correct payload in JWT for a request with callbackUrl', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee12() { | ||
var response; | ||
@@ -450,3 +438,3 @@ return _regenerator2.default.wrap(function _callee12$(_context12) { | ||
_context12.next = 2; | ||
return createAndVerify({ requested: ['name', 'phone'], verified: ['name'] }); | ||
return createAndVerify({ requested: ['name', 'phone'], callbackUrl: 'https://myserver.com' }); | ||
@@ -465,3 +453,3 @@ case 2: | ||
it('has correct payload in JWT for a request with callbackUrl', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee13() { | ||
it('has correct payload in JWT for a request for push notifications', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee13() { | ||
var response; | ||
@@ -473,3 +461,3 @@ return _regenerator2.default.wrap(function _callee13$(_context13) { | ||
_context13.next = 2; | ||
return createAndVerify({ requested: ['name', 'phone'], callbackUrl: 'https://myserver.com' }); | ||
return createAndVerify({ requested: ['name', 'phone'], notifications: true }); | ||
@@ -487,23 +475,2 @@ case 2: | ||
}))); | ||
it('has correct payload in JWT for a request for push notifications', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee14() { | ||
var response; | ||
return _regenerator2.default.wrap(function _callee14$(_context14) { | ||
while (1) { | ||
switch (_context14.prev = _context14.next) { | ||
case 0: | ||
_context14.next = 2; | ||
return createAndVerify({ requested: ['name', 'phone'], notifications: true }); | ||
case 2: | ||
response = _context14.sent; | ||
return _context14.abrupt('return', expect(response).toMatchSnapshot()); | ||
case 4: | ||
case 'end': | ||
return _context14.stop(); | ||
} | ||
} | ||
}, _callee14, undefined); | ||
}))); | ||
}); | ||
@@ -513,30 +480,30 @@ | ||
var createAndVerify = function () { | ||
var _ref16 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee16() { | ||
var _ref15 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee15() { | ||
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var jwt; | ||
return _regenerator2.default.wrap(function _callee16$(_context16) { | ||
return _regenerator2.default.wrap(function _callee15$(_context15) { | ||
while (1) { | ||
switch (_context16.prev = _context16.next) { | ||
switch (_context15.prev = _context15.next) { | ||
case 0: | ||
_context16.next = 2; | ||
_context15.next = 2; | ||
return uport.createRequest(params); | ||
case 2: | ||
jwt = _context16.sent; | ||
_context16.next = 5; | ||
return (0, _didJwt.verifyJWT)(jwt); | ||
jwt = _context15.sent; | ||
_context15.next = 5; | ||
return (0, _JWT.verifyJWT)({}, jwt); | ||
case 5: | ||
return _context16.abrupt('return', _context16.sent); | ||
return _context15.abrupt('return', _context15.sent); | ||
case 6: | ||
case 'end': | ||
return _context16.stop(); | ||
return _context15.stop(); | ||
} | ||
} | ||
}, _callee16, this); | ||
}, _callee15, this); | ||
})); | ||
return function createAndVerify() { | ||
return _ref16.apply(this, arguments); | ||
return _ref15.apply(this, arguments); | ||
}; | ||
@@ -549,136 +516,50 @@ }(); | ||
it('creates a valid JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee17() { | ||
it('creates a valid JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee16() { | ||
var response; | ||
return _regenerator2.default.wrap(function _callee17$(_context17) { | ||
return _regenerator2.default.wrap(function _callee16$(_context16) { | ||
while (1) { | ||
switch (_context17.prev = _context17.next) { | ||
switch (_context16.prev = _context16.next) { | ||
case 0: | ||
_context17.next = 2; | ||
_context16.next = 2; | ||
return createAndVerify({ requested: ['name', 'phone'] }); | ||
case 2: | ||
response = _context17.sent; | ||
return _context17.abrupt('return', expect(response).toMatchSnapshot()); | ||
response = _context16.sent; | ||
return _context16.abrupt('return', expect(response).toMatchSnapshot()); | ||
case 4: | ||
case 'end': | ||
return _context17.stop(); | ||
return _context16.stop(); | ||
} | ||
} | ||
}, _callee17, undefined); | ||
}, _callee16, undefined); | ||
}))); | ||
}); | ||
describe('disclose()', function () { | ||
var createAndVerify = function () { | ||
var _ref18 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee18() { | ||
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var jwt; | ||
return _regenerator2.default.wrap(function _callee18$(_context18) { | ||
while (1) { | ||
switch (_context18.prev = _context18.next) { | ||
case 0: | ||
_context18.next = 2; | ||
return uport.disclose(params); | ||
case 2: | ||
jwt = _context18.sent; | ||
_context18.next = 5; | ||
return (0, _didJwt.verifyJWT)(jwt, { audience: did }); | ||
case 5: | ||
return _context18.abrupt('return', _context18.sent); | ||
case 6: | ||
case 'end': | ||
return _context18.stop(); | ||
} | ||
} | ||
}, _callee18, this); | ||
})); | ||
return function createAndVerify() { | ||
return _ref18.apply(this, arguments); | ||
}; | ||
}(); | ||
beforeAll(function () { | ||
return mockresolver(); | ||
}); | ||
it('creates a valid JWT for a disclosure', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee19() { | ||
var response; | ||
return _regenerator2.default.wrap(function _callee19$(_context19) { | ||
while (1) { | ||
switch (_context19.prev = _context19.next) { | ||
case 0: | ||
_context19.next = 2; | ||
return createAndVerify({ own: { name: 'Bob' } }); | ||
case 2: | ||
response = _context19.sent; | ||
return _context19.abrupt('return', expect(response).toMatchSnapshot()); | ||
case 4: | ||
case 'end': | ||
return _context19.stop(); | ||
} | ||
} | ||
}, _callee19, undefined); | ||
}))); | ||
it('creates a valid JWT for a disclosure', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee20() { | ||
var req, response; | ||
return _regenerator2.default.wrap(function _callee20$(_context20) { | ||
while (1) { | ||
switch (_context20.prev = _context20.next) { | ||
case 0: | ||
_context20.next = 2; | ||
return uport.requestDisclosure(); | ||
case 2: | ||
req = _context20.sent; | ||
_context20.next = 5; | ||
return createAndVerify({ req: req }); | ||
case 5: | ||
response = _context20.sent; | ||
return _context20.abrupt('return', expect(response).toMatchSnapshot()); | ||
case 7: | ||
case 'end': | ||
return _context20.stop(); | ||
} | ||
} | ||
}, _callee20, undefined); | ||
}))); | ||
}); | ||
describe('createVerificationRequest', function () { | ||
it('creates a valid JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee21() { | ||
it('creates a valid JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee17() { | ||
var jwt; | ||
return _regenerator2.default.wrap(function _callee21$(_context21) { | ||
return _regenerator2.default.wrap(function _callee17$(_context17) { | ||
while (1) { | ||
switch (_context21.prev = _context21.next) { | ||
switch (_context17.prev = _context17.next) { | ||
case 0: | ||
_context21.next = 2; | ||
_context17.next = 2; | ||
return uport.createVerificationRequest({ 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 }); | ||
jwt = _context17.sent; | ||
_context17.t0 = expect; | ||
_context17.next = 6; | ||
return (0, _JWT.verifyJWT)({ audience: did }, jwt); | ||
case 6: | ||
_context21.t1 = _context21.sent; | ||
return _context21.abrupt('return', (0, _context21.t0)(_context21.t1).toMatchSnapshot()); | ||
_context17.t1 = _context17.sent; | ||
return _context17.abrupt('return', (0, _context17.t0)(_context17.t1).toMatchSnapshot()); | ||
case 8: | ||
case 'end': | ||
return _context21.stop(); | ||
return _context17.stop(); | ||
} | ||
} | ||
}, _callee21, undefined); | ||
}, _callee17, undefined); | ||
}))); | ||
@@ -693,3 +574,3 @@ }); | ||
return uport.attest({ sub: 'did:uport:223ab45', claim: { email: 'bingbangbung@email.com' }, exp: 1485321133 + 1 }).then(function (jwt) { | ||
return expect((0, _didJwt.verifyJWT)(jwt)).toMatchSnapshot(); | ||
return expect((0, _JWT.verifyJWT)({ audience: did }, jwt)).toMatchSnapshot(); | ||
}); | ||
@@ -701,26 +582,26 @@ }); | ||
var createShareResp = function () { | ||
var _ref22 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee22() { | ||
var _ref18 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee18() { | ||
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var req; | ||
return _regenerator2.default.wrap(function _callee22$(_context22) { | ||
return _regenerator2.default.wrap(function _callee18$(_context18) { | ||
while (1) { | ||
switch (_context22.prev = _context22.next) { | ||
switch (_context18.prev = _context18.next) { | ||
case 0: | ||
_context22.next = 2; | ||
return uport.requestDisclosure({ requested: ['name', 'phone'] }); | ||
_context18.next = 2; | ||
return uport.createRequest({ requested: ['name', 'phone'] }); | ||
case 2: | ||
req = _context22.sent; | ||
return _context22.abrupt('return', uport.disclose((0, _extends3.default)({}, payload, { req: req }))); | ||
req = _context18.sent; | ||
return _context18.abrupt('return', (0, _JWT.createJWT)({ address: mnid, signer: signer }, (0, _extends3.default)({}, payload, { req: req }))); | ||
case 4: | ||
case 'end': | ||
return _context22.stop(); | ||
return _context18.stop(); | ||
} | ||
} | ||
}, _callee22, this); | ||
}, _callee18, this); | ||
})); | ||
return function createShareResp() { | ||
return _ref22.apply(this, arguments); | ||
return _ref18.apply(this, arguments); | ||
}; | ||
@@ -730,31 +611,31 @@ }(); | ||
var createShareRespWithVerifiedCredential = function () { | ||
var _ref23 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee23() { | ||
var _ref19 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee19() { | ||
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var req, attestation; | ||
return _regenerator2.default.wrap(function _callee23$(_context23) { | ||
return _regenerator2.default.wrap(function _callee19$(_context19) { | ||
while (1) { | ||
switch (_context23.prev = _context23.next) { | ||
switch (_context19.prev = _context19.next) { | ||
case 0: | ||
_context23.next = 2; | ||
return uport.requestDisclosure({ requested: ['name', 'phone'] }); | ||
_context19.next = 2; | ||
return uport.createRequest({ requested: ['name', 'phone'] }); | ||
case 2: | ||
req = _context23.sent; | ||
_context23.next = 5; | ||
req = _context19.sent; | ||
_context19.next = 5; | ||
return uport.attest(claim); | ||
case 5: | ||
attestation = _context23.sent; | ||
return _context23.abrupt('return', uport.disclose((0, _extends3.default)({}, payload, { verified: [attestation], req: req }))); | ||
attestation = _context19.sent; | ||
return _context19.abrupt('return', (0, _JWT.createJWT)({ address: mnid, signer: signer }, (0, _extends3.default)({}, payload, { verified: [attestation], req: req }))); | ||
case 7: | ||
case 'end': | ||
return _context23.stop(); | ||
return _context19.stop(); | ||
} | ||
} | ||
}, _callee23, this); | ||
}, _callee19, this); | ||
})); | ||
return function createShareRespWithVerifiedCredential() { | ||
return _ref23.apply(this, arguments); | ||
return _ref19.apply(this, arguments); | ||
}; | ||
@@ -770,18 +651,18 @@ }(); | ||
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee24() { | ||
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee20() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee24$(_context24) { | ||
return _regenerator2.default.wrap(function _callee20$(_context20) { | ||
while (1) { | ||
switch (_context24.prev = _context24.next) { | ||
switch (_context20.prev = _context20.next) { | ||
case 0: | ||
_context24.next = 2; | ||
_context20.next = 2; | ||
return createShareResp({ own: { name: 'Davie', phone: '+15555551234' } }); | ||
case 2: | ||
jwt = _context24.sent; | ||
_context24.next = 5; | ||
jwt = _context20.sent; | ||
_context20.next = 5; | ||
return uport.authenticate(jwt); | ||
case 5: | ||
profile = _context24.sent; | ||
profile = _context20.sent; | ||
@@ -792,24 +673,24 @@ expect(profile).toMatchSnapshot(); | ||
case 'end': | ||
return _context24.stop(); | ||
return _context20.stop(); | ||
} | ||
} | ||
}, _callee24, undefined); | ||
}, _callee20, undefined); | ||
}))); | ||
it('returns profile mixing public and private claims and verified credentials', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee25() { | ||
it('returns profile mixing public and private claims and verified credentials', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee21() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee25$(_context25) { | ||
return _regenerator2.default.wrap(function _callee21$(_context21) { | ||
while (1) { | ||
switch (_context25.prev = _context25.next) { | ||
switch (_context21.prev = _context21.next) { | ||
case 0: | ||
_context25.next = 2; | ||
_context21.next = 2; | ||
return createShareRespWithVerifiedCredential({ own: { name: 'Davie', phone: '+15555551234' } }); | ||
case 2: | ||
jwt = _context25.sent; | ||
_context25.next = 5; | ||
jwt = _context21.sent; | ||
_context21.next = 5; | ||
return uport.authenticate(jwt); | ||
case 5: | ||
profile = _context25.sent; | ||
profile = _context21.sent; | ||
@@ -820,24 +701,24 @@ expect(profile).toMatchSnapshot(); | ||
case 'end': | ||
return _context25.stop(); | ||
return _context21.stop(); | ||
} | ||
} | ||
}, _callee25, undefined); | ||
}, _callee21, undefined); | ||
}))); | ||
it('returns profile with only public claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee26() { | ||
it('returns profile with only public claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee22() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee26$(_context26) { | ||
return _regenerator2.default.wrap(function _callee22$(_context22) { | ||
while (1) { | ||
switch (_context26.prev = _context26.next) { | ||
switch (_context22.prev = _context22.next) { | ||
case 0: | ||
_context26.next = 2; | ||
_context22.next = 2; | ||
return createShareResp(); | ||
case 2: | ||
jwt = _context26.sent; | ||
_context26.next = 5; | ||
jwt = _context22.sent; | ||
_context22.next = 5; | ||
return uport.authenticate(jwt); | ||
case 5: | ||
profile = _context26.sent; | ||
profile = _context22.sent; | ||
@@ -848,24 +729,24 @@ expect(profile).toMatchSnapshot(); | ||
case 'end': | ||
return _context26.stop(); | ||
return _context22.stop(); | ||
} | ||
} | ||
}, _callee26, undefined); | ||
}, _callee22, undefined); | ||
}))); | ||
it('returns profile with private chain network id claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee27() { | ||
it('returns profile with private chain network id claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee23() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee27$(_context27) { | ||
return _regenerator2.default.wrap(function _callee23$(_context23) { | ||
while (1) { | ||
switch (_context27.prev = _context27.next) { | ||
switch (_context23.prev = _context23.next) { | ||
case 0: | ||
_context27.next = 2; | ||
_context23.next = 2; | ||
return createShareResp({ nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m' }); | ||
case 2: | ||
jwt = _context27.sent; | ||
_context27.next = 5; | ||
jwt = _context23.sent; | ||
_context23.next = 5; | ||
return uport.authenticate(jwt); | ||
case 5: | ||
profile = _context27.sent; | ||
profile = _context23.sent; | ||
@@ -876,24 +757,24 @@ expect(profile).toMatchSnapshot(); | ||
case 'end': | ||
return _context27.stop(); | ||
return _context23.stop(); | ||
} | ||
} | ||
}, _callee27, undefined); | ||
}, _callee23, undefined); | ||
}))); | ||
it('returns profile with device key claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee28() { | ||
it('returns profile with device key claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee24() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee28$(_context28) { | ||
return _regenerator2.default.wrap(function _callee24$(_context24) { | ||
while (1) { | ||
switch (_context28.prev = _context28.next) { | ||
switch (_context24.prev = _context24.next) { | ||
case 0: | ||
_context28.next = 2; | ||
_context24.next = 2; | ||
return createShareResp({ dad: '0xdeviceKey' }); | ||
case 2: | ||
jwt = _context28.sent; | ||
_context28.next = 5; | ||
jwt = _context24.sent; | ||
_context24.next = 5; | ||
return uport.authenticate(jwt); | ||
case 5: | ||
profile = _context28.sent; | ||
profile = _context24.sent; | ||
@@ -904,24 +785,24 @@ expect(profile).toMatchSnapshot(); | ||
case 'end': | ||
return _context28.stop(); | ||
return _context24.stop(); | ||
} | ||
} | ||
}, _callee28, undefined); | ||
}, _callee24, undefined); | ||
}))); | ||
it('returns pushToken if available', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee29() { | ||
it('returns pushToken if available', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee25() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee29$(_context29) { | ||
return _regenerator2.default.wrap(function _callee25$(_context25) { | ||
while (1) { | ||
switch (_context29.prev = _context29.next) { | ||
switch (_context25.prev = _context25.next) { | ||
case 0: | ||
_context29.next = 2; | ||
_context25.next = 2; | ||
return createShareResp({ capabilities: ['PUSHTOKEN'] }); | ||
case 2: | ||
jwt = _context29.sent; | ||
_context29.next = 5; | ||
jwt = _context25.sent; | ||
_context25.next = 5; | ||
return uport.authenticate(jwt); | ||
case 5: | ||
profile = _context29.sent; | ||
profile = _context25.sent; | ||
@@ -932,19 +813,19 @@ expect(profile).toMatchSnapshot(); | ||
case 'end': | ||
return _context29.stop(); | ||
return _context25.stop(); | ||
} | ||
} | ||
}, _callee29, undefined); | ||
}, _callee25, undefined); | ||
}))); | ||
it('handles response with missing challenge', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee30() { | ||
it('handles response with missing challenge', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee26() { | ||
var jwt; | ||
return _regenerator2.default.wrap(function _callee30$(_context30) { | ||
return _regenerator2.default.wrap(function _callee26$(_context26) { | ||
while (1) { | ||
switch (_context30.prev = _context30.next) { | ||
switch (_context26.prev = _context26.next) { | ||
case 0: | ||
_context30.next = 2; | ||
return uport.disclose({ own: { name: 'bob' } }); | ||
_context26.next = 2; | ||
return (0, _JWT.createJWT)({ address: mnid, signer: signer }, { own: { name: 'bob' } }); | ||
case 2: | ||
jwt = _context30.sent; | ||
jwt = _context26.sent; | ||
@@ -955,10 +836,10 @@ expect(uport.authenticate(jwt)).rejects.toMatchSnapshot(); | ||
case 'end': | ||
return _context30.stop(); | ||
return _context26.stop(); | ||
} | ||
} | ||
}, _callee30, undefined); | ||
}, _callee26, undefined); | ||
}))); | ||
}); | ||
describe('verifyProfile()', function () { | ||
describe('LEGACY receive()', function () { | ||
beforeAll(function () { | ||
@@ -970,50 +851,23 @@ return mockresolver({ | ||
}); | ||
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee31() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee31$(_context31) { | ||
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee27() { | ||
var req, jwt, profile; | ||
return _regenerator2.default.wrap(function _callee27$(_context27) { | ||
while (1) { | ||
switch (_context31.prev = _context31.next) { | ||
switch (_context27.prev = _context27.next) { | ||
case 0: | ||
_context31.next = 2; | ||
return uport.disclose({ own: { name: 'Davie', phone: '+15555551234' } }); | ||
_context27.next = 2; | ||
return uport.createRequest({ requested: ['name', 'phone'] }); | ||
case 2: | ||
jwt = _context31.sent; | ||
_context31.next = 5; | ||
return uport.verifyProfile(jwt); | ||
req = _context27.sent; | ||
_context27.next = 5; | ||
return (0, _JWT.createJWT)({ address: mnid, signer: signer }, { own: { name: 'Davie', phone: '+15555551234' }, req: req }); | ||
case 5: | ||
profile = _context31.sent; | ||
jwt = _context27.sent; | ||
_context27.next = 8; | ||
return uport.receive(jwt); | ||
expect(profile).toMatchSnapshot(); | ||
case 7: | ||
case 'end': | ||
return _context31.stop(); | ||
} | ||
} | ||
}, _callee31, undefined); | ||
}))); | ||
it('returns profile mixing public and private claims and verified credentials', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee32() { | ||
var attestation, jwt, profile; | ||
return _regenerator2.default.wrap(function _callee32$(_context32) { | ||
while (1) { | ||
switch (_context32.prev = _context32.next) { | ||
case 0: | ||
_context32.next = 2; | ||
return uport.attest(claim); | ||
case 2: | ||
attestation = _context32.sent; | ||
_context32.next = 5; | ||
return uport.disclose({ own: { name: 'Davie', phone: '+15555551234' }, verified: [attestation] }); | ||
case 5: | ||
jwt = _context32.sent; | ||
_context32.next = 8; | ||
return uport.verifyProfile(jwt); | ||
case 8: | ||
profile = _context32.sent; | ||
profile = _context27.sent; | ||
@@ -1024,249 +878,254 @@ expect(profile).toMatchSnapshot(); | ||
case 'end': | ||
return _context32.stop(); | ||
return _context27.stop(); | ||
} | ||
} | ||
}, _callee32, undefined); | ||
}, _callee27, undefined); | ||
}))); | ||
}); | ||
it('returns profile with only public claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee33() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee33$(_context33) { | ||
while (1) { | ||
switch (_context33.prev = _context33.next) { | ||
case 0: | ||
_context33.next = 2; | ||
return uport.disclose(); | ||
describe('receive', function () { | ||
case 2: | ||
jwt = _context33.sent; | ||
_context33.next = 5; | ||
return uport.verifyProfile(jwt); | ||
function createShareResp() { | ||
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
case 5: | ||
profile = _context33.sent; | ||
return uport.createRequest({ requested: ['name', 'phone'] }).then(function (jwt) { | ||
return (0, _JWT.createJWT)({ address: mnid, signer: signer }, (0, _extends3.default)({}, payload, { type: 'shareResp', req: jwt })); | ||
}); | ||
} | ||
expect(profile).toMatchSnapshot(); | ||
function createShareRespMissingRequest() { | ||
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
case 7: | ||
case 'end': | ||
return _context33.stop(); | ||
} | ||
} | ||
}, _callee33, undefined); | ||
}))); | ||
return uport.createRequest({ requested: ['name', 'phone'] }).then(function (jwt) { | ||
return (0, _JWT.createJWT)({ address: mnid, signer: signer }, (0, _extends3.default)({}, payload, { type: 'shareResp' })); | ||
}); | ||
} | ||
it('returns profile with private chain network id claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee34() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee34$(_context34) { | ||
while (1) { | ||
switch (_context34.prev = _context34.next) { | ||
case 0: | ||
_context34.next = 2; | ||
return uport.disclose({ nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m' }); | ||
function createShareRespWithExpiredRequest() { | ||
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
case 2: | ||
jwt = _context34.sent; | ||
_context34.next = 5; | ||
return uport.verifyProfile(jwt); | ||
return uport.createRequest({ requested: ['name', 'phone'], exp: Date.now() - 1 }).then(function (jwt) { | ||
return (0, _JWT.createJWT)({ address: mnid, signer: signer }, (0, _extends3.default)({}, payload, { type: 'shareResp', req: jwt })); | ||
}); | ||
} | ||
case 5: | ||
profile = _context34.sent; | ||
function createShareRespWithVerifiedCredential() { | ||
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var verifiedClaim = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { sub: '0x112233', claim: { email: 'bingbangbung@email.com' }, exp: 1485321133 + 1 }; | ||
expect(profile).toMatchSnapshot(); | ||
return uport.attest(verifiedClaim).then(function (jwt) { | ||
return createShareResp((0, _extends3.default)({}, payload, { verified: [jwt] })); | ||
}); | ||
} | ||
case 7: | ||
case 'end': | ||
return _context34.stop(); | ||
} | ||
} | ||
}, _callee34, undefined); | ||
}))); | ||
it('returns profile mixing public and private claims', function () { | ||
return createShareResp({ own: { name: 'Davie', phone: '+15555551234' } }).then(function (jwt) { | ||
return uport.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile).toMatchSnapshot(); | ||
}); | ||
}); | ||
it('returns pushToken if available', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee35() { | ||
var jwt, profile; | ||
return _regenerator2.default.wrap(function _callee35$(_context35) { | ||
while (1) { | ||
switch (_context35.prev = _context35.next) { | ||
case 0: | ||
_context35.next = 2; | ||
return uport.disclose({ capabilities: ['PUSHTOKEN'] }); | ||
it('returns profile mixing public and private claims and verified credentials', function () { | ||
return createShareRespWithVerifiedCredential({ own: { name: 'Davie', phone: '+15555551234' } }).then(function (jwt) { | ||
return uport.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile).toMatchSnapshot(); | ||
}); | ||
}); | ||
case 2: | ||
jwt = _context35.sent; | ||
_context35.next = 5; | ||
return uport.verifyProfile(jwt); | ||
it('returns profile with only public claims', function () { | ||
return createShareResp().then(function (jwt) { | ||
return uport.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile).toMatchSnapshot(); | ||
}); | ||
}); | ||
case 5: | ||
profile = _context35.sent; | ||
it('returns profile with private chain network id claims', function () { | ||
return createShareResp({ nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m' }).then(function (jwt) { | ||
return uport.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile).toMatchSnapshot(); | ||
}); | ||
}); | ||
expect(profile).toMatchSnapshot(); | ||
it('returns profile with device key claims', function () { | ||
return createShareResp({ dad: '0xdeviceKey' }).then(function (jwt) { | ||
return uport.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile).toMatchSnapshot(); | ||
}); | ||
}); | ||
case 7: | ||
case 'end': | ||
return _context35.stop(); | ||
} | ||
} | ||
}, _callee35, undefined); | ||
}))); | ||
}); | ||
it('returns pushToken if available', function () { | ||
return createShareResp({ capabilities: ['PUSHTOKEN'] }).then(function (jwt) { | ||
return uport.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile.pushToken).toEqual('PUSHTOKEN'); | ||
}); | ||
}); | ||
describe('LEGACY receive()', function () { | ||
beforeAll(function () { | ||
return mockresolver({ | ||
name: 'Bob Smith', | ||
country: 'NI' | ||
it('handles response to expired request', function () { | ||
return createShareRespWithExpiredRequest().then(function (jwt) { | ||
return uport.receive(jwt); | ||
}).catch(function (error) { | ||
return expect(error.message).toEqual('JWT has expired: exp: 1485321132999 < now: 1485321133'); | ||
}); | ||
}); | ||
it('returns profile mixing public and private claims', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee36() { | ||
var req, jwt, profile; | ||
return _regenerator2.default.wrap(function _callee36$(_context36) { | ||
while (1) { | ||
switch (_context36.prev = _context36.next) { | ||
case 0: | ||
_context36.next = 2; | ||
return uport.requestDisclosure({ requested: ['name', 'phone'] }); | ||
case 2: | ||
req = _context36.sent; | ||
_context36.next = 5; | ||
return uport.disclose({ own: { name: 'Davie', phone: '+15555551234' }, req: req }); | ||
it('handles response with missing challenge', function () { | ||
return createShareRespMissingRequest().then(function (jwt) { | ||
return uport.receive(jwt); | ||
}).catch(function (error) { | ||
return expect(error.message).toEqual('Challenge was not included in response'); | ||
}); | ||
}); | ||
case 5: | ||
jwt = _context36.sent; | ||
_context36.next = 8; | ||
return uport.receive(jwt); | ||
/////////////////////////////// no address in uport settings /////////////////////////////// | ||
case 8: | ||
profile = _context36.sent; | ||
it('returns profile mixing public and private claims', function () { | ||
return createShareResp({ own: { name: 'Davie', phone: '+15555551234' } }).then(function (jwt) { | ||
return uport2.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile).toMatchSnapshot(); | ||
}); | ||
}); | ||
expect(profile).toMatchSnapshot(); | ||
it('returns profile mixing public and private claims and verified credentials', function () { | ||
return createShareRespWithVerifiedCredential({ own: { name: 'Davie', phone: '+15555551234' } }).then(function (jwt) { | ||
return uport2.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile).toMatchSnapshot(); | ||
}); | ||
}); | ||
case 10: | ||
case 'end': | ||
return _context36.stop(); | ||
} | ||
} | ||
}, _callee36, undefined); | ||
}))); | ||
}); | ||
it('returns profile with only public claims', function () { | ||
return createShareResp().then(function (jwt) { | ||
return uport2.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile).toMatchSnapshot(); | ||
}); | ||
}); | ||
describe('txRequest()', function () { | ||
beforeAll(function () { | ||
return mockresolver(); | ||
it('returns profile with private chain network id claims', function () { | ||
return createShareResp({ nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m' }).then(function (jwt) { | ||
return uport2.receive(jwt); | ||
}).then(function (profile) { | ||
return expect(profile).toMatchSnapshot(); | ||
}); | ||
}); | ||
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('returns pushToken if available', function () { | ||
return createShareResp({ capabilities: ['PUSHTOKEN'] }).then(function (jwt) { | ||
return uport.receive(jwt); | ||
}).then(function (profile) { | ||
expect(profile.pushToken).toEqual('PUSHTOKEN'); | ||
}); | ||
}); | ||
}); | ||
it('creates a valid JWT for a request', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee37() { | ||
var jwt, verified; | ||
return _regenerator2.default.wrap(function _callee37$(_context37) { | ||
while (1) { | ||
switch (_context37.prev = _context37.next) { | ||
case 0: | ||
_context37.next = 2; | ||
return statusContract.updateStatus('hello'); | ||
describe('push', function () { | ||
var PUTUTU_URL = 'https://api.uport.me'; //'https://pututu.uport.space' // TODO - change to .me | ||
var API_v1_PATH = '/api/v1/sns'; | ||
var API_v2_PATH = '/pututu/sns'; | ||
var lambda = '/pututu/sns'; | ||
var PUSHTOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NkstUiJ9.eyJpYXQiOjE1MzExOTcwMzgsImV4cCI6MTUzMjQ5MzAzOCwiYXVkIjoiMzVERFh3RjZIZHI2ZFFRbzFCUndRcnU3VzNkNTRhdnpCd2siLCJ0eXBlIjoibm90aWZpY2F0aW9ucyIsInZhbHVlIjoiYXJuOmF3czpzbnM6dXMtd2VzdC0yOjExMzE5NjIxNjU1ODplbmRwb2ludC9BUE5TL3VQb3J0LzVmMTA4YjZlLTk3NTItM2IwZC05NWM2LWYyZTU3MTM4ZWNlNSIsImlzcyI6ImRpZDpldGhyOjB4YjA2ZDJjZWY5ZDJjYTA3MjU2NmU3Y2RlZDMyYWI0OWY1OTFlNDRlOCJ9.0qZE3N2m7rTn8JaNVfp5LhICmzEWCqTBBh9_gn4ZGD19PCfhInX7XTav0JBRBtSKkJXx03nik9k4jZ3qvQ6CigE'; | ||
var token = PUSHTOKEN; | ||
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; | ||
case 2: | ||
jwt = _context37.sent; | ||
_context37.next = 5; | ||
return (0, _didJwt.verifyJWT)(jwt); | ||
beforeEach(function () { | ||
_nock2.default.disableNetConnect(); | ||
}); | ||
case 5: | ||
verified = _context37.sent; | ||
afterEach(function () { | ||
_nock2.default.enableNetConnect(); | ||
}); | ||
expect(verified.payload).toMatchSnapshot(); | ||
case 7: | ||
case 'end': | ||
return _context37.stop(); | ||
} | ||
it('pushes url to pututu', function () { | ||
(0, _nock2.default)(PUTUTU_URL, { | ||
reqheaders: { | ||
'authorization': 'Bearer ' + PUSHTOKEN | ||
} | ||
}, _callee37, undefined); | ||
}))); | ||
}).post(lambda, 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)); | ||
it('encodes readable function calls including given args in function key of jwt', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee38() { | ||
var jwt, verified; | ||
return _regenerator2.default.wrap(function _callee38$(_context38) { | ||
while (1) { | ||
switch (_context38.prev = _context38.next) { | ||
case 0: | ||
_context38.next = 2; | ||
return statusContract.updateStatus('hello'); | ||
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(function (response) { | ||
return expect(response).toEqual({ status: 'success', message: 'd0b2bd07-d49e-5ba1-9b05-ec23ac921930' }); | ||
}); | ||
}); | ||
case 2: | ||
jwt = _context38.sent; | ||
_context38.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 = _context38.sent; | ||
expect(verified.payload.fn).toEqual("updateStatus(string 'hello')"); | ||
case 7: | ||
case 'end': | ||
return _context38.stop(); | ||
} | ||
it('handles missing pubEncKey', function () { | ||
(0, _nock2.default)('https://pututu.uport.space', { | ||
reqheaders: { | ||
'authorization': 'Bearer ' + PUSHTOKEN | ||
} | ||
}, _callee38, undefined); | ||
}))); | ||
}).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' }); | ||
it('adds to key as contract address to jwt', (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'); | ||
console.error = jest.fn(function (msg) { | ||
expect(msg).toEqual('WARNING: Calling push without a public encryption key is deprecated'); | ||
}); | ||
return uport.push(PUSHTOKEN, null, payload).catch(function (error) { | ||
return expect(error.message).toEqual('Missing public encryption key of the receiver'); | ||
}); | ||
}); | ||
case 2: | ||
jwt = _context39.sent; | ||
_context39.next = 5; | ||
return (0, _didJwt.verifyJWT)(jwt); | ||
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 5: | ||
verified = _context39.sent; | ||
expect(verified.payload.to).toEqual(address); | ||
case 7: | ||
case 'end': | ||
return _context39.stop(); | ||
} | ||
it('handles invalid token', function () { | ||
(0, _nock2.default)(PUTUTU_URL, { | ||
reqheaders: { | ||
'authorization': 'Bearer ' + PUSHTOKEN | ||
} | ||
}, _callee39, undefined); | ||
}))); | ||
}).post(lambda, 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)); | ||
it('adds additional request options passed to jwt', (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee40() { | ||
var network_id, callbackUrl, jwt, verified; | ||
return _regenerator2.default.wrap(function _callee40$(_context40) { | ||
while (1) { | ||
switch (_context40.prev = _context40.next) { | ||
case 0: | ||
network_id = '0x4'; | ||
callbackUrl = 'mydomain'; | ||
_context40.next = 4; | ||
return statusContract.updateStatus('hello', { network_id: network_id, callbackUrl: callbackUrl }); | ||
return result.url === payload.url && result.message === payload.message; | ||
}).reply(403, 'Not allowed'); | ||
case 4: | ||
jwt = _context40.sent; | ||
_context40.next = 7; | ||
return (0, _didJwt.verifyJWT)(jwt); | ||
return uport.push(PUSHTOKEN, pubEncKey, payload).catch(function (error) { | ||
return expect(error.message).toEqual('Error sending push notification to user: Invalid Token'); | ||
}); | ||
}); | ||
case 7: | ||
verified = _context40.sent; | ||
it('handles random error', function () { | ||
(0, _nock2.default)(PUTUTU_URL, { | ||
reqheaders: { | ||
'authorization': 'Bearer ' + PUSHTOKEN | ||
} | ||
}).post(API_v2_PATH, function () { | ||
return true; | ||
}).reply(500, 'Server Error'); | ||
expect(verified.payload.net).toEqual(network_id); | ||
expect(verified.payload.callback).toEqual(callbackUrl); | ||
case 10: | ||
case 'end': | ||
return _context40.stop(); | ||
} | ||
} | ||
}, _callee40, undefined); | ||
}))); | ||
return uport.push(PUSHTOKEN, pubEncKey, payload).catch(function (error) { | ||
return expect(error.message).toEqual('Error sending push notification to user: 500 Server Error'); | ||
}); | ||
}); | ||
}); |
@@ -24,2 +24,4 @@ 'use strict'; | ||
var _mnid = require('mnid'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -123,3 +125,19 @@ | ||
var buildRequestURI = function buildRequestURI(txObject) { | ||
return 'me.uport:' + txObject.to + '?function=' + txObject.function; | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
callbackUrl = _ref.callbackUrl, | ||
type = _ref.type; | ||
if (!(0, _mnid.isMNID)(txObject.to)) throw new Error('To address must be MNID'); | ||
var uri = 'https://id.uport.me/' + txObject.to; | ||
var pairs = []; | ||
if (txObject.value) pairs.push(['value', parseInt(txObject.value, 16)]); | ||
if (txObject.function) pairs.push(['function', txObject.function]); | ||
if (callbackUrl) pairs.push(['callback_url', callbackUrl]); | ||
if (txObject.gasPrice) pairs.push(['gasPrice', txObject.gasPrice]); | ||
if (type) pairs.push(['type', type]); | ||
return uri + '?' + pairs.map(function (kv) { | ||
return kv[0] + '=' + encodeURIComponent(kv[1]); | ||
}).join('&'); | ||
}; | ||
@@ -126,0 +144,0 @@ |
@@ -15,2 +15,6 @@ 'use strict'; | ||
var _stringify = require('babel-runtime/core-js/json/stringify'); | ||
var _stringify2 = _interopRequireDefault(_stringify); | ||
var _regenerator = require('babel-runtime/regenerator'); | ||
@@ -42,5 +46,5 @@ | ||
var _uportLite = require('uport-lite'); | ||
var _JWT = require('./JWT'); | ||
var _uportLite2 = _interopRequireDefault(_uportLite); | ||
var _elliptic = require('elliptic'); | ||
@@ -59,10 +63,18 @@ var _uportDidResolver = require('uport-did-resolver'); | ||
var _Digest = require('did-jwt/lib/Digest'); | ||
var _uportLite = require('uport-lite'); | ||
var _elliptic = require('elliptic'); | ||
var _uportLite2 = _interopRequireDefault(_uportLite); | ||
var _Contract = require('./Contract.js'); | ||
var _nets = require('nets'); | ||
var _uportCore = require('uport-core'); | ||
var _nets2 = _interopRequireDefault(_nets); | ||
var _tweetnacl = require('tweetnacl'); | ||
var _tweetnacl2 = _interopRequireDefault(_tweetnacl); | ||
var _tweetnaclUtil = require('tweetnacl-util'); | ||
var _tweetnaclUtil2 = _interopRequireDefault(_tweetnaclUtil); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -73,2 +85,3 @@ | ||
var secp256k1 = new _elliptic.ec('secp256k1'); | ||
/** | ||
@@ -80,3 +93,2 @@ * The Credentials class allows you to easily create the signed payloads used in uPort inlcuding | ||
*/ | ||
var Credentials = function () { | ||
@@ -87,55 +99,17 @@ | ||
* | ||
* The following example is just for testing purposes. You should never store a private key in source code. | ||
* | ||
* @example | ||
* import { Credentials } from 'uport' | ||
* const credentials = new Credentials({ | ||
* privateKey: '74894f8853f90e6e3d6dfdd343eb0eb70cca06e552ed8af80adadcc573b35da3' | ||
* }) | ||
* import { Credentials, SimpleSigner } from 'uport' | ||
* const networks = { '0x94365e3b': { rpcUrl: 'https://private.chain/rpc', registry: '0x0101.... }} | ||
* const setttings = { networks, address: '5A8bRWU3F7j3REx3vkJ...', signer: new SimpleSigner(process.env.PRIVATE_KEY)} | ||
* const credentials = new Credentials(settings) | ||
* | ||
* 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' | ||
* const credentials = new Credentials({ | ||
* did: 'did:ethr:0xbc3ae59bc76f894822622cdef7a2018dbe353840', | ||
* privateKey: '74894f8853f90e6e3d6dfdd343eb0eb70cca06e552ed8af80adadcc573b35da3' | ||
* }) | ||
* const credentials = new Credentials() | ||
* | ||
* It is recommended to store the address and private key in environment variables for your server application | ||
* | ||
* @example | ||
* import { Credentials, SimpleSigner } from 'uport' | ||
* const credentials = new Credentials({ | ||
* did: process.env.APPLICATION_DID, | ||
* signer: SimpleSigner(process.env.PRIVATE_KEY) | ||
* }) | ||
* | ||
* 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' | ||
* | ||
* function mySigner (data) { | ||
* return new Promise((resolve, reject) => { | ||
* const signature = /// sign it | ||
* resolve(signature) | ||
* }) | ||
* } | ||
* | ||
* const credentials = new Credentials({ | ||
* did: process.env.APPLICATION_DID, | ||
* signer: mySigner | ||
* }) | ||
* | ||
* @param {Object} [settings] setttings | ||
* @param {DID} settings.did Application [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) (unique identifier) for your application | ||
* @param {String} settings.privateKey A hex encoded 32 byte private key | ||
* @param {SimpleSigner} settings.signer a signer object, see [Signer Functions](https://github.com/uport-project/did-jwt#signer-functions) | ||
* @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) | ||
* @param {Object} settings.networks DEPRECATED networks config object, ie. { '0x94365e3b': { rpcUrl: 'https://private.chain/rpc', address: '0x0101.... }} | ||
* @param {UportLite} settings.registry DEPRECATED a registry object from UportLite | ||
* @param {Object} settings.networks networks config object, ie. { '0x94365e3b': { rpcUrl: 'https://private.chain/rpc', address: '0x0101.... }} | ||
* @param {UportLite} settings.registry a registry object from UportLite | ||
* @param {SimpleSigner} settings.signer a signer object, see SimpleSigner.js | ||
* @param {Address} settings.address your uPort address (may be the address of your application's uPort identity) | ||
* @return {Credentials} self | ||
@@ -147,8 +121,6 @@ */ | ||
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
did = _ref.did, | ||
address = _ref.address, | ||
privateKey = _ref.privateKey, | ||
signer = _ref.signer, | ||
networks = _ref.networks, | ||
registry = _ref.registry, | ||
signer = _ref.signer, | ||
address = _ref.address, | ||
ethrConfig = _ref.ethrConfig, | ||
@@ -159,31 +131,35 @@ muportConfig = _ref.muportConfig; | ||
this.settings = {}; | ||
if (signer) { | ||
this.signer = signer; | ||
} else if (privateKey) { | ||
this.signer = (0, _didJwt.SimpleSigner)(privateKey); | ||
this.settings.signer = signer; | ||
// this.signer = signer | ||
} | ||
this.givenDID = false; | ||
if (did) { | ||
this.did = did; | ||
this.givenDID = true; | ||
} else if (address) { | ||
this.address = address; | ||
if (address) { | ||
if (MNID.isMNID(address)) { | ||
this.did = 'did:uport:' + address; | ||
this.settings.address = address; | ||
// this.address = address | ||
this.settings.did = 'did:uport:' + address; | ||
} else { | ||
throw new Error('Only MNID app identities accepted'); | ||
} | ||
if (address.match('^0x[0-9a-fA-F]{40}$')) { | ||
this.did = 'did:ethr:' + address; | ||
} | ||
} else if (privateKey) { | ||
var kp = secp256k1.keyFromPrivate(privateKey); | ||
var _address = (0, _Digest.toEthereumAddress)(kp.getPublic('hex')); | ||
this.did = 'did:ethr:' + _address; | ||
} | ||
this.signJWT = function (payload, expiresIn) { | ||
return (0, _didJwt.createJWT)(payload, { issuer: _this.givenDID ? _this.did : _this.address, signer: _this.signer, alg: _this.did.match('^did:uport:') ? 'ES256K' : 'ES256K-R', expiresIn: expiresIn }); | ||
return (0, _JWT.createJWT)({ issuer: _this.settings.address, signer: _this.settings.signer, expiresIn: expiresIn }, payload); | ||
}; | ||
// backwards compatibility | ||
this.settings.networks = networks ? configNetworks(networks) : {}; | ||
if (!this.settings.registry) { | ||
var _registry = (0, _uportLite2.default)({ networks: this.settings.networks }); | ||
this.settings.registry = function (address) { | ||
return new _promise2.default(function (resolve, reject) { | ||
_registry(address, function (error, profile) { | ||
if (error) return reject(error); | ||
resolve(profile); | ||
}); | ||
}); | ||
}; | ||
} | ||
(0, _uportDidResolver2.default)(registry || (0, _uportLite2.default)({ networks: networks ? configNetworks(networks) : {} })); | ||
@@ -195,3 +171,24 @@ (0, _ethrDidResolver2.default)(ethrConfig || {}); | ||
/** | ||
* generate a DID and private key | ||
* Creates a signed request token (JWT) given a request params object. | ||
* | ||
* @example | ||
* const req = { requested: ['name', 'country'], | ||
* callbackUrl: 'https://myserver.com', | ||
* notifications: true } | ||
* credentials.createRequest(req).then(jwt => { | ||
* ... | ||
* }) | ||
requested: ['name','phone','identity_no'], | ||
callbackUrl: 'https://....' // URL to send the response of the request to | ||
notifications: true | ||
* | ||
* @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 | ||
*/ | ||
@@ -201,27 +198,4 @@ | ||
(0, _createClass3.default)(Credentials, [{ | ||
key: 'requestDisclosure', | ||
/** | ||
* 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 | ||
*/ | ||
value: function requestDisclosure() { | ||
key: 'createRequest', | ||
value: function createRequest() { | ||
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -253,132 +227,74 @@ var expiresIn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 600; | ||
} | ||
if (params.exp) { | ||
// checks for expiration on requests, if none is provided the default is 10 min | ||
payload.exp = params.exp; | ||
} | ||
return this.signJWT((0, _extends3.default)({}, payload, { type: 'shareReq' }), params.exp ? undefined : expiresIn); | ||
return (0, _JWT.createJWT)({ address: this.settings.address, signer: this.settings.signer }, (0, _extends3.default)({}, payload, { type: 'shareReq' })); | ||
//return this.signJWT({...payload, type: 'shareReq'}, params.exp ? undefined : expiresIn) | ||
} | ||
/** | ||
* Creates a [Selective Disclosure Request JWT](https://github.com/uport-project/specs/blob/develop/messages/sharereq.md) | ||
* Creates a signed request for the user to attest a list of claims. | ||
* | ||
* @example | ||
* const req = { requested: ['name', 'country'], | ||
* callbackUrl: 'https://myserver.com', | ||
* notifications: true } | ||
* credentials.createRequest(req).then(jwt => { | ||
* ... | ||
* const unsignedClaim = { | ||
* claim: { | ||
* "Citizen of city X": { | ||
* "Allowed to vote": true, | ||
* "Document": "QmZZBBKPS2NWc6PMZbUk9zUHCo1SHKzQPPX4ndfwaYzmPW" | ||
* } | ||
* }, | ||
* sub: "2oTvBxSGseWFqhstsEHgmCBi762FbcigK5u" | ||
* } | ||
* credentials.createVerificationRequest(unsignedClaim).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 | ||
* @param {Object} unsignedClaim an object that is an unsigned claim which you want the user to attest | ||
* @param {String} sub the DID of the identity you want to sign the attestation | ||
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error | ||
*/ | ||
}, { | ||
key: 'createRequest', | ||
value: function createRequest() { | ||
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return this.requestDisclosure(params); | ||
key: 'createVerificationRequest', | ||
value: function createVerificationRequest(unsignedClaim, sub) { | ||
return (0, _JWT.createJWT)({ address: this.settings.address, signer: this.settings.signer }, { unsignedClaim: unsignedClaim, sub: sub, type: 'verReq' }); | ||
} | ||
/** | ||
* 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 | ||
*/ | ||
* 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. | ||
*/ | ||
}, { | ||
key: 'push', | ||
value: function push(token, pubEncKey, payload) { | ||
var iss = (0, _didJwt.decodeJWT)(token).payload.iss; | ||
var pushUrl = iss.match(/did/) ? 'https://api.uport.me/pututu/sns' : 'https://pututu.uport.space/api/v2/sns'; | ||
return _uportCore.transport.push.send(token, pubEncKey, pushUrl)(payload.url, { message: payload.message }); | ||
key: 'receive', | ||
value: function receive(token) { | ||
var callbackUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
return this.authenticate(token, callbackUrl); | ||
} | ||
/** | ||
* 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 | ||
*/ | ||
}, { | ||
key: 'disclose', | ||
value: function () { | ||
var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() { | ||
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var expiresIn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 600; | ||
var verified; | ||
return _regenerator2.default.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
if (!payload.req) { | ||
_context.next = 5; | ||
break; | ||
} | ||
_context.next = 3; | ||
return (0, _didJwt.verifyJWT)(payload.req); | ||
case 3: | ||
verified = _context.sent; | ||
if (verified.issuer) { | ||
payload.aud = verified.issuer; | ||
} | ||
case 5: | ||
return _context.abrupt('return', this.signJWT((0, _extends3.default)({}, payload, { type: 'shareResp' }), expiresIn)); | ||
case 6: | ||
case 'end': | ||
return _context.stop(); | ||
} | ||
} | ||
}, _callee, this); | ||
})); | ||
function disclose() { | ||
return _ref2.apply(this, arguments); | ||
} | ||
return disclose; | ||
}() | ||
}, { | ||
key: 'processDisclosurePayload', | ||
value: function () { | ||
var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(_ref3) { | ||
var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(_ref2) { | ||
var _this2 = this; | ||
var doc = _ref3.doc, | ||
payload = _ref3.payload; | ||
var doc = _ref2.doc, | ||
payload = _ref2.payload; | ||
var credentials, verified; | ||
return _regenerator2.default.wrap(function _callee2$(_context2) { | ||
return _regenerator2.default.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context2.prev = _context2.next) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
@@ -403,14 +319,14 @@ credentials = (0, _extends3.default)({}, doc.uportProfile || {}, payload.own || {}, payload.capabilities && payload.capabilities.length === 1 ? { pushToken: payload.capabilities[0] } : {}, { address: payload.nad, did: payload.iss }); | ||
if (!payload.verified) { | ||
_context2.next = 12; | ||
_context.next = 12; | ||
break; | ||
} | ||
_context2.next = 8; | ||
_context.next = 8; | ||
return _promise2.default.all(payload.verified.map(function (token) { | ||
return (0, _didJwt.verifyJWT)(token, { audience: _this2.givenDID ? _this2.did : _this2.address }); | ||
return (0, _JWT.verifyJWT)({ adress: _this2.settings.address, signer: _this2.settingssigner }, token); | ||
})); | ||
case 8: | ||
verified = _context2.sent; | ||
return _context2.abrupt('return', (0, _extends3.default)({}, credentials, { verified: verified.map(function (v) { | ||
verified = _context.sent; | ||
return _context.abrupt('return', (0, _extends3.default)({}, credentials, { verified: verified.map(function (v) { | ||
return (0, _extends3.default)({}, v.payload, { jwt: v.jwt }); | ||
@@ -420,14 +336,14 @@ }) })); | ||
case 12: | ||
return _context2.abrupt('return', credentials); | ||
return _context.abrupt('return', credentials); | ||
case 13: | ||
case 'end': | ||
return _context2.stop(); | ||
return _context.stop(); | ||
} | ||
} | ||
}, _callee2, this); | ||
}, _callee, this); | ||
})); | ||
function processDisclosurePayload(_x7) { | ||
return _ref4.apply(this, arguments); | ||
function processDisclosurePayload(_x5) { | ||
return _ref3.apply(this, arguments); | ||
} | ||
@@ -439,19 +355,19 @@ | ||
/** | ||
* 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.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. | ||
*/ | ||
@@ -461,39 +377,39 @@ }, { | ||
value: function () { | ||
var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(token) { | ||
var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(token) { | ||
var callbackUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
var _ref6, payload, doc, challenge; | ||
var _ref5, payload, doc, challenge; | ||
return _regenerator2.default.wrap(function _callee3$(_context3) { | ||
return _regenerator2.default.wrap(function _callee2$(_context2) { | ||
while (1) { | ||
switch (_context3.prev = _context3.next) { | ||
switch (_context2.prev = _context2.next) { | ||
case 0: | ||
_context3.next = 2; | ||
return (0, _didJwt.verifyJWT)(token, { audience: this.givenDID ? this.did : this.address, callbackUrl: callbackUrl, auth: true }); | ||
_context2.next = 2; | ||
return (0, _JWT.verifyJWT)({ address: this.settings.address }, token); | ||
case 2: | ||
_ref6 = _context3.sent; | ||
payload = _ref6.payload; | ||
doc = _ref6.doc; | ||
_ref5 = _context2.sent; | ||
payload = _ref5.payload; | ||
doc = _ref5.doc; | ||
if (!payload.req) { | ||
_context3.next = 13; | ||
_context2.next = 13; | ||
break; | ||
} | ||
_context3.next = 8; | ||
return (0, _didJwt.verifyJWT)(payload.req); | ||
_context2.next = 8; | ||
return (0, _JWT.verifyJWT)({ address: this.settings.address }, payload.req); | ||
case 8: | ||
challenge = _context3.sent; | ||
challenge = _context2.sent; | ||
if (!(challenge.payload.iss === this.did && challenge.payload.type === 'shareReq')) { | ||
_context3.next = 11; | ||
if (!(challenge.payload.iss === this.settings.address && challenge.payload.type === 'shareReq')) { | ||
_context2.next = 11; | ||
break; | ||
} | ||
return _context3.abrupt('return', this.processDisclosurePayload({ payload: payload, doc: doc })); | ||
return _context2.abrupt('return', this.processDisclosurePayload({ payload: payload, doc: doc })); | ||
case 11: | ||
_context3.next = 14; | ||
_context2.next = 14; | ||
break; | ||
@@ -506,10 +422,10 @@ | ||
case 'end': | ||
return _context3.stop(); | ||
return _context2.stop(); | ||
} | ||
} | ||
}, _callee3, this); | ||
}, _callee2, this); | ||
})); | ||
function authenticate(_x9) { | ||
return _ref5.apply(this, arguments); | ||
function authenticate(_x7) { | ||
return _ref4.apply(this, arguments); | ||
} | ||
@@ -521,111 +437,55 @@ | ||
/** | ||
* 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: 'createVerificationRequest', | ||
value: function createVerificationRequest(unsignedClaim, sub, callbackUrl, aud) { | ||
return this.signJWT({ unsignedClaim: unsignedClaim, sub: sub, aud: aud, callback: callbackUrl, type: 'verReq' }); | ||
} | ||
/** | ||
* Receive signed response token from mobile app. Verifies and parses the given response token. | ||
* 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. | ||
* | ||
* @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 | ||
* @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 | ||
*/ | ||
}, { | ||
key: 'receive', | ||
value: function receive(token) { | ||
var callbackUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
key: 'push', | ||
value: function push(token, pubEncKey, payload) { | ||
return new _promise2.default(function (resolve, reject) { | ||
if (!token) { | ||
reject(new Error('Missing push notification token')); | ||
} | ||
if (!pubEncKey || pubEncKey.url) { | ||
reject(new Error('Missing public encryption key of the receiver')); | ||
} | ||
if (!payload || !payload.url) { | ||
reject(new Error('Missing payload url for sending to users device')); | ||
} | ||
var iss = (0, _didJwt.decodeJWT)(token).payload.iss; | ||
var PUTUTU_URL = iss.match(/did/) ? 'https://api.uport.me' : 'https://pututu.uport.space'; | ||
var endpoint = iss.match(/did/) ? '/pututu/sns' : '/api/v2/sns'; | ||
var plaintext = padMessage((0, _stringify2.default)(payload)); | ||
var enc = encryptMessage(plaintext, pubEncKey); | ||
payload = { message: (0, _stringify2.default)(enc) }; | ||
return (0, _didJwt.decodeJWT)(token).req ? this.authenticate(token, callbackUrl) : this.verifyProfile(token, callbackUrl); | ||
(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())); | ||
}); | ||
}); | ||
} | ||
/** | ||
* 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. | ||
*/ | ||
}, { | ||
key: 'verifyProfile', | ||
value: function () { | ||
var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(token) { | ||
var callbackUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
var _ref8, payload, doc; | ||
return _regenerator2.default.wrap(function _callee4$(_context4) { | ||
while (1) { | ||
switch (_context4.prev = _context4.next) { | ||
case 0: | ||
_context4.next = 2; | ||
return (0, _didJwt.verifyJWT)(token, { audience: this.givenDID ? this.did : this.address, callbackUrl: callbackUrl }); | ||
case 2: | ||
_ref8 = _context4.sent; | ||
payload = _ref8.payload; | ||
doc = _ref8.doc; | ||
return _context4.abrupt('return', this.processDisclosurePayload({ payload: payload, doc: doc })); | ||
case 6: | ||
case 'end': | ||
return _context4.stop(); | ||
} | ||
} | ||
}, _callee4, this); | ||
})); | ||
function verifyProfile(_x12) { | ||
return _ref7.apply(this, arguments); | ||
} | ||
return verifyProfile; | ||
}() | ||
/** | ||
* Create a credential (a signed JSON Web Token) | ||
@@ -651,69 +511,11 @@ * | ||
key: 'attest', | ||
value: function attest(_ref9) { | ||
var sub = _ref9.sub, | ||
claim = _ref9.claim, | ||
exp = _ref9.exp; | ||
value: function attest(_ref6) { | ||
var sub = _ref6.sub, | ||
claim = _ref6.claim, | ||
exp = _ref6.exp; | ||
return this.signJWT({ sub: sub, claim: claim, exp: exp }); | ||
return (0, _JWT.createJWT)({ address: this.settings.address, signer: this.settings.signer }, { sub: sub, claim: claim, exp: 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 | ||
*/ | ||
}, { | ||
key: 'contract', | ||
value: function contract(abi) { | ||
var _this3 = this; | ||
var txObjHandler = function txObjHandler(txObj, opts) { | ||
txObj.fn = txObj.function; | ||
delete txObj['function']; | ||
return _this3.txRequest(txObj, opts); | ||
}; | ||
return (0, _Contract.ContractFactory)(txObjHandler.bind(this))(abi); | ||
} | ||
/** | ||
* 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.txRequest(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: 'txRequest', | ||
value: function txRequest(txObj) { | ||
var _ref10 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
callbackUrl = _ref10.callbackUrl, | ||
_ref10$exp = _ref10.exp, | ||
exp = _ref10$exp === undefined ? 600 : _ref10$exp, | ||
network_id = _ref10.network_id, | ||
label = _ref10.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: 'ethtx' }), exp); | ||
} | ||
/** | ||
* Look up a profile in the registry for a given uPort address. Address must be MNID encoded. | ||
@@ -737,12 +539,13 @@ * | ||
} | ||
}], [{ | ||
key: 'createIdentity', | ||
value: function createIdentity() { | ||
var kp = secp256k1.genKeyPair(); | ||
var publicKey = kp.getPublic('hex'); | ||
var privateKey = kp.getPrivate('hex'); | ||
var address = (0, _Digest.toEthereumAddress)(publicKey); | ||
var did = 'did:ethr:' + address; | ||
return { did: did, privateKey: privateKey }; | ||
} | ||
// createJWT ({address, signer}, payload) { | ||
// return createJWT( | ||
// payload, { issuer: address, | ||
// signer: signer}) | ||
// } | ||
// verifyJWT ({registry, address}, jwt, callbackUrl = null) { | ||
// return verifyJWT(jwt, {audience: address, callbackUrl: callbackUrl}) | ||
// } | ||
}]); | ||
@@ -766,2 +569,38 @@ return Credentials; | ||
/** | ||
* 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; |
@@ -7,2 +7,4 @@ 'use strict'; | ||
var _JWT = require('./JWT'); | ||
var _didJwt = require('did-jwt'); | ||
@@ -18,18 +20,4 @@ | ||
var createJWTWrap = function createJWTWrap(_ref, payload) { | ||
var address = _ref.address, | ||
signer = _ref.signer; | ||
return (0, _didJwt.createJWT)(payload, { issuer: address, signer: signer }); | ||
}; | ||
var JWT = { createJWT: _JWT.createJWT, verifyJWT: _JWT.verifyJWT }; | ||
var verifyJWTWrap = function verifyJWTWrap(_ref2, jwt) { | ||
var registry = _ref2.registry, | ||
address = _ref2.address; | ||
var callbackUrl = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; | ||
if (registry) (0, _uportDidResolver2.default)(registry); | ||
return (0, _didJwt.verifyJWT)(jwt, { callbackUrl: callbackUrl, audience: address }); | ||
}; | ||
var JWT = { createJWT: createJWTWrap, verifyJWT: verifyJWTWrap }; | ||
module.exports = { Credentials: _Credentials2.default, SimpleSigner: _didJwt.SimpleSigner, Contract: _Contract.Contract, ContractFactory: _Contract.ContractFactory, JWT: JWT }; |
@@ -6,31 +6,18 @@ 'use strict'; | ||
}); | ||
exports.IAT_SKEW = undefined; | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
var _extends2 = require('babel-runtime/helpers/extends'); | ||
exports.createJWT = createJWT; | ||
exports.verifyJWT = verifyJWT; | ||
var _extends3 = _interopRequireDefault(_extends2); | ||
var _jsontokens = require('jsontokens'); | ||
var _promise = require('babel-runtime/core-js/promise'); | ||
var _mnid = require('mnid'); | ||
var _promise2 = _interopRequireDefault(_promise); | ||
var _base64url = require('base64url'); | ||
exports.createJWT = createJWT; | ||
exports.verifyJWT = verifyJWT; | ||
var _base64url2 = _interopRequireDefault(_base64url); | ||
var _didJwt = require('did-jwt'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var JOSE_HEADER = { typ: 'JWT', alg: 'ES256K' }; | ||
function encodeSection(data) { | ||
return _base64url2.default.encode(JSON.stringify(data)); | ||
} | ||
var ENCODED_HEADER = encodeSection(JOSE_HEADER); | ||
var LEGACY_MS = 1000000000000; | ||
var IAT_SKEW = exports.IAT_SKEW = 60; | ||
/** @module uport-js/JWT */ | ||
@@ -57,10 +44,12 @@ | ||
var signingInput = [ENCODED_HEADER, encodeSection(_extends({ iss: address, iat: Math.floor(Date.now() / 1000) }, payload))].join('.'); | ||
return new Promise(function (resolve, reject) { | ||
if (!signer) return reject(new Error('No Signer functionality has been configured')); | ||
if (!address) return reject(new Error('No application identity address has been configured')); | ||
return signer(signingInput, function (error, signature) { | ||
if (error) return reject(error); | ||
resolve([signingInput, signature].join('.')); | ||
return new _promise2.default(function (resolve, reject) { | ||
if (!address) { | ||
return reject(new Error('No application identity address has been configured')); | ||
} | ||
if (!signer) { | ||
return reject(new Error('No Signer functionality has been configured')); | ||
} | ||
return (0, _didJwt.createJWT)(payload, { issuer: address, | ||
signer: signer }).then(function (jwt) { | ||
resolve(jwt); | ||
}); | ||
@@ -96,42 +85,8 @@ }); | ||
return new Promise(function (resolve, reject) { | ||
var _decodeToken = (0, _jsontokens.decodeToken)(jwt), | ||
payload = _decodeToken.payload; | ||
registry(payload.iss).then(function (profile) { | ||
if (!profile) return reject(new Error('No profile found, unable to verify JWT')); | ||
var publicKey = profile.publicKey.match(/^0x/) ? profile.publicKey.slice(2) : profile.publicKey; | ||
var verifier = new _jsontokens.TokenVerifier('ES256K', publicKey); | ||
if (verifier.verify(jwt)) { | ||
if (payload.iat >= LEGACY_MS && payload.iat > Date.now() + IAT_SKEW * 1000 || payload.iat < LEGACY_MS && payload.iat > Date.now() / 1000 + IAT_SKEW) { | ||
return reject(new Error('JWT not valid yet (issued in the future): iat: ' + payload.iat + ' > now: ' + Date.now() / 1000)); | ||
} | ||
if (payload.exp && payload.exp >= LEGACY_MS && payload.exp <= Date.now() || payload.iat < LEGACY_MS && payload.exp <= Date.now() / 1000) { | ||
return reject(new Error('JWT has expired: exp: ' + payload.exp + ' < now: ' + Date.now() / 1000)); | ||
} | ||
if (payload.aud) { | ||
if (payload.aud.match(/^0x[0-9a-fA-F]+$/) || (0, _mnid.isMNID)(payload.aud)) { | ||
if (!address) { | ||
return reject(new Error('JWT audience is required but your app address has not been configured')); | ||
} | ||
var addressHex = (0, _mnid.isMNID)(address) ? (0, _mnid.decode)(address).address : address; | ||
var audHex = (0, _mnid.isMNID)(payload.aud) ? (0, _mnid.decode)(payload.aud).address : payload.aud; | ||
if (audHex !== addressHex) { | ||
return reject(new Error('JWT audience does not match your address: aud: ' + payload.aud + ' !== yours: ' + address)); | ||
} | ||
} else { | ||
if (!callbackUrl) { | ||
return reject(new Error('JWT audience matching your callback url is required but one wasn\'t passed in')); | ||
} | ||
if (payload.aud !== callbackUrl) { | ||
return reject(new Error('JWT audience does not match the callback url: aud: ' + payload.aud + ' !== url: ' + callbackUrl)); | ||
} | ||
} | ||
} | ||
resolve({ payload: payload, profile: profile, jwt: jwt }); | ||
} else { | ||
return reject(new Error('Signature invalid for JWT')); | ||
} | ||
}).catch(reject); | ||
return new _promise2.default(function (resolve, reject) { | ||
return (0, _didJwt.verifyJWT)(jwt, { audience: address, callbackUrl: callbackUrl }).then(function (verifiedObj) { | ||
var obj = {}; | ||
if (verifiedObj.doc) obj.profile = verifiedObj.doc; | ||
resolve((0, _extends3.default)({}, obj, verifiedObj)); | ||
}); | ||
}); | ||
@@ -138,0 +93,0 @@ } |
{ | ||
"name": "uport", | ||
"version": "0.7.0-alpha-minor-12", | ||
"version": "0.7.0-alpha-minor-13", | ||
"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", | ||
@@ -27,14 +27,20 @@ "prepublish": "yarn build" | ||
"dependencies": { | ||
"base64url": "^3.0.0", | ||
"did-jwt": "^0.0.7", | ||
"did-resolver": "^0.0.4", | ||
"ethr-did-resolver": "^0.0.7", | ||
"ethjs-util": "^0.1.3", | ||
"ethr-did-resolver": "^0.0.7", | ||
"jsontokens": "^0.7.6", | ||
"mnid": "^0.1.1", | ||
"muport-did-resolver": "^0.1.0", | ||
"nets": "^3.2.0", | ||
"tweetnacl": "^1.0.0", | ||
"tweetnacl-util": "^0.15.0", | ||
"uport-lite": "^1.0.0", | ||
"uport-core": "^0.0.43", | ||
"uport-did-resolver": "^0.0.3", | ||
"uport-lite": "^1.0.2" | ||
"uport-did-resolver": "^0.0.3" | ||
}, | ||
"jest": { | ||
"coverageDirectory": "./coverage/", | ||
"collectCoverage": true, | ||
"unmockedModulePathPatterns": [ | ||
@@ -63,2 +69,3 @@ "<rootDir>/node_modules/nock" | ||
"mockdate": "^2.0.1", | ||
"nock": "^9.0.6", | ||
"webpack": "^4.6.0", | ||
@@ -65,0 +72,0 @@ "webpack-cli": "^2.0.15" |
@@ -41,25 +41,27 @@ # uport-js | ||
```javascript | ||
import { Credentials } from 'uport' | ||
import { Credentials, SimpleSigner } from 'uport' | ||
// For new ethereum based addresses | ||
const signer = SimpleSigner(process.env.PRIVATE_KEY) | ||
const credentials = new Credentials({ | ||
appName: 'App Name', | ||
did: 'did:ethr:0x....', | ||
privateKey: process.env.PRIVATE_KEY | ||
address: 'MNID Encoded uPort Address For Your App', | ||
signer: signer, | ||
networks: networks | ||
}) | ||
``` | ||
// You can create a new identity | ||
Going forward all uPort application ID addresses must be [MNID encoded](https://github.com/uport-project/mnid). MNID will encode the network with the address. Use of hex encoded addresses is deprecated. Using a hex encoded address will indicated you are on ropsten using our deprecated registry, if you require this use case then continue to pass a hex encoded address. If you are on ropsten but using our latest registry, pass a MNID encoded address with ropsten. | ||
console.log(Credentials.createIdentity()) | ||
The networks object includes a set of networks for which JWTs will be verified over. JWT verification includes an on-chain lookup for the public key mapped to the issuers identity, the MIND encoding of the issuer's address defines the network and registry to use for lookup. If you are interested in verifying JWTs over additional networks, pass in a network configs object, defined as follows: | ||
// For legacy application identity created on App Manager | ||
const credentials = new Credentials({ | ||
appName: 'App Name', | ||
address: 'MNID Encoded uPort Address For Your App', | ||
privateKey: process.env.PRIVATE_KEY | ||
}) | ||
```javascript | ||
const networks = { id: '0x2a' : | ||
{ registry: '0x5f8e9351dc2d238fb878b6ae43aa740d62fc9758', | ||
rpcUrl: 'https://kovan.infura.io' }, | ||
id: .... : { ... } | ||
} | ||
``` | ||
Look in [uport-lite](https://github.com/uport-project/uport-lite) for the default networks and registries which will be queried for JWT verification. | ||
``` | ||
## Requesting information from your users | ||
@@ -72,3 +74,3 @@ | ||
```javascript | ||
credentials.requestDisclosure().then(requestToken => { | ||
credentials.createRequest().then(requestToken => { | ||
// send requestToken to browser | ||
@@ -81,6 +83,6 @@ }) | ||
```javascript | ||
credentials.requestDisclosure({ | ||
credentials.createRequest({ | ||
requested: ['name','phone','identity_no'], | ||
callbackUrl: 'https://....' // URL to send the response of the request to | ||
}.then(requestToken => { | ||
}).then(requestToken => { | ||
// send requestToken to browser | ||
@@ -93,3 +95,3 @@ }) | ||
```javascript | ||
credentials.requestDisclosure({network_id: '0x4'}).then(requestToken => { | ||
credentials.createRequest({network_id: '0x4'}).then(requestToken => { | ||
// send requestToken to browser | ||
@@ -99,9 +101,19 @@ }) | ||
In your front end use [uport-connect](https://github.com/uport-project/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.showRequest(requestToken).then(response => { | ||
// send response back to server | ||
}) | ||
``` | ||
Back in your server code you receive the token: | ||
```javascript | ||
credentials.authenticate(responseToken).then(profile => { | ||
credentials.receive(responseToken).then(profile => { | ||
// Store user profile | ||
}) | ||
``` | ||
For more information about the contents of the profile object see the uport-persona documentation. | ||
@@ -201,1 +213,11 @@ ### Stateless Challenge/Response | ||
## Creating Custom Signers for integrating with HSM | ||
You can easily create custom signers that integrates into your existing signing infrastructure. | ||
```javascript | ||
function sign(data, callback) { | ||
const signature = '' // send your data to your back end signer and return DER signed data | ||
callback(null, signature) | ||
} | ||
``` |
@@ -1,5 +0,5 @@ | ||
import {Contract, ContractFactory } from '../Contract' | ||
import {Contract, ContractFactory } from '..//Contract' | ||
const address = '0x41566e3a081f5032bdcad470adb797635ddfe1f0' | ||
const address = '2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX' | ||
const abiToken = [ | ||
@@ -321,4 +321,10 @@ { | ||
const uri = tokenContract.transfer('0x41566e3a081f5032bdcad470adb797635ddfe1f0', 10) | ||
expect(uri).toEqual("me.uport:0x41566e3a081f5032bdcad470adb797635ddfe1f0?function=transfer(address 0x41566e3a081f5032bdcad470adb797635ddfe1f0, uint256 10)") | ||
expect(uri).toEqual("https://id.uport.me/2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX?function=transfer(address%200x41566e3a081f5032bdcad470adb797635ddfe1f0%2C%20uint256%2010)") | ||
}); | ||
it('it add addtional params to request when passed as last arg', () => { | ||
const params = {callbackUrl: 'http://myswebsite.com', type: 'post'} | ||
const uri = tokenContract.transfer('0x41566e3a081f5032bdcad470adb797635ddfe1f0', 10, params) | ||
expect(uri).toEqual("https://id.uport.me/2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX?function=transfer(address%200x41566e3a081f5032bdcad470adb797635ddfe1f0%2C%20uint256%2010)&callback_url=http%3A%2F%2Fmyswebsite.com&type=post") | ||
}); | ||
}); | ||
@@ -325,0 +331,0 @@ |
import Credentials from '../Credentials' | ||
import { SimpleSigner, createJWT, verifyJWT, decodeJWT } from 'did-jwt' | ||
import { createJWT, verifyJWT } from '../JWT' | ||
import { SimpleSigner, decodeJWT } from 'did-jwt' | ||
import MockDate from 'mockdate' | ||
import { registerMethod } from 'did-resolver' | ||
import nacl from 'tweetnacl' | ||
import naclutil from 'tweetnacl-util' | ||
import nock from 'nock' | ||
MockDate.set(1485321133 * 1000) | ||
@@ -11,12 +14,12 @@ | ||
const address = '0xbc3ae59bc76f894822622cdef7a2018dbe353840' | ||
const did = `did:ethr:${address}` | ||
const mnid = '2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX' | ||
const did = `did:ethr:${mnid}` | ||
const claim = {sub: '0x112233', claim: {email: 'bingbangbung@email.com'}, exp: 1485321133 + 1} | ||
const uport = new Credentials({privateKey, did}) | ||
const uport = new Credentials({signer: signer, address: mnid}) | ||
const uport2 = new Credentials({}) | ||
function mockresolver (profile) { | ||
registerMethod('ethr', async (id, parsed) => { | ||
registerMethod('uport', async (id, parsed) => { | ||
const doc = { | ||
@@ -28,2 +31,3 @@ '@context': 'https://w3id.org/did/v1', | ||
type: 'Secp256k1VerificationKey2018', | ||
publicKeyHex: '048f71f156f9b489d8c566c9943585d4c255aa5d22924abe3b9f7997de46a378ac89a668eacb9053ceac72f6a0abdeee025d61984059a6732e6cb4f106ed281ffe', | ||
owner: id, | ||
@@ -47,28 +51,16 @@ ethereumAddress: parsed.id | ||
describe('sets did', () => { | ||
describe('`did` configured', () => { | ||
expect(new Credentials({did}).did).toEqual(did) | ||
it('ethereum `address` configured', () => { | ||
expect(() => new Credentials({address})).toThrowError('Only MNID app identities accepted') | ||
}) | ||
describe('ethereum `address` configured', () => { | ||
expect(new Credentials({address}).did).toEqual(did) | ||
it('mnid `address` configured', () => { | ||
expect(new Credentials({address: mnid}).settings.did).toEqual(`did:uport:${mnid}`) | ||
}) | ||
describe('`privateKey` configured', () => { | ||
expect(new Credentials({privateKey}).did).toEqual(did) | ||
}) | ||
describe('mnid `address` configured', () => { | ||
expect(new Credentials({address: mnid}).did).toEqual(`did:uport:${mnid}`) | ||
}) | ||
}) | ||
describe('sets signer', () => { | ||
describe('always uses signer if passed in', () => { | ||
it('always uses signer if passed in', () => { | ||
const signer = SimpleSigner(privateKey) | ||
expect(new Credentials({signer, privateKey}).signer).toEqual(signer) | ||
expect(new Credentials({signer, mnid}).settings.signer).toEqual(signer) | ||
}) | ||
describe('sets signer if privateKey is passed in', () => { | ||
expect(new Credentials({privateKey}).signer).toBeDefined() | ||
}) | ||
}) | ||
@@ -101,15 +93,7 @@ | ||
describe('createIdentity()', () => { | ||
it('creates Identity', () => { | ||
const {did, privateKey} = Credentials.createIdentity() | ||
expect(did).toMatch(/^did:ethr:0x[0-9a-fA-F]{40}$/) | ||
expect(privateKey).toMatch(/^[0-9a-fA-F]{64}$/) | ||
}) | ||
}) | ||
describe('signJWT', () => { | ||
describe('uport method', () => { | ||
it('uses ES256K algorithm', async () => { | ||
const credentials = new Credentials({address: mnid, privateKey}) | ||
const jwt = await credentials.signJWT({hello: 1}) | ||
const credentials = new Credentials({address: mnid, signer: signer}) | ||
const jwt = await createJWT({address: address, signer: signer}, {hello: 1}) | ||
const { header } = decodeJWT(jwt) | ||
@@ -120,18 +104,9 @@ expect(header.alg).toEqual('ES256K') | ||
describe('ethr method', () => { | ||
it('uses ES256K-R algorithm', async () => { | ||
const credentials = new Credentials({did, privateKey}) | ||
const jwt = await credentials.signJWT({hello: 1}) | ||
const { header } = decodeJWT(jwt) | ||
expect(header.alg).toEqual('ES256K-R') | ||
}) | ||
}) | ||
}) | ||
describe('requestDisclosure()', () => { | ||
describe('createRequest()', () => { | ||
beforeAll(() => mockresolver()) | ||
async function createAndVerify (params={}) { | ||
const jwt = await uport.requestDisclosure(params) | ||
return await verifyJWT(jwt) | ||
const jwt = await uport.createRequest(params) | ||
return await verifyJWT({address: mnid, signer: signer}, jwt) | ||
} | ||
@@ -199,3 +174,3 @@ it('creates a valid JWT for a request', async () => { | ||
const jwt = await uport.createRequest(params) | ||
return await verifyJWT(jwt) | ||
return await verifyJWT({}, jwt) | ||
} | ||
@@ -208,25 +183,6 @@ it('creates a valid JWT for a request', async () => { | ||
describe('disclose()', () => { | ||
beforeAll(() => mockresolver()) | ||
async function createAndVerify (params={}) { | ||
const jwt = await uport.disclose(params) | ||
return await verifyJWT(jwt, {audience: did}) | ||
} | ||
it('creates a valid JWT for a disclosure', async () => { | ||
const response = await createAndVerify({own: {name: 'Bob'}}) | ||
return expect(response).toMatchSnapshot() | ||
}) | ||
it('creates a valid JWT for a disclosure', async () => { | ||
const req = await uport.requestDisclosure() | ||
const response = await createAndVerify({req}) | ||
return expect(response).toMatchSnapshot() | ||
}) | ||
}) | ||
describe('createVerificationRequest', () => { | ||
it('creates a valid JWT for a request', async () => { | ||
const jwt = await uport.createVerificationRequest({claim: { test: {prop1: 1, prop2: 2}}}, 'did:uport:223ab45') | ||
return expect(await verifyJWT(jwt, {audience: did})).toMatchSnapshot() | ||
return expect(await verifyJWT({audience: did}, jwt)).toMatchSnapshot() | ||
}) | ||
@@ -239,3 +195,3 @@ }) | ||
return uport.attest({sub: 'did:uport:223ab45', claim: {email: 'bingbangbung@email.com'}, exp: 1485321133 + 1}).then((jwt) => { | ||
return expect(verifyJWT(jwt)).toMatchSnapshot() | ||
return expect(verifyJWT({audience: did}, jwt)).toMatchSnapshot() | ||
}) | ||
@@ -252,10 +208,10 @@ }) | ||
async function createShareResp (payload = {}) { | ||
const req = await uport.requestDisclosure({requested: ['name', 'phone']}) | ||
return uport.disclose({...payload, req}) | ||
const req = await uport.createRequest({requested: ['name', 'phone']}) | ||
return createJWT({address: mnid, signer: signer}, {...payload, req}) | ||
} | ||
async function createShareRespWithVerifiedCredential (payload = {}) { | ||
const req = await uport.requestDisclosure({requested: ['name', 'phone']}) | ||
const req = await uport.createRequest({requested: ['name', 'phone']}) | ||
const attestation = await uport.attest(claim) | ||
return uport.disclose({...payload, verified: [attestation], req}) | ||
return createJWT({address: mnid, signer: signer}, {...payload, verified: [attestation], req}) | ||
} | ||
@@ -300,3 +256,3 @@ | ||
it('handles response with missing challenge', async () => { | ||
const jwt = await uport.disclose({own: {name: 'bob'}}) | ||
const jwt = await createJWT({address: mnid, signer: signer}, {own: {name: 'bob'}}) | ||
expect(uport.authenticate(jwt)).rejects.toMatchSnapshot() | ||
@@ -306,3 +262,3 @@ }) | ||
describe('verifyProfile()', () => { | ||
describe('LEGACY receive()', () => { | ||
beforeAll(() => mockresolver({ | ||
@@ -313,79 +269,212 @@ 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 req = await uport.createRequest({requested: ['name', 'phone']}) | ||
const jwt = await createJWT({address: mnid, signer: signer}, {own: {name: 'Davie', phone: '+15555551234'}, req}) | ||
const profile = await uport.receive(jwt) | ||
expect(profile).toMatchSnapshot() | ||
}) | ||
}) | ||
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) | ||
expect(profile).toMatchSnapshot() | ||
describe('receive', () => { | ||
function createShareResp (payload = {}) { | ||
return uport.createRequest({requested: ['name', 'phone']}).then((jwt) => { | ||
return createJWT({address: mnid, signer: signer}, {...payload, type: 'shareResp', req: jwt}) | ||
}) | ||
} | ||
function createShareRespMissingRequest (payload = {}) { | ||
return uport.createRequest({requested: ['name', 'phone']}).then((jwt) => { | ||
return createJWT({address: mnid, signer: signer}, {...payload, type: 'shareResp'}) | ||
}) | ||
} | ||
function createShareRespWithExpiredRequest (payload = {}) { | ||
return uport.createRequest({requested: ['name', 'phone'], exp: Date.now() - 1}).then((jwt) => { | ||
return createJWT({address: mnid, signer: signer}, {...payload, type: 'shareResp', req: jwt}) | ||
}) | ||
} | ||
function createShareRespWithVerifiedCredential (payload = {}, verifiedClaim = {sub: '0x112233', claim: {email: 'bingbangbung@email.com'}, exp: 1485321133 + 1}) { | ||
return uport.attest(verifiedClaim).then(jwt => { | ||
return createShareResp({...payload, verified: [jwt]}) | ||
}) | ||
} | ||
it('returns profile mixing public and private claims', () => { | ||
return createShareResp({own: {name: 'Davie', phone: '+15555551234'}}).then(jwt => uport.receive(jwt)).then(profile => | ||
expect(profile).toMatchSnapshot() | ||
) | ||
}) | ||
it('returns profile with only public claims', async () => { | ||
const jwt = await uport.disclose() | ||
const profile = await uport.verifyProfile(jwt) | ||
expect(profile).toMatchSnapshot() | ||
it('returns profile mixing public and private claims and verified credentials', () => { | ||
return createShareRespWithVerifiedCredential({own: {name: 'Davie', phone: '+15555551234'}}).then(jwt => uport.receive(jwt)).then(profile => | ||
expect(profile).toMatchSnapshot() | ||
) | ||
}) | ||
it('returns profile with private chain network id claims', async () => { | ||
const jwt = await uport.disclose({nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m'}) | ||
const profile = await uport.verifyProfile(jwt) | ||
expect(profile).toMatchSnapshot() | ||
it('returns profile with only public claims', () => { | ||
return createShareResp().then(jwt => uport.receive(jwt)).then(profile => | ||
expect(profile).toMatchSnapshot() | ||
) | ||
}) | ||
it('returns pushToken if available', async () => { | ||
const jwt = await uport.disclose({capabilities: ['PUSHTOKEN']}) | ||
const profile = await uport.verifyProfile(jwt) | ||
expect(profile).toMatchSnapshot() | ||
it('returns profile with private chain network id claims', () => { | ||
return createShareResp({nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m'}).then(jwt => uport.receive(jwt)).then(profile => | ||
expect(profile).toMatchSnapshot() | ||
) | ||
}) | ||
}) | ||
describe('LEGACY receive()', () => { | ||
beforeAll(() => mockresolver({ | ||
name: 'Bob Smith', | ||
country: 'NI' | ||
})) | ||
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 profile = await uport.receive(jwt) | ||
expect(profile).toMatchSnapshot() | ||
it('returns profile with device key claims', () => { | ||
return createShareResp({dad: '0xdeviceKey'}).then(jwt => uport.receive(jwt)).then(profile => | ||
expect(profile).toMatchSnapshot() | ||
) | ||
}) | ||
it('returns pushToken if available', () => { | ||
return createShareResp({capabilities: ['PUSHTOKEN']}).then(jwt => uport.receive(jwt)).then(profile => | ||
expect(profile.pushToken).toEqual('PUSHTOKEN') | ||
) | ||
}) | ||
it('handles response to expired request', () => { | ||
return createShareRespWithExpiredRequest().then(jwt => uport.receive(jwt)).catch(error => expect(error.message).toEqual('JWT has expired: exp: 1485321132999 < now: 1485321133')) | ||
}) | ||
it('handles response with missing challenge', () => { | ||
return createShareRespMissingRequest().then(jwt => uport.receive(jwt)).catch(error => expect(error.message).toEqual('Challenge was not included in response')) | ||
}) | ||
/////////////////////////////// no address in uport settings /////////////////////////////// | ||
it('returns profile mixing public and private claims', () => { | ||
return createShareResp({own: {name: 'Davie', phone: '+15555551234'}}).then(jwt => uport2.receive(jwt)).then(profile => | ||
expect(profile).toMatchSnapshot() | ||
) | ||
}) | ||
it('returns profile mixing public and private claims and verified credentials', () => { | ||
return createShareRespWithVerifiedCredential({own: {name: 'Davie', phone: '+15555551234'}}).then(jwt => uport2.receive(jwt)).then(profile => | ||
expect(profile).toMatchSnapshot() | ||
) | ||
}) | ||
it('returns profile with only public claims', () => { | ||
return createShareResp().then(jwt => uport2.receive(jwt)).then(profile => | ||
expect(profile).toMatchSnapshot() | ||
) | ||
}) | ||
it('returns profile with private chain network id claims', () => { | ||
return createShareResp({nad: '34wjsxwvduano7NFC8ujNJnFjbacgYeWA8m'}).then(jwt => uport2.receive(jwt)).then(profile => | ||
expect(profile).toMatchSnapshot() | ||
) | ||
}) | ||
it('returns pushToken if available', () => { | ||
return createShareResp({capabilities: ['PUSHTOKEN']}).then(jwt => uport.receive(jwt)).then(profile => { | ||
expect(profile.pushToken).toEqual('PUSHTOKEN') | ||
} | ||
) | ||
}) | ||
}) | ||
describe('txRequest()', () => { | ||
beforeAll(() => mockresolver()) | ||
describe('push', () => { | ||
const PUTUTU_URL = 'https://api.uport.me'//'https://pututu.uport.space' // TODO - change to .me | ||
const API_v1_PATH = '/api/v1/sns' | ||
const API_v2_PATH = '/pututu/sns' | ||
const lambda = '/pututu/sns' | ||
const PUSHTOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NkstUiJ9.eyJpYXQiOjE1MzExOTcwMzgsImV4cCI6MTUzMjQ5MzAzOCwiYXVkIjoiMzVERFh3RjZIZHI2ZFFRbzFCUndRcnU3VzNkNTRhdnpCd2siLCJ0eXBlIjoibm90aWZpY2F0aW9ucyIsInZhbHVlIjoiYXJuOmF3czpzbnM6dXMtd2VzdC0yOjExMzE5NjIxNjU1ODplbmRwb2ludC9BUE5TL3VQb3J0LzVmMTA4YjZlLTk3NTItM2IwZC05NWM2LWYyZTU3MTM4ZWNlNSIsImlzcyI6ImRpZDpldGhyOjB4YjA2ZDJjZWY5ZDJjYTA3MjU2NmU3Y2RlZDMyYWI0OWY1OTFlNDRlOCJ9.0qZE3N2m7rTn8JaNVfp5LhICmzEWCqTBBh9_gn4ZGD19PCfhInX7XTav0JBRBtSKkJXx03nik9k4jZ3qvQ6CigE' | ||
const token = PUSHTOKEN | ||
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 | ||
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) | ||
beforeEach(() => { | ||
nock.disableNetConnect() | ||
}) | ||
it('creates a valid JWT for a request', async () => { | ||
const jwt = await statusContract.updateStatus('hello') | ||
const verified = await verifyJWT(jwt) | ||
expect(verified.payload).toMatchSnapshot() | ||
afterEach(() => { | ||
nock.enableNetConnect() | ||
}) | ||
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('pushes url to pututu', () => { | ||
nock(PUTUTU_URL, { | ||
reqheaders: { | ||
'authorization': `Bearer ${PUSHTOKEN}` | ||
} | ||
}) | ||
.post(lambda, (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('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 token', () => { | ||
return uport.push(null, pubEncKey, payload).catch(error => expect(error.message).toEqual('Missing push notification token')) | ||
}) | ||
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 pubEncKey', () => { | ||
nock('https://pututu.uport.space', { | ||
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, null, payload).catch(error => expect(error.message).toEqual('Missing public encryption key of the receiver')) | ||
}) | ||
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(lambda, (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(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')) | ||
}) | ||
}) |
const arrayContainsArray = require('ethjs-util').arrayContainsArray; | ||
import { isMNID } from 'mnid' | ||
// A derivative work of Nick Dodson's eths-contract https://github.com/ethjs/ethjs-contract/blob/master/src/index.js | ||
@@ -95,4 +95,14 @@ | ||
const buildRequestURI = (txObject) => { | ||
return `me.uport:${txObject.to}?function=${txObject.function}` | ||
const buildRequestURI = (txObject, {callbackUrl, type} = {}) => { | ||
if (!isMNID(txObject.to)) throw new Error('To address must be MNID') | ||
const uri = `https://id.uport.me/${txObject.to}` | ||
const pairs = [] | ||
if (txObject.value) pairs.push(['value', parseInt(txObject.value, 16)]) | ||
if (txObject.function) pairs.push(['function', txObject.function]) | ||
if (callbackUrl) pairs.push(['callback_url', callbackUrl]) | ||
if (txObject.gasPrice) pairs.push(['gasPrice', txObject.gasPrice]) | ||
if (type) pairs.push(['type',type]) | ||
return `${uri}?${pairs.map(kv => `${kv[0]}=${encodeURIComponent(kv[1])}`).join('&')}` | ||
} | ||
@@ -99,0 +109,0 @@ |
@@ -1,12 +0,16 @@ | ||
import { createJWT, verifyJWT, SimpleSigner, decodeJWT } from 'did-jwt' | ||
import UportLite from 'uport-lite' | ||
import { SimpleSigner, decodeJWT } from 'did-jwt' | ||
import { createJWT, verifyJWT } from './JWT' | ||
const MNID = require('mnid') | ||
import { ec as EC } from 'elliptic' | ||
const secp256k1 = new EC('secp256k1') | ||
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 { ContractFactory } from './Contract.js' | ||
import { transport } from 'uport-core' | ||
const secp256k1 = new EC('secp256k1') | ||
import UportLite from 'uport-lite' | ||
import nets from 'nets' | ||
import nacl from 'tweetnacl' | ||
import naclutil from 'tweetnacl-util' | ||
/** | ||
@@ -23,86 +27,49 @@ * The Credentials class allows you to easily create the signed payloads used in uPort inlcuding | ||
* | ||
* The following example is just for testing purposes. You should never store a private key in source code. | ||
* | ||
* @example | ||
* import { Credentials } from 'uport' | ||
* const credentials = new Credentials({ | ||
* privateKey: '74894f8853f90e6e3d6dfdd343eb0eb70cca06e552ed8af80adadcc573b35da3' | ||
* }) | ||
* | ||
* 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' | ||
* const credentials = new Credentials({ | ||
* did: 'did:ethr:0xbc3ae59bc76f894822622cdef7a2018dbe353840', | ||
* privateKey: '74894f8853f90e6e3d6dfdd343eb0eb70cca06e552ed8af80adadcc573b35da3' | ||
* }) | ||
* | ||
* It is recommended to store the address and private key in environment variables for your server application | ||
* | ||
* @example | ||
* import { Credentials, SimpleSigner } from 'uport' | ||
* const credentials = new Credentials({ | ||
* did: process.env.APPLICATION_DID, | ||
* signer: SimpleSigner(process.env.PRIVATE_KEY) | ||
* }) | ||
* const networks = { '0x94365e3b': { rpcUrl: 'https://private.chain/rpc', registry: '0x0101.... }} | ||
* const setttings = { networks, address: '5A8bRWU3F7j3REx3vkJ...', signer: new SimpleSigner(process.env.PRIVATE_KEY)} | ||
* const credentials = new Credentials(settings) | ||
* | ||
* 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' | ||
* const credentials = new Credentials() | ||
* | ||
* function mySigner (data) { | ||
* return new Promise((resolve, reject) => { | ||
* const signature = /// sign it | ||
* resolve(signature) | ||
* }) | ||
* } | ||
* | ||
* const credentials = new Credentials({ | ||
* did: process.env.APPLICATION_DID, | ||
* signer: mySigner | ||
* }) | ||
* | ||
* @param {Object} [settings] setttings | ||
* @param {DID} settings.did Application [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) (unique identifier) for your application | ||
* @param {String} settings.privateKey A hex encoded 32 byte private key | ||
* @param {SimpleSigner} settings.signer a signer object, see [Signer Functions](https://github.com/uport-project/did-jwt#signer-functions) | ||
* @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) | ||
* @param {Object} settings.networks DEPRECATED networks config object, ie. { '0x94365e3b': { rpcUrl: 'https://private.chain/rpc', address: '0x0101.... }} | ||
* @param {UportLite} settings.registry DEPRECATED a registry object from UportLite | ||
* @param {Object} settings.networks networks config object, ie. { '0x94365e3b': { rpcUrl: 'https://private.chain/rpc', address: '0x0101.... }} | ||
* @param {UportLite} settings.registry a registry object from UportLite | ||
* @param {SimpleSigner} settings.signer a signer object, see SimpleSigner.js | ||
* @param {Address} settings.address your uPort address (may be the address of your application's uPort identity) | ||
* @return {Credentials} self | ||
*/ | ||
constructor ({did, address, privateKey, signer, networks, registry, ethrConfig, muportConfig} = {}) { | ||
constructor ({networks, registry, signer, address, ethrConfig, muportConfig} = {}) { | ||
this.settings = {} | ||
if (signer) { | ||
this.signer = signer | ||
} else if (privateKey) { | ||
this.signer = SimpleSigner(privateKey) | ||
this.settings.signer = signer | ||
// this.signer = signer | ||
} | ||
this.givenDID = false | ||
if (did) { | ||
this.did = did | ||
this.givenDID = true | ||
} else if (address) { | ||
this.address = address | ||
if (address) { | ||
if (MNID.isMNID(address)) { | ||
this.did = `did:uport:${address}` | ||
this.settings.address = address | ||
// this.address = address | ||
this.settings.did = `did:uport:${address}` | ||
} else { | ||
throw new Error('Only MNID app identities accepted') | ||
} | ||
if (address.match('^0x[0-9a-fA-F]{40}$')) { | ||
this.did = `did:ethr:${address}` | ||
} | ||
} else if (privateKey) { | ||
const kp = secp256k1.keyFromPrivate(privateKey) | ||
const address = toEthereumAddress(kp.getPublic('hex')) | ||
this.did = `did:ethr:${address}` | ||
} | ||
this.signJWT = (payload, expiresIn) => createJWT(payload, {issuer: this.givenDID ? this.did : this.address, signer: this.signer, alg: this.did.match('^did:uport:') ? 'ES256K' : 'ES256K-R', expiresIn }) | ||
this.signJWT = (payload, expiresIn) => createJWT({ issuer: this.settings.address, signer: this.settings.signer, expiresIn }, payload) | ||
// backwards compatibility | ||
this.settings.networks = networks ? configNetworks(networks) : {} | ||
if (!this.settings.registry) { | ||
const registry = UportLite({networks: this.settings.networks}) | ||
this.settings.registry = (address) => new Promise((resolve, reject) => { | ||
registry(address, (error, profile) => { | ||
if (error) return reject(error) | ||
resolve(profile) | ||
}) | ||
}) | ||
} | ||
UportDIDResolver(registry || UportLite({networks: networks ? configNetworks(networks) : {}})) | ||
@@ -113,16 +80,4 @@ EthrDIDResolver(ethrConfig || {}) | ||
/** | ||
* generate a DID and private key | ||
*/ | ||
static createIdentity () { | ||
const kp = secp256k1.genKeyPair() | ||
const publicKey = kp.getPublic('hex') | ||
const privateKey = kp.getPrivate('hex') | ||
const address = toEthereumAddress(publicKey) | ||
const did = `did:ethr:${address}` | ||
return {did, privateKey} | ||
} | ||
/** | ||
* Creates a [Selective Disclosure Request JWT](https://github.com/uport-project/specs/blob/develop/messages/sharereq.md) | ||
* Creates a signed request token (JWT) given a request params object. | ||
* | ||
@@ -133,5 +88,9 @@ * @example | ||
* notifications: true } | ||
* credentials.requestDisclosure(req).then(jwt => { | ||
* credentials.createRequest(req).then(jwt => { | ||
* ... | ||
* }) | ||
requested: ['name','phone','identity_no'], | ||
callbackUrl: 'https://....' // URL to send the response of the request to | ||
notifications: true | ||
* | ||
@@ -145,6 +104,5 @@ * @param {Object} [params={}] request params object | ||
* @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) { | ||
createRequest (params = {}, expiresIn = 600) { | ||
const payload = {} | ||
@@ -173,136 +131,11 @@ if (params.requested) { | ||
} | ||
if (params.exp) { | ||
if (params.exp) { // checks for expiration on requests, if none is provided the default is 10 min | ||
payload.exp = params.exp | ||
} | ||
return this.signJWT({...payload, type: 'shareReq'}, params.exp ? undefined : expiresIn) | ||
return createJWT({address: this.settings.address, signer: this.settings.signer}, {...payload, type: 'shareReq'}) | ||
//return this.signJWT({...payload, type: 'shareReq'}, 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) | ||
} | ||
/** | ||
* 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 iss = decodeJWT(token).payload.iss | ||
const pushUrl = iss.match(/did/) ? 'https://api.uport.me/pututu/sns' : 'https://pututu.uport.space/api/v2/sns' | ||
return transport.push.send(token, pubEncKey, pushUrl)(payload.url, {message: payload.message}) | ||
} | ||
/** | ||
* 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 | ||
} | ||
} | ||
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.nad, did: payload.iss} | ||
if (payload.nad) { | ||
credentials.networkAddress = payload.nad | ||
} | ||
if (payload.dad) { | ||
credentials.deviceKey = payload.dad | ||
} | ||
// Backwards support | ||
try { | ||
if (doc.publicKey[0].publicKeyHex) credentials.publicKey = '0x' + doc.publicKey[0].publicKeyHex | ||
if (doc.publicKey[1].publicKeyBase64) credentials.publicEncKey = doc.publicKey[1].publicKeyBase64 | ||
} catch (err) {} | ||
if (!credentials.publicEncKey) credentials.publicEncKey = payload.publicEncKey | ||
if (payload.verified) { | ||
const verified = await Promise.all(payload.verified.map(token => verifyJWT(token, {audience: this.givenDID ? this.did : this.address}))) | ||
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) { | ||
const { payload, doc } = await verifyJWT(token, {audience: this.givenDID ? this.did : this.address, callbackUrl, auth: true}) | ||
if(payload.req) { | ||
const challenge = await verifyJWT(payload.req) | ||
if(challenge.payload.iss === this.did && challenge.payload.type === 'shareReq') { | ||
return this.processDisclosurePayload({payload, doc}) | ||
} | ||
} else { | ||
throw new Error('Challenge was not included in response') | ||
} | ||
} | ||
/** | ||
* Creates a signed request for the user to attest a list of claims. | ||
@@ -324,10 +157,8 @@ * | ||
* | ||
* @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 | ||
* @param {Object} unsignedClaim an object that is an unsigned claim which you want the user to attest | ||
* @param {String} sub the DID of the identity you want to sign the attestation | ||
* @return {Promise<Object, Error>} a promise which resolves with a signed JSON Web Token or rejects with an error | ||
*/ | ||
createVerificationRequest(unsignedClaim, sub, callbackUrl, aud) { | ||
return this.signJWT({unsignedClaim, sub, aud, callback: callbackUrl, type: 'verReq'}) | ||
createVerificationRequest (unsignedClaim, sub) { | ||
return createJWT({address: this.settings.address, signer: this.settings.signer}, {unsignedClaim, sub, type: 'verReq'}) | ||
} | ||
@@ -349,19 +180,43 @@ | ||
* @return {Promise<Object, Error>} a promise which resolves with a parsed response or rejects with an error. | ||
* @deprecated | ||
*/ | ||
receive (token, callbackUrl = null) { | ||
return decodeJWT(token).req ? this.authenticate(token, callbackUrl) : this.verifyProfile(token, callbackUrl) | ||
return this.authenticate(token, callbackUrl) | ||
} | ||
async processDisclosurePayload ({doc, payload}) { | ||
const credentials = {...doc.uportProfile || {}, ...(payload.own || {}), ...(payload.capabilities && payload.capabilities.length === 1 ? {pushToken: payload.capabilities[0]} : {}), address: payload.nad, did: payload.iss} | ||
if (payload.nad) { | ||
credentials.networkAddress = payload.nad | ||
} | ||
if (payload.dad) { | ||
credentials.deviceKey = payload.dad | ||
} | ||
// Backwards support | ||
try { | ||
if (doc.publicKey[0].publicKeyHex) credentials.publicKey = '0x' + doc.publicKey[0].publicKeyHex | ||
if (doc.publicKey[1].publicKeyBase64) credentials.publicEncKey = doc.publicKey[1].publicKeyBase64 | ||
} catch (err) {} | ||
if (!credentials.publicEncKey) credentials.publicEncKey = payload.publicEncKey | ||
if (payload.verified) { | ||
const verified = await Promise.all(payload.verified.map(token => verifyJWT({adress: this.settings.address, signer: this.settingssigner}, token))) | ||
return {...credentials, verified: verified.map(v => ({...v.payload, jwt: v.jwt}))} | ||
} else { | ||
return credentials | ||
} | ||
} | ||
/** | ||
* Verify and return profile from a [Selective Disclosure Response JWT](https://github.com/uport-project/specs/blob/develop/messages/shareresp.md). | ||
* 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). | ||
* | ||
* 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. | ||
* It Verifies and parses the given response token and verifies the challenge response flow. | ||
* | ||
* @example | ||
* const resToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJyZXF1Z....' | ||
* credentials.verifyProfile(resToken).then(profile => { | ||
* const credentials = profile.verified | ||
const name = profile.name | ||
* credentials.authenticate(resToken).then(res => { | ||
* const credentials = res.verified | ||
* const name = res.name | ||
* ... | ||
@@ -371,10 +226,70 @@ * }) | ||
* @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 verifyProfile (token, callbackUrl = null) { | ||
const { payload, doc } = await verifyJWT(token, {audience: this.givenDID ? this.did : this.address, callbackUrl}) | ||
return this.processDisclosurePayload({ payload, doc }) | ||
} | ||
async authenticate (token, callbackUrl = null) { | ||
const { payload, doc } = await verifyJWT({ address: this.settings.address }, token) | ||
if (payload.req) { | ||
const challenge = await verifyJWT({ address: this.settings.address }, payload.req) | ||
if (challenge.payload.iss === this.settings.address && challenge.payload.type === 'shareReq') { | ||
return this.processDisclosurePayload({payload, doc}) | ||
} | ||
} else { | ||
throw new Error('Challenge was not included in response') | ||
} | ||
} | ||
/** | ||
* 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) { | ||
return new Promise((resolve, reject) => { | ||
if (!token) { | ||
reject(new Error('Missing push notification token')) | ||
} | ||
if (!pubEncKey || pubEncKey.url) { | ||
reject(new Error('Missing public encryption key of the receiver')) | ||
} | ||
if (!payload || !payload.url) { | ||
reject(new Error('Missing payload url for sending to users device')) | ||
} | ||
const iss = decodeJWT(token).payload.iss | ||
const PUTUTU_URL = iss.match(/did/) ? 'https://api.uport.me' : 'https://pututu.uport.space' | ||
let endpoint = iss.match(/did/) ? '/pututu/sns' : '/api/v2/sns' | ||
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()}`)) | ||
}) | ||
}) | ||
} | ||
/** | ||
* Create a credential (a signed JSON Web Token) | ||
@@ -398,48 +313,5 @@ * | ||
attest ({sub, claim, exp}) { | ||
return this.signJWT({sub: sub, claim, exp}) | ||
return createJWT({address: this.settings.address, signer: this.settings.signer}, {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) => { | ||
txObj.fn = txObj.function | ||
delete txObj['function'] | ||
return this.txRequest(txObj, opts) | ||
} | ||
return ContractFactory(txObjHandler.bind(this))(abi) | ||
} | ||
/** | ||
* 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.txRequest(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 | ||
*/ | ||
txRequest(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: 'ethtx'}, exp ) | ||
} | ||
/** | ||
@@ -461,2 +333,12 @@ * Look up a profile in the registry for a given uPort address. Address must be MNID encoded. | ||
} | ||
// createJWT ({address, signer}, payload) { | ||
// return createJWT( | ||
// payload, { issuer: address, | ||
// signer: signer}) | ||
// } | ||
// verifyJWT ({registry, address}, jwt, callbackUrl = null) { | ||
// return verifyJWT(jwt, {audience: address, callbackUrl: callbackUrl}) | ||
// } | ||
} | ||
@@ -478,3 +360,38 @@ | ||
/** | ||
* 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 |
import Credentials from './Credentials' | ||
import { SimpleSigner, createJWT, verifyJWT } from 'did-jwt' | ||
import { createJWT, verifyJWT } from './JWT' | ||
import { SimpleSigner } from 'did-jwt' | ||
import { Contract, ContractFactory } from './Contract' | ||
import UportDIDResolver from 'uport-did-resolver' | ||
const createJWTWrap = ({address, signer}, payload) => createJWT(payload, {issuer: address, signer}) | ||
const JWT = { createJWT: createJWT , verifyJWT: verifyJWT } | ||
const verifyJWTWrap = ({registry, address}, jwt, callbackUrl = null) => { | ||
if (registry) UportDIDResolver(registry) | ||
return verifyJWT(jwt, {callbackUrl, audience: address}) | ||
} | ||
const JWT = { createJWT: createJWTWrap , verifyJWT: verifyJWTWrap } | ||
module.exports = { Credentials, SimpleSigner, Contract, ContractFactory, JWT } | ||
module.exports = { Credentials, SimpleSigner, Contract, ContractFactory, JWT } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
1048360
21
6106
218
14
20
2
+ Addedbase64url@^3.0.0
+ Addedjsontokens@^0.7.6
+ Addednets@^3.2.0
+ Addedtweetnacl@^1.0.0
+ Addedtweetnacl-util@^0.15.0
+ Addedasn1.js@4.10.15.4.1(transitive)
+ Addedbase64url@3.0.1(transitive)
+ Addedjsontokens@0.7.8(transitive)
+ Addedkey-encoder@1.1.7(transitive)
+ Addedtweetnacl@1.0.3(transitive)
+ Addedvalidator@7.2.0(transitive)
- Removedcall-bind-apply-helpers@1.0.1(transitive)
- Removedcall-bound@1.0.3(transitive)
- Removeddunder-proto@1.0.1(transitive)
- Removedes-define-property@1.0.1(transitive)
- Removedes-errors@1.3.0(transitive)
- Removedes-object-atoms@1.1.1(transitive)
- Removedget-intrinsic@1.2.7(transitive)
- Removedget-proto@1.0.1(transitive)
- Removedgopd@1.2.0(transitive)
- Removedhas-symbols@1.1.0(transitive)
- Removedmath-intrinsics@1.1.0(transitive)
- Removedobject-inspect@1.13.3(transitive)
- Removedqs@6.14.0(transitive)
- Removedside-channel@1.1.0(transitive)
- Removedside-channel-list@1.0.0(transitive)
- Removedside-channel-map@1.0.1(transitive)
- Removedside-channel-weakmap@1.0.2(transitive)
Updateduport-lite@^1.0.0