Renewal Token Update

This commit is contained in:
Josef 2025-11-24 15:25:10 +01:00
parent d8ebe48a76
commit be4d6d9392
12 changed files with 118 additions and 12 deletions

View File

@ -5,4 +5,5 @@ export class Token
id:string;
expires:ISOTimeStamp;
hashedIP:string;
renewalToken:string;
}

View File

@ -1,6 +1,7 @@
import { DateHelper } from "../../browser/date/DateHelper";
import { DateMath } from "../../browser/date/DateMath";
import { ISOTimeStamp } from "../../browser/date/ISOTimeStamp";
import { Message } from "../../browser/messages/Message";
import { CryptIO } from "../crypt/CryptIO";
import { Token } from "./Token";
import { UserManagementServer } from "./UserManagementServer";
@ -28,6 +29,7 @@ export class TokenDB
let token = new Token();
token.id = CryptIO.createUUID();
token.renewalToken = CryptIO.createUUID();
token.hashedIP = await CryptIO.hash( ip );
token.expires = ISOTimeStamp.fromDate( DateMath.fromNowAddMinutes( expireDurationInMinutes ) );
@ -36,15 +38,20 @@ export class TokenDB
return Promise.resolve( token );
}
async validate( token:Token, ip:string ):Promise<boolean>
async validate( token:Token, ip:string, debugMesssges:Message[] = undefined ):Promise<boolean>
{
if ( this._tokens.get( token.id ) != token )
{
debugMesssges?.push( Message.Error( "Token doesn't exist" ) );
return Promise.resolve( false );
}
if ( DateMath.isExpired( ISOTimeStamp.toDate( token.expires ) ) )
{
debugMesssges?.push( Message.Error( "Token is out of date" ) );
return Promise.resolve( false );
}
@ -55,6 +62,8 @@ export class TokenDB
return true;
}
debugMesssges?.push( Message.Error( "Ip mismatch" ) );
return Promise.resolve( false );
}
}

View File

@ -156,6 +156,24 @@ export class UserDB
return Promise.resolve( isValid );
}
async renew( oldToken:string, renewal:string, ip:string ):Promise<Token>
{
let session = this._ums._sessions.get( oldToken );
let tokenData = this._ums._tokenDB._tokens.get( session.token );
if ( tokenData.renewalToken !== renewal )
{
return Promise.resolve( null );
}
let hour = 60;
let day = hour * 24;
let week = day * 7;
let token = await this._ums.tokenDB.create( ip, week );
return Promise.resolve( token );
}
async login( email:string, password:string, ip:string ):Promise<Token>
{

View File

@ -10,6 +10,7 @@ export class UserLoginData
timeStamp:ISOTimeStamp;
location:LocationData;
userAgent:string;
renewal:boolean
}
export class UserData

View File

