forked from cory/tildefriends
Make auth use JWTs.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3991 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
94
core/auth.js
94
core/auth.js
@ -5,38 +5,66 @@ import * as form from './form.js';
|
||||
var gTokens = {};
|
||||
var gDatabase = new Database("auth");
|
||||
|
||||
const kRefreshInterval = 1 * 60 * 60 * 1000;
|
||||
|
||||
function b64url(value) {
|
||||
value = value.replaceAll('+', '-').replaceAll('/', '_');
|
||||
let equals = value.indexOf('=');
|
||||
if (equals !== -1) {
|
||||
return value.substring(0, equals);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function unb64url(value) {
|
||||
value = value.replaceAll('-', '+').replaceAll('_', '/');
|
||||
let remainder = value.length % 4;
|
||||
if (remainder == 3) {
|
||||
return value + '=';
|
||||
} else if (remainder == 2) {
|
||||
return value + '==';
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function makeJwt(payload) {
|
||||
let ids = ssb.getIdentities(':auth');
|
||||
let id;
|
||||
if (ids?.length) {
|
||||
id = ids[0];
|
||||
} else {
|
||||
id = ssb.createIdentity(':auth');
|
||||
}
|
||||
|
||||
let final_payload = b64url(base64Encode(JSON.stringify(Object.assign({}, payload, {exp: (new Date().valueOf()) + kRefreshInterval}))));
|
||||
let jwt = [b64url(base64Encode(JSON.stringify({alg: 'HS256', typ: 'JWT'}))), final_payload, b64url(ssb.hmacsha256sign(final_payload, ':auth', id))].join('.');
|
||||
return jwt;
|
||||
}
|
||||
|
||||
function readSession(session) {
|
||||
var result = session ? gDatabase.get("session:" + session) : null;
|
||||
|
||||
if (result) {
|
||||
result = JSON.parse(result);
|
||||
|
||||
let kRefreshInterval = 1 * 60 * 60 * 1000;
|
||||
let now = Date.now();
|
||||
if (!result.lastAccess || result.lastAccess < now - kRefreshInterval) {
|
||||
result.lastAccess = now;
|
||||
writeSession(session, result);
|
||||
let jwt_parts = session?.split('.');
|
||||
if (jwt_parts?.length === 3) {
|
||||
let [header, payload, signature] = jwt_parts;
|
||||
header = JSON.parse(base64Decode(unb64url(header)));
|
||||
if (header.typ === 'JWT' && header.alg === 'HS256') {
|
||||
signature = unb64url(signature);
|
||||
let id = ssb.getIdentities(':auth');
|
||||
if (id?.length && ssb.hmacsha256verify(id[0], payload, signature)) {
|
||||
let result = JSON.parse(base64Decode(unb64url(payload)));
|
||||
if ((new Date()).valueOf() < result.exp) {
|
||||
return result;
|
||||
} else {
|
||||
print('JWT expired.');
|
||||
}
|
||||
} else {
|
||||
print('JWT verification failed.');
|
||||
}
|
||||
} else {
|
||||
print('Invalid JWT header.');
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function writeSession(session, value) {
|
||||
gDatabase.set("session:" + session, JSON.stringify(value));
|
||||
}
|
||||
|
||||
function removeSession(session, value) {
|
||||
gDatabase.remove("session:" + session);
|
||||
}
|
||||
|
||||
function newSession() {
|
||||
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var result = "";
|
||||
for (var i = 0; i < 32; i++) {
|
||||
result += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function verifyPassword(password, hash) {
|
||||
@ -92,7 +120,6 @@ function handler(request, response) {
|
||||
var formData = form.decodeForm(request.query);
|
||||
|
||||
if (request.method == "POST" || formData.submit) {
|
||||
session = newSession();
|
||||
sessionIsNew = true;
|
||||
formData = form.decodeForm(utf8Decode(request.body), formData);
|
||||
if (formData.submit == "Login") {
|
||||
@ -114,7 +141,7 @@ function handler(request, response) {
|
||||
if (users !== users_original) {
|
||||
gDatabase.set('users', users);
|
||||
}
|
||||
writeSession(session, {name: formData.name});
|
||||
session = makeJwt({name: formData.name});
|
||||
account = {password: hashPassword(formData.password)};
|
||||
gDatabase.set("user:" + formData.name, JSON.stringify(account));
|
||||
if (noAdministrator()) {
|
||||
@ -127,7 +154,7 @@ function handler(request, response) {
|
||||
if (account &&
|
||||
account.password &&
|
||||
verifyPassword(formData.password, account.password)) {
|
||||
writeSession(session, {name: formData.name});
|
||||
session = makeJwt({name: formData.name});
|
||||
if (noAdministrator()) {
|
||||
makeAdministrator(formData.name);
|
||||
}
|
||||
@ -137,7 +164,7 @@ function handler(request, response) {
|
||||
}
|
||||
} else {
|
||||
// Proceed as Guest
|
||||
writeSession(session, {name: "guest"});
|
||||
session = makeJwt({name: 'guest'});
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,7 +218,6 @@ function handler(request, response) {
|
||||
});
|
||||
}
|
||||
} else if (request.uri == "/login/logout") {
|
||||
removeSession(session);
|
||||
response.writeHead(303, {"Set-Cookie": "session=; path=/; secure; SameSite=Strict; expires=Thu, 01 Jan 1970 00:00:00 GMT", "Location": "/login" + (request.query ? "?" + request.query : "")});
|
||||
response.end();
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user