strategy.test.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. var chai = require('chai');
  2. var sinon = require('sinon');
  3. var Strategy = require('../lib/strategy');
  4. describe('Strategy', function() {
  5. var clock;
  6. afterEach(function() {
  7. clock && clock.restore();
  8. });
  9. it('should be named ethereum', function() {
  10. var strategy = new Strategy(function(){});
  11. expect(strategy.name).to.equal('ethereum');
  12. });
  13. it('should verify address', function(done) {
  14. chai.passport.use(new Strategy(function(address, cb) {
  15. expect(address).to.equal('0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758');
  16. return cb(null, { id: '248289761001' });
  17. }))
  18. .request(function(req) {
  19. req.connection = {};
  20. req.headers.host = 'localhost:3000';
  21. req.body = {
  22. message: 'localhost:3000 wants you to sign in with your Ethereum account:\n' +
  23. '0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758\n' +
  24. '\n' +
  25. 'Sign in with Ethereum to the app.\n' +
  26. '\n' +
  27. 'URI: http://localhost:3000\n' +
  28. 'Version: 1\n' +
  29. 'Chain ID: 1\n' +
  30. 'Nonce: VjglqeaSMDbPSYe0K\n' +
  31. 'Issued At: 2022-06-07T16:28:10.957Z',
  32. signature: '0xb303d03782c532e2371e3d75a8b2b093c2dceb5faed5d07d6506be96be783245515db6ad55ad6d598ebdf1f7e1c5cb0d24e7147bbad47d3b9d8dfbcfab2ddcc71b'
  33. };
  34. req.session = {
  35. messages: [],
  36. 'ethereum:siwe': {
  37. nonce: 'VjglqeaSMDbPSYe0K'
  38. }
  39. };
  40. })
  41. .success(function(user, info) {
  42. expect(user).to.deep.equal({ id: '248289761001' });
  43. expect(info).to.be.undefined;
  44. expect(this.session).to.deep.equal({
  45. messages: []
  46. });
  47. done();
  48. })
  49. .error(done)
  50. .authenticate();
  51. }); // should verify address
  52. it('should fail when URI is invalid', function(done) {
  53. chai.passport.use(new Strategy(function(address, cb) {
  54. expect(address).to.equal('0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758');
  55. return cb(null, { id: '248289761001' });
  56. }))
  57. .request(function(req) {
  58. req.connection = {};
  59. req.headers.host = 'localhost:3999';
  60. req.body = {
  61. message: 'localhost:3000 wants you to sign in with your Ethereum account:\n' +
  62. '0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758\n' +
  63. '\n' +
  64. 'Sign in with Ethereum to the app.\n' +
  65. '\n' +
  66. 'URI: http://localhost:3000\n' +
  67. 'Version: 1\n' +
  68. 'Chain ID: 1\n' +
  69. 'Nonce: VjglqeaSMDbPSYe0K\n' +
  70. 'Issued At: 2022-06-07T16:28:10.957Z',
  71. signature: '0xb303d03782c532e2371e3d75a8b2b093c2dceb5faed5d07d6506be96be783245515db6ad55ad6d598ebdf1f7e1c5cb0d24e7147bbad47d3b9d8dfbcfab2ddcc71b'
  72. };
  73. req.session = {
  74. messages: [],
  75. 'ethereum:siwe': {
  76. nonce: 'VjglqeaSMDbPSYe0K'
  77. }
  78. };
  79. })
  80. .fail(function(challenge, status) {
  81. expect(challenge).to.deep.equal({ message: 'URI mismatch.' });
  82. expect(status).to.equal(403);
  83. done();
  84. })
  85. .error(done)
  86. .authenticate();
  87. }); // should fail when URI is invalid
  88. it('should fail when message is expired', function(done) {
  89. chai.passport.use(new Strategy(function(address, cb) {
  90. expect(address).to.equal('0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758');
  91. return cb(null, { id: '248289761001' });
  92. }))
  93. .request(function(req) {
  94. req.connection = {};
  95. req.headers.host = 'localhost:3000';
  96. req.body = {
  97. message: 'localhost:3000 wants you to sign in with your Ethereum account:\n' +
  98. '0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758\n' +
  99. '\n' +
  100. 'Sign in with Ethereum to the app.\n' +
  101. '\n' +
  102. 'URI: http://localhost:3000\n' +
  103. 'Version: 1\n' +
  104. 'Chain ID: 1\n' +
  105. 'Nonce: GFRz6rD1XKFyYyQT\n' +
  106. 'Issued At: 2022-06-07T22:19:22.065Z\n' +
  107. 'Expiration Time: 2022-06-07T22:20:22.065Z',
  108. signature: '0xc5050e9144943695d2ab233e3d5f205687e29735b07f4e99ef6738ff5512f249582c2b8c105c5c8b9cd9c7910e971765532a55071e0dfd2bbd13e931a024e4991c'
  109. };
  110. req.session = {
  111. messages: [],
  112. 'ethereum:siwe': {
  113. nonce: 'GFRz6rD1XKFyYyQT'
  114. }
  115. };
  116. })
  117. .fail(function(challenge, status) {
  118. expect(challenge).to.deep.equal({ message: 'Expired message.' });
  119. expect(status).to.equal(403);
  120. done();
  121. })
  122. .error(done)
  123. .authenticate();
  124. }); // should fail when message is expired
  125. it('should fail when message is not yet valid', function(done) {
  126. clock = sinon.useFakeTimers(1654640839635);
  127. chai.passport.use(new Strategy(function(address, cb) {
  128. expect(address).to.equal('0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758');
  129. return cb(null, { id: '248289761001' });
  130. }))
  131. .request(function(req) {
  132. req.connection = {};
  133. req.headers.host = 'localhost:3000';
  134. req.body = {
  135. message: 'localhost:3000 wants you to sign in with your Ethereum account:\n' +
  136. '0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758\n' +
  137. '\n' +
  138. 'Sign in with Ethereum to the app.\n' +
  139. '\n' +
  140. 'URI: http://localhost:3000\n' +
  141. 'Version: 1\n' +
  142. 'Chain ID: 1\n' +
  143. 'Nonce: uri9Uq8fydQXUDHx\n' +
  144. 'Issued At: 2022-06-07T22:27:19.635Z\n' +
  145. 'Not Before: 2022-06-07T22:28:19.635Z',
  146. signature: '0x045404ec50df21499be5fdecbb334504070b767f75e3692a62806033d5e2e6ae70a2b13011ca34af0284b48b394994da2aeea73fe05f8fc1836e66db3f1b27521b'
  147. };
  148. req.session = {
  149. messages: [],
  150. 'ethereum:siwe': {
  151. nonce: 'uri9Uq8fydQXUDHx'
  152. }
  153. };
  154. })
  155. .fail(function(challenge, status) {
  156. expect(challenge).to.deep.equal({ message: 'Message not yet valid.' });
  157. expect(status).to.equal(403);
  158. done();
  159. })
  160. .error(done)
  161. .authenticate();
  162. }); // should fail when message is not yet valid
  163. it('should fail when nonce is invalid', function(done) {
  164. clock = sinon.useFakeTimers(1654640839635);
  165. chai.passport.use(new Strategy(function(address, cb) {
  166. expect(address).to.equal('0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758');
  167. return cb(null, { id: '248289761001' });
  168. }))
  169. .request(function(req) {
  170. req.connection = {};
  171. req.headers.host = 'localhost:3000';
  172. req.body = {
  173. message: 'localhost:3000 wants you to sign in with your Ethereum account:\n' +
  174. '0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758\n' +
  175. '\n' +
  176. 'Sign in with Ethereum to the app.\n' +
  177. '\n' +
  178. 'URI: http://localhost:3000\n' +
  179. 'Version: 1\n' +
  180. 'Chain ID: 1\n' +
  181. 'Nonce: VjglqeaSMDbPSYe0K\n' +
  182. 'Issued At: 2022-06-07T16:28:10.957Z',
  183. signature: '0xb303d03782c532e2371e3d75a8b2b093c2dceb5faed5d07d6506be96be783245515db6ad55ad6d598ebdf1f7e1c5cb0d24e7147bbad47d3b9d8dfbcfab2ddcc71b'
  184. };
  185. req.session = {
  186. messages: [],
  187. 'ethereum:siwe': {
  188. nonce: 'Xri9Uq8fydQXUDHx'
  189. }
  190. };
  191. })
  192. .fail(function(challenge, status) {
  193. expect(challenge).to.deep.equal({ message: 'Invalid nonce.' });
  194. expect(status).to.equal(403);
  195. done();
  196. })
  197. .error(done)
  198. .authenticate();
  199. }); // should fail when message is invalid
  200. it('should fail when signature is invalid', function(done) {
  201. chai.passport.use(new Strategy(function(address, cb) {
  202. expect(address).to.equal('0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758');
  203. return cb(null, { id: '248289761001' });
  204. }))
  205. .request(function(req) {
  206. req.connection = {};
  207. req.headers.host = 'localhost:3000';
  208. req.body = {
  209. message: 'localhost:3000 wants you to sign in with your Ethereum account:\n' +
  210. '0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758\n' +
  211. '\n' +
  212. 'Sign in with Ethereum to the app.\n' +
  213. '\n' +
  214. 'URI: http://localhost:3000\n' +
  215. 'Version: 1\n' +
  216. 'Chain ID: 1\n' +
  217. 'Nonce: VjglqeaSMDbPSYe0K\n' +
  218. 'Issued At: 2022-06-07T16:28:10.957Z',
  219. signature: '0xF303d03782c532e2371e3d75a8b2b093c2dceb5faed5d07d6506be96be783245515db6ad55ad6d598ebdf1f7e1c5cb0d24e7147bbad47d3b9d8dfbcfab2ddcc71b'
  220. };
  221. req.session = {
  222. messages: [],
  223. 'ethereum:siwe': {
  224. nonce: 'VjglqeaSMDbPSYe0K'
  225. }
  226. };
  227. })
  228. .fail(function(challenge, status) {
  229. expect(challenge).to.deep.equal({ message: 'Invalid signature.: 0x09967aCB4912a3efDb66039b8BC8ABb202a0f3E4 !== 0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758' });
  230. expect(status).to.equal(403);
  231. done();
  232. })
  233. .error(done)
  234. .authenticate();
  235. }); // should fail when signature is invalid
  236. it('should fail when message is malformed (missing address)', function(done) {
  237. chai.passport.use(new Strategy(function(address, cb) {
  238. expect(address).to.equal('0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758');
  239. return cb(null, { id: '248289761001' });
  240. }))
  241. .request(function(req) {
  242. req.connection = {};
  243. req.headers.host = 'localhost:3000';
  244. req.body = {
  245. message: 'localhost:3000 wants you to sign in with your Ethereum account:\n' +
  246. '\n' +
  247. 'Sign in with Ethereum to the app.\n' +
  248. '\n' +
  249. 'URI: http://localhost:3000\n' +
  250. 'Version: 1\n' +
  251. 'Chain ID: 1\n' +
  252. 'Nonce: VjglqeaSMDbPSYe0K\n' +
  253. 'Issued At: 2022-06-07T16:28:10.957Z',
  254. signature: '0xb303d03782c532e2371e3d75a8b2b093c2dceb5faed5d07d6506be96be783245515db6ad55ad6d598ebdf1f7e1c5cb0d24e7147bbad47d3b9d8dfbcfab2ddcc71b'
  255. };
  256. req.session = {
  257. messages: [],
  258. 'ethereum:siwe': {
  259. nonce: 'VjglqeaSMDbPSYe0K'
  260. }
  261. };
  262. })
  263. .fail(function(challenge, status) {
  264. expect(challenge).to.deep.equal({ message: 'Malformed message.' });
  265. expect(status).to.equal(403);
  266. done();
  267. })
  268. .error(done)
  269. .authenticate();
  270. }); // should fail when message is malformed (missing address)
  271. it('should fail when missing message', function(done) {
  272. chai.passport.use(new Strategy(function(address, cb) {
  273. throw new Error('verify function should not be called');
  274. }))
  275. .request(function(req) {
  276. req.connection = {};
  277. req.headers.host = 'localhost:3000';
  278. req.body = {
  279. signature: '0xb303d03782c532e2371e3d75a8b2b093c2dceb5faed5d07d6506be96be783245515db6ad55ad6d598ebdf1f7e1c5cb0d24e7147bbad47d3b9d8dfbcfab2ddcc71b'
  280. };
  281. req.session = {
  282. messages: []
  283. };
  284. })
  285. .fail(function(challenge, status) {
  286. expect(challenge).to.deep.equal({ message: 'Missing message.' });
  287. expect(status).to.equal(400);
  288. done();
  289. })
  290. .error(done)
  291. .authenticate();
  292. });
  293. it('should fail when missing signature', function(done) {
  294. chai.passport.use(new Strategy(function(address, cb) {
  295. throw new Error('verify function should not be called');
  296. }))
  297. .request(function(req) {
  298. req.connection = {};
  299. req.headers.host = 'localhost:3000';
  300. req.body = {
  301. message: 'localhost:3000 wants you to sign in with your Ethereum account:\n' +
  302. '0xCC6F4DF4B758C4DE3203e8842E2d8CAc564D7758\n' +
  303. '\n' +
  304. 'Sign in with Ethereum to the app.\n' +
  305. '\n' +
  306. 'URI: http://localhost:3000\n' +
  307. 'Version: 1\n' +
  308. 'Chain ID: 1\n' +
  309. 'Nonce: VjglqeaSMDbPSYe0K\n' +
  310. 'Issued At: 2022-06-07T16:28:10.957Z'
  311. };
  312. req.session = {
  313. messages: []
  314. };
  315. })
  316. .fail(function(challenge, status) {
  317. expect(challenge).to.deep.equal({ message: 'Missing signature.' });
  318. expect(status).to.equal(400);
  319. done();
  320. })
  321. .error(done)
  322. .authenticate();
  323. });
  324. });