mirror of
https://github.com/tcsenpai/OCSA-On-Chain-Signon-Authentication.git
synced 2025-06-03 18:00:03 +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