Add automatic token migration

This commit is contained in:
KernelDeimos 2024-04-11 21:33:44 -04:00
parent 09bf422686
commit bb9edc4f65
5 changed files with 74 additions and 9 deletions

View File

@ -97,6 +97,10 @@ if (config.server_id) {
config.contact_email = 'hey@' + config.domain;
// TODO: default value will be changed to false in a future release;
// details to follow in a future announcement.
config.legacy_token_migrate = true;
module.exports = config;
// NEW_CONFIG_LOADING

View File

@ -18,8 +18,24 @@
*/
const APIError = require("../api/APIError");
const config = require("../config");
const { UserActorType } = require("../services/auth/Actor");
const { LegacyTokenError } = require("../services/auth/AuthService");
const { Context } = require("../util/context");
// The "/whoami" endpoint is a special case where we want to allow
// a legacy token to be used for authentication. The "/whoami"
// endpoint will then return a new token for further requests.
//
const is_whoami = (req) => {
if ( ! config.legacy_token_migrate ) return;
if ( req.path !== '/whoami' ) return;
// const subdomain = req.subdomains[res.subdomains.length - 1];
// if ( subdomain !== 'api' ) return;
return true;
}
// TODO: Allow auth middleware to be used without requiring
// authentication. This will allow us to use the auth middleware
// in endpoints that do not require authentication, but can
@ -70,6 +86,20 @@ const auth2 = async (req, res, next) => {
e.write(res);
return;
}
if ( e instanceof LegacyTokenError && is_whoami(req) ) {
const new_info = await svc_auth.check_session(token, {
req,
from_upgrade: true,
})
context.set('actor', new_info.actor);
context.set('user', new_info.user);
req.new_token = new_info.token;
req.token = new_info.token;
req.user = new_info.user;
req.actor = new_info.actor;
next();
return;
}
const re = APIError.create('token_auth_failed');
re.write(res);
return;

View File

@ -54,6 +54,7 @@ const WHOAMI_GET = eggspress('/whoami', {
is_temp: (req.user.password === null && req.user.email === null),
taskbar_items: await get_taskbar_items(req.user),
referral_code: req.user.referral_code,
...(req.new_token ? { token: req.token } : {})
};
if ( ! is_user ) {
@ -65,6 +66,7 @@ const WHOAMI_GET = eggspress('/whoami', {
delete details.desktop_bg_color;
delete details.desktop_bg_fit;
delete details.taskbar_items;
delete details.token;
}
res.send(details);
@ -76,8 +78,19 @@ const WHOAMI_GET = eggspress('/whoami', {
const WHOAMI_POST = new express.Router();
WHOAMI_POST.post('/whoami', auth, fs, express.json(), async (req, response, next)=>{
// check subdomain
if(require('../helpers').subdomain(req) !== 'api')
next();
if(require('../helpers').subdomain(req) !== 'api') {
return;
}
const actor = Context.get('actor');
if ( ! actor ) {
throw Error('actor not found in context');
}
const is_user = actor.type instanceof UserActorType;
if ( ! is_user ) {
throw Error('actor is not a user');
}
let desktop_items = [];

View File

@ -25,6 +25,8 @@ const { DB_WRITE } = require("../database/consts");
const APP_ORIGIN_UUID_NAMESPACE = '33de3768-8ee0-43e9-9e73-db192b97a5d8';
const LegacyTokenError = class extends Error {};
class AuthService extends BaseService {
static MODULES = {
jwt: require('jsonwebtoken'),
@ -45,7 +47,7 @@ class AuthService extends BaseService {
);
if ( ! decoded.hasOwnProperty('type') ) {
throw new Error('legacy token');
throw new LegacyTokenError();
const user = await this.db.requireRead(
"SELECT * FROM `user` WHERE `uuid` = ? LIMIT 1",
[decoded.uuid],
@ -251,7 +253,7 @@ class AuthService extends BaseService {
user_uid: user.uuid,
}, this.global_config.jwt_secret);
return token;
return { session, token };
}
async check_session (cur_token, meta) {
@ -264,13 +266,17 @@ class AuthService extends BaseService {
if ( decoded.type && decoded.type !== 'session' ) {
return {};
}
const is_legacy = ! decoded.type;
const user = await get_user({ uuid: decoded.user_uid });
const user = await get_user({ uuid:
is_legacy ? decoded.uuid : decoded.user_uid
});
if ( ! user ) {
return {};
}
if ( decoded.type ) {
if ( ! is_legacy ) {
// Ensure session exists
const session = await this.get_session_(decoded.uuid);
if ( ! session ) {
@ -285,8 +291,19 @@ class AuthService extends BaseService {
// Upgrade legacy token
// TODO: phase this out
const { token } = await this.create_session_token(user, meta);
return { user, token };
const { session, token } = await this.create_session_token(user, meta);
const actor_type = new UserActorType({
user,
session,
});
const actor = new Actor({
user_uid: user.uuid,
type: actor_type,
});
return { actor, user, token };
}
async create_access_token (authorizer, permissions) {
@ -430,4 +447,5 @@ class AuthService extends BaseService {
module.exports = {
AuthService,
LegacyTokenError,
};

View File

@ -366,7 +366,7 @@ window.initgui = async function(){
}
while(!is_verified)
}
update_auth_data(window.auth_token, whoami);
update_auth_data(whoami.token || window.auth_token, whoami);
// -------------------------------------------------------------------------------------
// Load desktop, only if we're not embedded in a popup