Initial commit

This commit is contained in:
thecookingsenpai 2023-12-25 13:28:38 +01:00
commit 72f381232b
7 changed files with 301 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.DS_Store
server/node_modules/.bin/
server/node_modules/
server/package-lock.json

16
README.md Normal file
View File

@ -0,0 +1,16 @@
# OCSA
![image](https://user-images.githubusercontent.com/67682496/210122012-9cee1a06-ca3d-43b1-bef3-20dc22835705.png)
## 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
View 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
View 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
View File

189
server/auth.js Normal file
View 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
View 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"
}
}