mirror of
https://github.com/tcsenpai/OCSA-On-Chain-Signon-Authentication.git
synced 2025-06-05 10:35:28 +00:00
Initial commit
This commit is contained in:
commit
72f381232b
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
server/node_modules/.bin/
|
||||||
|
|
||||||
|
server/node_modules/
|
||||||
|
|
||||||
|
server/package-lock.json
|
16
README.md
Normal file
16
README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# OCSA
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## A blockchain based gas free (aka without costs) authentication module based on data signing through Metamask enabled browsers
|
||||||
|
|
||||||
|
This module is a work in progress and should be used with a grain of salt
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
Host auth.js on a server and run it:
|
||||||
|
|
||||||
|
npm install
|
||||||
|
node auth.js
|
||||||
|
|
||||||
|
Use the code provided in client folder to build your app.
|
46
client/auth_client.js
Normal file
46
client/auth_client.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// INFO This library relies on ethers.js
|
||||||
|
|
||||||
|
var provider;
|
||||||
|
var signer;
|
||||||
|
var address;
|
||||||
|
|
||||||
|
var session_token_storage;
|
||||||
|
|
||||||
|
const server = "http://193.187.129.116:9000"
|
||||||
|
|
||||||
|
async function connect() {
|
||||||
|
provider = new ethers.providers.Web3Provider(window.ethereum, "any");
|
||||||
|
// Prompt user for account connections
|
||||||
|
await provider.send("eth_requestAccounts", []);
|
||||||
|
signer = provider.getSigner();
|
||||||
|
address = await signer.getAddress();
|
||||||
|
console.log("Connected to: " + address);
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
// Fetching our public ip
|
||||||
|
var response = await fetch("https://api.ipify.org?format=json");
|
||||||
|
var data = await response.json();
|
||||||
|
var ip = data.ip;
|
||||||
|
// Get the message from the server
|
||||||
|
var HEADERS = new Headers ({"User-Agent": "Auther"})
|
||||||
|
var response = await fetch(server + "/auth/hello/" + ip, {headers: HEADERS });
|
||||||
|
var data = await response.json();
|
||||||
|
var session_token = data.session_token;
|
||||||
|
console.log("Session token: " + session_token);
|
||||||
|
var message = data.message;
|
||||||
|
console.log("Message: " + message);
|
||||||
|
// Sign the message
|
||||||
|
var signature = await signer.signMessage(message);
|
||||||
|
// Send the signature to the server
|
||||||
|
var response = await fetch(server + "/auth/response/", { headers: HEADERS, method: "POST", body: JSON.stringify({ session_token: session_token, message: message, signature: signature, address: address, ip: ip }) });
|
||||||
|
var data = await response.json();
|
||||||
|
console.log(data);
|
||||||
|
session_token_storage = session_token;
|
||||||
|
if (data.verified) {
|
||||||
|
return "Authenticated"
|
||||||
|
} else {
|
||||||
|
return "NOPE"
|
||||||
|
}
|
||||||
|
}
|
25
client/index.html
Normal file
25
client/index.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ethers/5.7.2/ethers.umd.min.js"></script>
|
||||||
|
<script src="auth_client.js"></script>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<head>
|
||||||
|
<title>Auth Client</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<h1>Auth Client</h1>
|
||||||
|
<button id="login">Login</button>
|
||||||
|
<h3 id="status">Waiting</h3>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// On click event
|
||||||
|
document.getElementById("login").addEventListener("click", async () => {
|
||||||
|
var addy = await connect();
|
||||||
|
console.log(addy);
|
||||||
|
document.getElementById("status").innerHTML = "Logged in";
|
||||||
|
document.getElementById("login").innerHTML = "Logged in as " + addy;
|
||||||
|
var result = await login();
|
||||||
|
document.getElementById("status").innerHTML = result;
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
0
client/style.css
Normal file
0
client/style.css
Normal file
189
server/auth.js
Normal file
189
server/auth.js
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// Require the framework and instantiate it
|
||||||
|
const fastify = require('fastify')({ logger: true })
|
||||||
|
const ethers = require('ethers')
|
||||||
|
const { hashMessage } = require("@ethersproject/hash");
|
||||||
|
|
||||||
|
var timeout = 3 // In minutes
|
||||||
|
var sessions = {}
|
||||||
|
|
||||||
|
// NOTE Verify signature method
|
||||||
|
async function verifySignature(message, signature, address) {
|
||||||
|
var derived = ethers.utils.recoverAddress(hashMessage(message),signature);
|
||||||
|
console.log("Derived: " + derived)
|
||||||
|
console.log("Address: " + address)
|
||||||
|
if (derived == address) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateUID() {
|
||||||
|
// I generate the UID from two parts here
|
||||||
|
// to ensure the random number provide enough bits.
|
||||||
|
var uid = "";
|
||||||
|
for (var i = 0; i < 12; i++) {
|
||||||
|
var part = (Math.random() * 46656) | 0;
|
||||||
|
part = (part.toString(36)).slice(-3);
|
||||||
|
uid += part;
|
||||||
|
}
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSessions() {
|
||||||
|
return sessions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare a route
|
||||||
|
fastify.get('/', async (request, reply) => {
|
||||||
|
// Allow cross origin requests
|
||||||
|
reply.header("Access-Control-Allow-Origin", "*");
|
||||||
|
console.log('request', request)
|
||||||
|
return { hello: 'world' }
|
||||||
|
})
|
||||||
|
|
||||||
|
fastify.get("/auth/hello/:ip", async (request, reply) => {
|
||||||
|
// Allow cross origin requests
|
||||||
|
reply.header("Access-Control-Allow-Origin", "*");
|
||||||
|
// Get the ip from the request
|
||||||
|
var derived_ip = request.raw.connection.remoteAddress
|
||||||
|
console.log("Derived IP: " + derived_ip)
|
||||||
|
var ip = request.params.ip
|
||||||
|
console.log(ip)
|
||||||
|
// Ip checking
|
||||||
|
if (derived_ip == "127.0.0.1" || derived_ip == "::ffff:" || derived_ip == '::1') {
|
||||||
|
console.log("Localhost")
|
||||||
|
} else {
|
||||||
|
console.log("Not localhost")
|
||||||
|
if (derived_ip != ip) {
|
||||||
|
console.log("IP mismatch")
|
||||||
|
return { error: "IP mismatch" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create a uid
|
||||||
|
var session_token = generateUID()
|
||||||
|
// Generate a random message based on timestamp
|
||||||
|
var message = "Hello_" + Date.now() + "_" + session_token + "_" + Math.random()
|
||||||
|
message = message.replace(/[^a-z0-9áéíóúñü \.,_-]/gim,"");
|
||||||
|
message = message.trim()
|
||||||
|
// Store the message in the session overwriting any previous message
|
||||||
|
sessions[session_token.toString()] = { message: message, timeout: Date.now() + 1000 * 60 * timeout, ip: ip, verified: false } // x minutes timeout
|
||||||
|
console.log("Message: " + message
|
||||||
|
+ " Timeout: " + sessions[session_token.toString()].timeout
|
||||||
|
+ " Session token: " + session_token)
|
||||||
|
console.log(sessions)
|
||||||
|
return { message: message, session_token: session_token }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Sign based authentication
|
||||||
|
fastify.post("/auth/response/", async (request, reply) => {
|
||||||
|
var local_sessions = getSessions()
|
||||||
|
console.log(local_sessions)
|
||||||
|
// Allow cross origin requests
|
||||||
|
reply.header("Access-Control-Allow-Origin", "*");
|
||||||
|
// Parse the json
|
||||||
|
var data = request.body
|
||||||
|
data = JSON.parse(data)
|
||||||
|
console.log(data)
|
||||||
|
var session_token = data.session_token
|
||||||
|
console.log("Session token: " + session_token)
|
||||||
|
var message = data.message
|
||||||
|
console.log("Message: " + message)
|
||||||
|
var signature = data.signature
|
||||||
|
console.log("Signature: " + signature)
|
||||||
|
var address = data.address
|
||||||
|
console.log("Address: " + address)
|
||||||
|
var derived_ip = request.raw.connection.remoteAddress
|
||||||
|
console.log("Derived IP: " + derived_ip)
|
||||||
|
var ip = data.ip
|
||||||
|
console.log("IP: " + ip)
|
||||||
|
// Ip checking
|
||||||
|
if (derived_ip == "127.0.0.1" || derived_ip == "::ffff:" || derived_ip == '::1') {
|
||||||
|
console.log("Localhost")
|
||||||
|
} else {
|
||||||
|
console.log("Not localhost")
|
||||||
|
if (derived_ip != ip) {
|
||||||
|
console.log("IP mismatch")
|
||||||
|
return { error: "IP mismatch" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if the session_token is in the session
|
||||||
|
if (!(session_token in local_sessions)) {
|
||||||
|
return { verified: false, error: "No session found" }
|
||||||
|
}
|
||||||
|
// Check if the message is the same as the one we sent and clear the session
|
||||||
|
if (message != local_sessions[session_token].message) {
|
||||||
|
local_sessions[session_token] = null
|
||||||
|
return { verified: false, error: "Message does not match" }
|
||||||
|
}
|
||||||
|
// Check for timeout and clear the session
|
||||||
|
if (local_sessions[session_token].timeout < Date.now()) {
|
||||||
|
local_sessions[session_token] = null
|
||||||
|
return { verified: false, error: "Session timed out" }
|
||||||
|
}
|
||||||
|
// Ensure ip is the same
|
||||||
|
if (local_sessions[session_token].ip != ip) {
|
||||||
|
local_sessions[session_token] = null
|
||||||
|
return { verified: false, error: "IP does not match" }
|
||||||
|
}
|
||||||
|
// Verify signature
|
||||||
|
var verified = await verifySignature(message, signature, address)
|
||||||
|
console.log("Verified: " + verified)
|
||||||
|
local_sessions[session_token].verified = verified
|
||||||
|
return { verified: verified, error: null }
|
||||||
|
})
|
||||||
|
|
||||||
|
fastify.post("/check", async (request, reply) => {
|
||||||
|
// Allow cross origin requests
|
||||||
|
reply.header("Access-Control-Allow-Origin", "*");
|
||||||
|
// Parse the json
|
||||||
|
var data = request.body
|
||||||
|
data = JSON.parse(data)
|
||||||
|
console.log(data)
|
||||||
|
var session_token = data.session_token
|
||||||
|
console.log("Session token: " + session_token)
|
||||||
|
var derived_ip = request.raw.connection.remoteAddress
|
||||||
|
console.log("Derived IP: " + derived_ip)
|
||||||
|
var ip = data.ip
|
||||||
|
console.log("IP: " + ip)
|
||||||
|
// Ip checking
|
||||||
|
if (derived_ip == "127.0.0.1" || derived_ip == "::ffff:" || derived_ip == '::1') {
|
||||||
|
console.log("Localhost")
|
||||||
|
} else {
|
||||||
|
console.log("Not localhost")
|
||||||
|
if (derived_ip != ip) {
|
||||||
|
console.log("IP mismatch")
|
||||||
|
return { error: "IP mismatch" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if the session_token is in the session
|
||||||
|
if (!(session_token in sessions)) {
|
||||||
|
return { authorized: false, error: "No session found" }
|
||||||
|
}
|
||||||
|
// Check for timeout and clear the session
|
||||||
|
if (sessions[session_token].timeout < Date.now()) {
|
||||||
|
sessions[session_token] = null
|
||||||
|
return { authorized: false, error: "Session timed out" }
|
||||||
|
}
|
||||||
|
// Ensure ip is the same
|
||||||
|
if (sessions[session_token].ip != ip) {
|
||||||
|
sessions[session_token] = null
|
||||||
|
return { authorized: false, error: "IP does not match" }
|
||||||
|
}
|
||||||
|
// Check if the session is verified
|
||||||
|
if (sessions[session_token].verified == false) {
|
||||||
|
return { authorized: false, error: "Session not verified" }
|
||||||
|
}
|
||||||
|
return { authorized: true, error: null, timeout: sessions[session_token].timeout}
|
||||||
|
})
|
||||||
|
|
||||||
|
const start = async () => {
|
||||||
|
|
||||||
|
// Run the api server
|
||||||
|
try {
|
||||||
|
await fastify.listen({ host: "0.0.0.0", port: 9000 })
|
||||||
|
} catch (err) {
|
||||||
|
fastify.log.error(err)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start()
|
17
server/package.json
Normal file
17
server/package.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "web3auth_server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Server side web3 based authenticator",
|
||||||
|
"main": "auth.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "tcsenpai",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@fastify/cors": "^8.2.0",
|
||||||
|
"ethers": "^5.7.2",
|
||||||
|
"fastify": "^4.10.2",
|
||||||
|
"fastify-cors": "^6.1.0"
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user