├── .idea ├── encodings.xml ├── vcs.xml ├── modules.xml ├── misc.xml ├── blockey.iml ├── inspectionProfiles │ └── Project_Default.xml └── workspace.xml ├── package.json ├── LICENSE ├── .gitignore ├── README.md ├── server.js └── yarn.lock /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/blockey.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockey", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.18.3", 14 | "cors": "^2.8.4", 15 | "express": "^4.16.3", 16 | "node-fetch": "^2.1.2", 17 | "request": "^2.87.0", 18 | "web3": "^1.0.0-beta.34" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Piotr Brudny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # Typescript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | /frontend/node_modules/ 62 | /frontend/.idea/ 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Blockey 3 | KYC system using European bank's API ([PSD2 directive](https://ec.europa.eu/info/law/payment-services-psd-2-directive-eu-2015-2366_e)) 4 | 5 | 6 | Created on [Warsaw Blockathon 2018](http://blockathon.pl/) 7 | 8 | # Concept 9 | To simplify KYC process we introduced a system which verifies users identity based on his account in any European bank. We use bank open API's provided by [PSD2 directive](https://ec.europa.eu/info/law/payment-services-psd-2-directive-eu-2015-2366_e) to confirm user personal data 10 | and assign it to user wallet address. We store such assignment in a public blockchain so that any 3rd party can easily use it to assign user to his wallet and skip the KYC process. 11 | 12 | Keep in mind that we don't store any personal data on the blockchain but just the hash which can be used only for comparison but not for retrieving data so that we don't break GDPR. 13 | 14 | ## Cooperation 15 | [Alior Bank](https://www.aliorbank.pl/en) 16 | 17 | ## Frontend 18 | http://github.com/pbrudny/blockey-react 19 | 20 | ## Architecture 21 | ![blockeyready](https://user-images.githubusercontent.com/29052/41507314-a68029c8-7230-11e8-81f4-32ecdf1dec7d.png) 22 | 23 | ### Local setup 24 | ``` 25 | yarn install 26 | yarn start 27 | ``` 28 | * running on http://localhost:8000 29 | 30 | ## Contributors 31 | * [Jakub Włodarczyk](https://github.com/fenris85) 32 | * [Bartłomiej Rutkowski](https://github.com/anze1m) 33 | * [Piotr Brudny](https://github.com/pbrudny) 34 | * [Justyna Broniszewska](https://github.com/justynabroniszewska) 35 | * [Łukasz Misiak]() 36 | * [Piotr Sobczak]() 37 | 38 | ## License 39 | Blockey is released under the MIT License. 40 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var bodyParser = require('body-parser'); 3 | var Web3 = require('web3'); 4 | const request = require('request'); 5 | const fetch = require("node-fetch"); 6 | 7 | var port = process.env.PORT || 8000; 8 | var cors = require('cors'); 9 | var app = express(); 10 | 11 | app.use(cors()); 12 | 13 | const kycJson = require('./contracts/KYCValidations.json'); 14 | 15 | app.options('*', cors()); 16 | 17 | app.use(bodyParser.json()); // support json encoded bodies 18 | app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies 19 | 20 | const localnet = 'http://localhost:8545'; 21 | const infura = 'https://ropsten.infura.io/aSkZNGyMOzIcw1eOLEj9' 22 | const web3 = new Web3( new Web3.providers.HttpProvider(infura) ); 23 | const {utils} = web3; 24 | const contractAddress = "0x3603d6dd4034d91130722dbc4b0a0769a3ae8f4a"; 25 | let contract = new web3.eth.Contract(kycJson.abi, contractAddress); 26 | 27 | const privateKey = '28b8154da399ae122867893502d290d988d63bbfa478149d8c688b412bc129e6'; 28 | const account = web3.eth.accounts.privateKeyToAccount('0x' + privateKey); 29 | web3.eth.accounts.wallet.add(account); 30 | web3.eth.defaultAccount = account.address; 31 | 32 | const getPSD2Identity = async (url, token) => { 33 | let options = { 34 | method: 'POST', 35 | body: token 36 | }; 37 | 38 | const res = await fetch(url, options); 39 | const json = await res.json(); 40 | return json; 41 | }; 42 | 43 | app.post('/kyc', async function(req, res) { 44 | if (req.method === 'OPTIONS') { 45 | console.log('!OPTIONS'); 46 | var headers = {}; 47 | // IE8 does not allow domains to be specified, just the * 48 | headers["Access-Control-Allow-Origin"] = req.headers.origin; 49 | // headers["Access-Control-Allow-Origin"] = "*"; 50 | headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS"; 51 | headers["Access-Control-Allow-Credentials"] = false; 52 | headers["Access-Control-Max-Age"] = '86400'; // 24 hours 53 | headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept"; 54 | res.writeHead(200, headers); 55 | res.end(); 56 | } else { 57 | console.log("Received data"); 58 | var token = req.body.token; 59 | var wallet = req.body.wallet; 60 | var hashedData = req.body.hashed_data; 61 | 62 | var PSD2url = req.protocol + '://' + req.get('host') + '/psd2/my/transactions'; 63 | var psd2Result = await getPSD2Identity(PSD2url, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyIiOiIifQ.1cFjlFgBpDQI9ZEDSLLtceT6VXDVW79nBIY23Q6jRcM'); 64 | 65 | console.log('identity'); 66 | console.log(psd2Result); 67 | 68 | var result = psd2Result.firstName + psd2Result.lastName + psd2Result.email + psd2Result.identificationNumber; 69 | var hashFromBank = utils.sha3(result); 70 | 71 | console.log('hash1',hashFromBank); 72 | console.log('hash2',hashedData); 73 | if (hashFromBank === hashedData) { 74 | try { 75 | var result = await contract.methods.addOwnership(wallet, hashedData).send({from: web3.eth.defaultAccount, gas: 50000}); 76 | 77 | console.log('contract result', result); 78 | } catch (error) { 79 | var data = { 80 | result: "Yes" 81 | }; 82 | res.send(JSON.stringify(data)); 83 | } 84 | var data = { 85 | result: "Yes" 86 | }; 87 | res.send(JSON.stringify(data)); 88 | } else { 89 | var data = { 90 | result: "No" 91 | }; 92 | res.send(JSON.stringify(data)); 93 | } 94 | } 95 | }); 96 | 97 | app.get('/', function(req, res) { 98 | res.send('Hello from BlocKey!'); 99 | }); 100 | 101 | app.get('/success', function (req, res) { 102 | console.log('Success'); 103 | res.send('Success'); 104 | }); 105 | 106 | // mocked psd2 endpoints 107 | var psd2Result = { 108 | firstName: "John", 109 | lastName: "Doe", 110 | email: "john.doe@gmail.com", 111 | identificationNumber: "84010902434" 112 | } 113 | 114 | app.post('/psd2/my/transactions', function(req, res) { 115 | // if (req.body.token === "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyIiOiIifQ.1cFjlFgBpDQI9ZEDSLLtceT6VXDVW79nBIY23Q6jRcM") { 116 | res.json(psd2Result); 117 | // } else { 118 | // res.sendStatus(400); 119 | // } 120 | }); 121 | 122 | app.post('/psd2/my/logins/direct', function(req, res) { 123 | var token_data = { 124 | token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyIiOiIifQ.1cFjlFgBpDQI9ZEDSLLtceT6VXDVW79nBIY23Q6jRcM" 125 | }; 126 | res.send(JSON.stringify(token_data)); 127 | }); 128 | 129 | app.use(function(req, res, next) { 130 | var oneof = false; 131 | if (req.headers.origin) { //req.headers.origin.match(/whateverDomainYouWantToWhitelist/g) ) { 132 | res.header('Access-Control-Allow-Origin', req.headers.origin); 133 | oneof = true; 134 | } 135 | if (req.headers['access-control-request-method']) { 136 | res.header('Access-Control-Allow-Methods', req.headers['access-control-request-method']); 137 | oneof = true; 138 | } 139 | if (req.headers['access-control-request-headers']) { 140 | res.header('Access-Control-Allow-Headers', req.headers['access-control-request-headers']); 141 | oneof = true; 142 | } 143 | if (oneof) { 144 | res.header('Access-Control-Max-Age', 60 * 60 * 24 * 365); 145 | } 146 | 147 | // intercept OPTIONS method 148 | if (oneof && req.method == 'OPTIONS') { 149 | res.sendStatus(200); 150 | } else { 151 | next(); 152 | } 153 | }); 154 | 155 | app.listen(port, function() { 156 | console.log('Our app is running on http://localhost:' + port); 157 | }); 158 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Web3 92 | truffle 93 | app.user 94 | from 95 | 96 | 97 | 98 | 100 | 101 | 113 | 114 | 115 | 116 | 117 | true 118 | DEFINITION_ORDER 119 | 120 | 121 | 122 | 123 | 124 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 |