add simple app authorization page
This commit is contained in:
parent
abc03b4cde
commit
83941add4d
21
css/authorize.css
Normal file
21
css/authorize.css
Normal file
@ -0,0 +1,21 @@
|
||||
button {
|
||||
font-size: 18px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
width: 50%;
|
||||
border: 3px solid green;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
26
package-lock.json
generated
26
package-lock.json
generated
@ -17,6 +17,7 @@
|
||||
"express": "^4.18.2",
|
||||
"express-fileupload": "^1.3.1",
|
||||
"express-useragent": "^1.0.15",
|
||||
"hcaptcha": "^0.1.1",
|
||||
"html-minifier-terser": "^7.1.0",
|
||||
"lru-cache": "^7.14.1",
|
||||
"mysql2": "^3.1.0",
|
||||
@ -26,6 +27,9 @@
|
||||
"uglify-js": "^3.17.4",
|
||||
"unsafe_encrypt": "^1.0.4",
|
||||
"ws": "^8.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcaptcha/types": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@colors/colors": {
|
||||
@ -80,6 +84,12 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@hcaptcha/types": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@hcaptcha/types/-/types-1.0.3.tgz",
|
||||
"integrity": "sha512-1mbU6eSGawRrqeahRrOzZo/SVLI6oZ5/azuBpSyVrRRR96CnS3fOVDWfzxpngfxKD0/I9Rwu6c/3ITqD8rXeTQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
@ -1086,6 +1096,11 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hcaptcha": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/hcaptcha/-/hcaptcha-0.1.1.tgz",
|
||||
"integrity": "sha512-iMrDmH2VpIEKOrcKWidVjI89FdDKTEdZ7PfPWkP27sTazIIkob8YfdY2ezaufAnWBiUUcvzsn0qF+dyXtBH2Vw=="
|
||||
},
|
||||
"node_modules/hpack.js": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
|
||||
@ -2498,6 +2513,12 @@
|
||||
"yargs": "^16.2.0"
|
||||
}
|
||||
},
|
||||
"@hcaptcha/types": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@hcaptcha/types/-/types-1.0.3.tgz",
|
||||
"integrity": "sha512-1mbU6eSGawRrqeahRrOzZo/SVLI6oZ5/azuBpSyVrRRR96CnS3fOVDWfzxpngfxKD0/I9Rwu6c/3ITqD8rXeTQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
@ -3278,6 +3299,11 @@
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
|
||||
},
|
||||
"hcaptcha": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/hcaptcha/-/hcaptcha-0.1.1.tgz",
|
||||
"integrity": "sha512-iMrDmH2VpIEKOrcKWidVjI89FdDKTEdZ7PfPWkP27sTazIIkob8YfdY2ezaufAnWBiUUcvzsn0qF+dyXtBH2Vw=="
|
||||
},
|
||||
"hpack.js": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
|
||||
|
@ -8,6 +8,7 @@
|
||||
"express": "^4.18.2",
|
||||
"express-fileupload": "^1.3.1",
|
||||
"express-useragent": "^1.0.15",
|
||||
"hcaptcha": "^0.1.1",
|
||||
"html-minifier-terser": "^7.1.0",
|
||||
"lru-cache": "^7.14.1",
|
||||
"mysql2": "^3.1.0",
|
||||
@ -36,5 +37,8 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/002Hub/IPost/issues"
|
||||
},
|
||||
"homepage": "https://github.com/002Hub/IPost#readme"
|
||||
"homepage": "https://github.com/002Hub/IPost#readme",
|
||||
"devDependencies": {
|
||||
"@hcaptcha/types": "^1.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ export const setup = function (router, con, server) {
|
||||
console.log("error parsing header",err)
|
||||
}
|
||||
}
|
||||
if(req.body.auth != undefined) {
|
||||
if(req.body.auth !== undefined && req.originalUrl!=="/redeemauthcode") {
|
||||
if(typeof req.body.auth === "string") {
|
||||
try{
|
||||
req.body.auth = JSON.parse(req.body.auth)
|
||||
|
125
routes/authorize.js
Normal file
125
routes/authorize.js
Normal file
@ -0,0 +1,125 @@
|
||||
import {randomBytes} from "crypto"
|
||||
import {SHA256} from "../extra_modules/SHA.js";
|
||||
import {unsign} from "../extra_modules/unsign.js";
|
||||
|
||||
export const setup = function (router, con, server) {
|
||||
const temp_code_to_token = {}
|
||||
router.post("/authorize",async (req,res) => {
|
||||
if (!unsign(req.cookies.AUTH_COOKIE, req, res)){
|
||||
return
|
||||
}
|
||||
|
||||
let data = await server.hcaptcha.verify(req.body["h-captcha-response"])
|
||||
|
||||
if(data.success) {
|
||||
|
||||
console.log("captcha success")
|
||||
|
||||
let appid = req.body.application_id
|
||||
if(typeof appid === "string") {
|
||||
appid = Number(appid)
|
||||
}
|
||||
if(typeof appid === "number") {
|
||||
|
||||
const token = randomBytes(150).toString("base64")
|
||||
|
||||
let tokencode;
|
||||
while(tokencode===undefined || temp_code_to_token[tokencode]!==undefined) {
|
||||
tokencode = randomBytes(15).toString("base64")
|
||||
}
|
||||
temp_code_to_token[tokencode]={
|
||||
"userid":res.locals.userid,
|
||||
"appid":appid,
|
||||
"token":token
|
||||
}
|
||||
setTimeout(() => {
|
||||
let data = temp_code_to_token[tokencode]
|
||||
if(data !== undefined && data.token===token && data.appid === appid && data.userid === userid) {
|
||||
temp_code_to_token[tokencode]=undefined
|
||||
}
|
||||
}, 300000); //wait for 5 minutes
|
||||
|
||||
const sql = "SELECT application_auth_url FROM ipost.application where application_id=?"
|
||||
|
||||
con.query(sql,[appid],(err,result) => {
|
||||
if(err || result.length !== 1) {
|
||||
console.err(err)
|
||||
res.redirect(`/authorize?id=${req.body.application_id}`)
|
||||
return
|
||||
}
|
||||
res.redirect(`${result[0].application_auth_url}?code=${tokencode}`)
|
||||
})
|
||||
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res.redirect(`/authorize?id=${req.body.application_id}`)
|
||||
})
|
||||
|
||||
router.post("/redeemauthcode", (req,res) => {
|
||||
|
||||
if(temp_code_to_token[req.body.authcode]===undefined) {
|
||||
res.status(400)
|
||||
res.json({"status":400,"message":"invalid code given"})
|
||||
return
|
||||
}
|
||||
|
||||
if(typeof req.body.auth === "string") {
|
||||
try{
|
||||
req.body.auth = JSON.parse(req.body.auth)
|
||||
} catch(err) {
|
||||
console.log("error parsing",err)
|
||||
}
|
||||
}
|
||||
if(
|
||||
typeof req.body.auth !== "object" ||
|
||||
typeof req.body.auth.secret !== "string" ||
|
||||
typeof req.body.auth.appid !== "number" ||
|
||||
req.body.auth.secret.length !== 200 ||
|
||||
Buffer.from(req.body.auth.secret,"base64").length !== 150 ||
|
||||
req.body.auth.appid !== temp_code_to_token[req.body.authcode].appid
|
||||
) {
|
||||
//console.log(1,req.body.auth,temp_code_to_token[req.body.authcode].appid)
|
||||
res.status(420).send("invalid authentication object")
|
||||
return;
|
||||
}
|
||||
|
||||
const appid = req.body.auth.appid
|
||||
|
||||
const checksecret = SHA256(req.body.auth.secret,appid,10000)
|
||||
|
||||
const checksql = "SELECT application_id from ipost.application where application_secret=? and application_id=?"
|
||||
const checkvalues = [checksecret,appid]
|
||||
|
||||
con.query(checksql,checkvalues,(error,result_object) => {
|
||||
|
||||
if(error || result_object[0]===undefined || result_object[0].application_id!==appid) {
|
||||
res.status(400)
|
||||
res.json({"status":400,"message":"invalid code given"})
|
||||
return
|
||||
}
|
||||
|
||||
let data = temp_code_to_token[req.body.authcode]
|
||||
temp_code_to_token[req.body.authcode] = undefined
|
||||
|
||||
|
||||
const sql = "INSERT INTO `ipost`.`auth_tokens`(`auth_token`,`auth_token_u_id`,`auth_token_isfrom_application_id`) VALUES(?,?,?);"
|
||||
|
||||
const values = [SHA256(data.token,appid,10000),data.userid,data.appid] //token,id,appid
|
||||
con.query(sql,values,(err,result) => {
|
||||
if(err) {
|
||||
res.json({"status":500,"message":"error redeeming code"})
|
||||
console.err(err)
|
||||
} else {
|
||||
res.json({"status":200,"message":"successfully redeemed code","token":data.token})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
}
|
@ -11,6 +11,7 @@ import { setup as userroutessetup } from "./api/userRoutes.js";
|
||||
import { setup as servefilessetup} from "./serve_static_files.js"
|
||||
import { setup as userfilessetup} from "./userfiles.js"
|
||||
import { setup as userauthsetup} from "./user_auth.js"
|
||||
import { setup as applicationsetup} from "./authorize.js"
|
||||
|
||||
export const setup = function (router, con, server) {
|
||||
const setuproute = handler => handler(router,con,server)
|
||||
@ -34,4 +35,6 @@ export const setup = function (router, con, server) {
|
||||
setuproute(userfilessetup) //needs getPID and getDMPID
|
||||
|
||||
setuproute(userauthsetup) //login & register
|
||||
|
||||
setuproute(applicationsetup)
|
||||
}
|
@ -80,7 +80,8 @@ export const setup = function (router, con, server) {
|
||||
newrelic: load_var("./extra_modules/newrelic_monitor.html"),
|
||||
getPID: server.global_page_variables.getPID,
|
||||
getDMPID: server.global_page_variables.getDMPID,
|
||||
unauthorized_description: "Chat now by creating an account on IPost"
|
||||
unauthorized_description: "Chat now by creating an account on IPost",
|
||||
hcaptcha_sitekey: server.hcaptcha.sitekey
|
||||
}
|
||||
|
||||
|
||||
|
16
server.js
16
server.js
@ -13,6 +13,7 @@ import {unsign} from "./extra_modules/unsign.js";
|
||||
import { readFileSync, appendFile } from "fs";
|
||||
import { format } from "util";
|
||||
import { setup as SETUP_ROUTES} from "./routes/setup_all_routes.js"
|
||||
import { verify as verifyHCaptcha_int } from "hcaptcha"
|
||||
|
||||
import { ensureExists } from "./extra_modules/ensureExists.js"
|
||||
|
||||
@ -58,6 +59,13 @@ function log_info(level, ...info) {
|
||||
}
|
||||
console.log = log_info;
|
||||
|
||||
|
||||
const hcaptcha_secret = config.hcaptcha_secret
|
||||
// wrapper for the HCaptcha verify function
|
||||
function verifyHCaptcha(token) {
|
||||
return verifyHCaptcha_int(hcaptcha_secret,token,undefined,config.hcaptcha_sitekey)
|
||||
}
|
||||
|
||||
const WebSocket = ws.WebSocketServer;
|
||||
|
||||
const router = Router();
|
||||
@ -259,7 +267,7 @@ app.use(bodyParser.default.json({ limit: "100mb" }));
|
||||
app.use(bodyParser.default.urlencoded({ limit: "100mb", extended: true }));
|
||||
app.use(cookieParser(cookiesecret));
|
||||
app.use(compression())
|
||||
var blocked_headers = [
|
||||
let blocked_headers = [
|
||||
'HTTP_VIA',
|
||||
'HTTP_X_FORWARDED_FOR',
|
||||
'HTTP_FORWARDED_FOR',
|
||||
@ -334,7 +342,11 @@ var commonfunctions = {
|
||||
ensureExists,
|
||||
"dirname": __dirname,
|
||||
config,
|
||||
DID_I_FINALLY_ADD_HTTPS
|
||||
DID_I_FINALLY_ADD_HTTPS,
|
||||
hcaptcha: {
|
||||
"verify":verifyHCaptcha,
|
||||
"sitekey":config.hcaptcha_sitekey
|
||||
}
|
||||
};
|
||||
|
||||
SETUP_ROUTES(router,con,commonfunctions)
|
||||
|
@ -155,12 +155,14 @@
|
||||
"level": 5
|
||||
},
|
||||
"ssl": {
|
||||
"privateKey": "/etc/letsencrypt/live/ipost.rocks/privkey.pem",
|
||||
"certificate" : "/etc/letsencrypt/live/ipost.rocks/cert.pem"
|
||||
"privateKey": "./etc/letsencrypt/live/ipost.rocks/privkey.pem",
|
||||
"certificate" : "./etc/letsencrypt/live/ipost.rocks/fullchain.pem"
|
||||
},
|
||||
"ports": {
|
||||
"http": 9999,
|
||||
"https": 9998
|
||||
},
|
||||
"disallow_proxies_by_headers": true
|
||||
"disallow_proxies_by_headers": true,
|
||||
"hcaptcha_secret": "0x0000000000000000000000000000000000000000",
|
||||
"hcaptcha_sitekey": "10000000-ffff-ffff-ffff-000000000001"
|
||||
}
|
||||
|
31
views/authorize.html
Normal file
31
views/authorize.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<% if(user.username === undefined) { %>
|
||||
<meta name="description" content="<%-unauthorized_description%>">
|
||||
<% } else { %>
|
||||
<meta name="description" content="IPost Extension Authorization Page">
|
||||
<% } %>
|
||||
<title>Authorize App</title>
|
||||
<style>
|
||||
<%- globalcss %>
|
||||
<%- loadfile("./css/authorize.css") %>
|
||||
</style>
|
||||
<% if(user.username === undefined) { %>
|
||||
<script> document.location.href = '/no_login?r='+encodeURIComponent(document.location.pathname) </script>
|
||||
<% } else { %>
|
||||
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<div class="center">
|
||||
<h1>Authorize App</h1>
|
||||
<p>Please authorize the app to access your information:</p>
|
||||
<form action="/authorize" method="post">
|
||||
<input type="number" value=<%- query.id %> class="hidden" name="application_id" id="application_id">
|
||||
<div class="h-captcha" data-sitekey="<%- hcaptcha_sitekey %>"></div>
|
||||
<input type="submit" value="Authorize">
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user