@ -61,6 +61,7 @@ export class ReminderApp extends UserApp<ReminderData> implements iTaskScheduler
( me )=>
{
let date = DateHelper.parseDateExpression( me.date );
date = DateMath.addMinutes( date, me.utcOffsetMinutes );
if ( DateMath.isBefore( date, maxDate ) )
{

View File

@ -9,6 +9,7 @@ import { ReminderEntry, ReminderEntryType } from "./ReminderEntry";
export class MailEntry extends ReminderEntry
{
date:string;
utcOffsetMinutes:number;
subject:string;
message:string;

View File

@ -4,6 +4,7 @@ import { ConfirmSignUpHandler } from "./_/confirm-signup";
import { InfoHandler } from "./_/info";
import { LoginHandler } from "./_/login";
import { LogoutHandler } from "./_/logout";
import { RenewHandler } from "./_/renew";
import { RequestPasswordChangeHandler } from "./_/request-password-change";
import { SignUpHandler } from "./_/signup";
import { AppsCanUseHandler } from "./apps/can-use";
@ -21,6 +22,7 @@ export class HandlerGroups
new ConfirmSignUpHandler(),
new LoginHandler(),
new RenewHandler(),
new LogoutHandler(),
new InfoHandler(),

View File

@ -38,6 +38,7 @@ export class LoginHandler extends RequestHandler
loginData.timeStamp = ISOTimeStamp.now();
loginData.location = await this.getLocation();
loginData.userAgent = this.userAgent;
loginData.renewal = false;
user.lastLogins = user.lastLogins || [];
user.lastLogins.push( loginData );
@ -56,7 +57,7 @@ export class LoginHandler extends RequestHandler
await this.ums.saveSessionData();
return this.sendDataInfo( "Login successfull", { token: session.token } );
return this.sendDataInfo( "Login successfull", { token: session.token, renewal: loginToken.renewalToken } );
}

View File

@ -0,0 +1,70 @@
import { RegExpUtility } from "../../../../browser/text/RegExpUtitlity";
import { CryptIO } from "../../../crypt/CryptIO";
import { RJLog } from "../../../log/RJLog";
import { RequestHandler, RequestType } from "../../RequestHandler";
import { FastifyRequest, FastifyReply } from 'fastify';
import { VariableReplacer, Variables } from "../../../../browser/text/replacing/VariableReplacer";
import { ConfirmSignUpHandler } from "./confirm-signup";
import { Session } from "../../Session";
import { UserLoginData } from "../../UserData";
import { ISOTimeStamp } from "../../../../browser/date/ISOTimeStamp";
import { Arrays } from "../../../../browser/tools/Arrays";
export class RenewHandler extends RequestHandler
{
static url = "/renew";
constructor(){ super( RequestType.POST, RenewHandler.url ); }
async _handle( request:FastifyRequest, reply:FastifyReply )
{
let requestBody = request.body;
let { token, renewal } = requestBody as { token: string; renewal: string };
if ( ! token || ! renewal )
{
return this.sendError( "Missing token or renewal:" + `"${requestBody}"` );
}
let session = this._ums._sessions.get( token );
if ( ! session )
{
return this.sendError( "Invalid token" );
}
let user = await this._ums.userDB.byID( session.userID );
let renewToken = await this.userDB.renew( token, renewal, this.ip );
if ( ! renewToken )
{
return this.sendError( "Renewal failed" );
}
let loginData = new UserLoginData();
loginData.timeStamp = ISOTimeStamp.now();
loginData.location = await this.getLocation();
loginData.userAgent = this.userAgent;
loginData.renewal = true;
user.lastLogins = user.lastLogins || [];
user.lastLogins.push( loginData );
Arrays.shiftToSize( user.lastLogins, this._ums._settings.maxLogins );
this._ums._sessions.delete( session.token );
session.token = renewToken.id;
this._ums._sessions.set( session.token, session );
await this.userDB.save();
await this.ums.saveSessionData();
return this.sendDataInfo( "Renewal successfull", { token: session.token, renewal: renewToken.renewalToken } );
}
}

View File

@ -27,11 +27,13 @@ export class UserIsLoggedIn extends RequestRequirement
let token = this.ums.tokenDB._tokens.get( tokenID );
let isValid = await this.ums.tokenDB.validate( token, request.ip );
let messages = [];
let isValid = await this.ums.tokenDB.validate( token, request.ip, messages );
if ( ! isValid )
{
return this.sendError( "Invalid token" + tokenID );
let message = messages.length > 0 ? ( " " + messages.join( ", " ) ) : "";
return this.sendError( "Invalid token: " + tokenID + message );
}
return this.giveOK();

View File

@ -56,13 +56,13 @@ export class Scheduler
scheduleTasks( tasks:Task[] )
{
let maxDate = DateMath.fromNowAddDays( this._maxScheduleDurationDays );
tasks = tasks.filter( t => ! this._scheduledTasks.has( t.id ) && ! DateMath.isAfter( t.date, maxDate ) );
tasks = tasks.filter( t => ! this._scheduledTasks.has( t.id ) && ! DateMath.isAfter( t.utcDate, maxDate ) );
this._queue = this._queue.concat( tasks );
tasks.forEach( t => this._scheduledTasks.add( t.id ) );
this._queue.sort( ( a, b ) => { return a.date.getTime() - b.date.getTime() } );
this._queue.sort( ( a, b ) => { return a.utcDate.getTime() - b.utcDate.getTime() } );
let newNextTask = this._queue[ 0 ];
@ -98,9 +98,9 @@ export class Scheduler
let task = this._queue[ 0 ];
this._nextTaskID = task.id;
let delay = Math.max( 0, task.date.getTime() - Date.now() );
let delay = Math.max( 0, task.utcDate.getTime() - Date.now() );
RJLog.log( task.id, DateFormatter.HMS( task.date ), "delaying:", Duration.fromMilliSeconds( delay ) + " sec" );
RJLog.log( task.id, DateFormatter.HMS( task.utcDate ), "delaying:", Duration.fromMilliSeconds( delay ) + " sec" );
this._taskTimerCallback = setTimeout(
() =>

View File

@ -8,7 +8,7 @@ export class Task
id:string;
userID:string;
appID:string;
date:Date;
utcDate:Date;
action:()=>void;
setUserContext( appID:string, userID:string )
@ -25,13 +25,13 @@ export class Task
return Task.createAt( id, date, action );
}
static createAt( id:string, date:Date, action:()=>void )
static createAt( id:string, utcDate:Date, action:()=>void )
{
let task = new Task();
task.id = id;
task.action = action;
task.date = date;
task.utcDate = utcDate;
return task;
}