From e98da0ac65de1942a8fd6181e2a7309ec5324b2a Mon Sep 17 00:00:00 2001 From: Rory& Date: Tue, 16 Dec 2025 06:58:45 +0100 Subject: [PATCH] Upate session info --- assets/openapi.json | Bin 857483 -> 862487 bytes assets/schemas.json | Bin 380874 -> 385068 bytes src/util/util/Token.ts | 36 +++++++++++++++++++++++++++++------- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index 0ac9295d602721309a26585bea61a04080b3ea7c..6a5298b806111e074e636a8a4cb1065d10805888 100644 GIT binary patch delta 1075 zcmaKqQD|FL7{|HyoO5r}Od4SeSz_8W>(uE?+tJoe*4oa|N{3srj)_`&b9-~zMACaN z_oPC!w1uJ*MP~O2pJg~#d{GA4)Q9?{9VmM#BMEy@1dTo@_|yq=FJpRdG8{$l;ScA0 z9Dd*LJACh6?ZIl#`dE~lVNq~$=FGe~H#^0hc~P95vhssa+US&QsC?4~ z$zULu&*>s&O~-^j&6Bz#_^>FN2CS(aQ2XF02I3Nf+HXOKrALWtgQ>U4e(E{m@H;fj zyqOwDY=2pf5@G9*N`#v3TRON>k-EtlYYwHG(loq(m%`%k2*&zp2nSk~E=>C62(~OL zY9RhhEIyfd;tUtIWeVN_zceLCmg}%`HI7T~`C9Syo8%$2G1MTYmdPQs-yl&rYe4Ew z2Lx`9KylzCmQ@z=W%YcXn=@R)(y#P!7>e}JtR)IqS(Yc6;qXijuF=PE=YrhF9LI8= z!Yiwi0DJzNEDUs&4?%jOj~SN3H7FlBfaRK;!sK1)t!Co^d+nI}1r1syvERq*B*V(U z+V5fJ^)sP+`w_TsDx$_kgH`H}Li1ohariE4*wR8$qcN7#N+14QWegv!vVGpNJPbRx zd;fPPrO~@SMO_n@K9ec@c|H6e-(6mw%a>!%*P1^Wi#`c|r~S*lO21 z0~00Uhbt2=p?#WFJ!}_rea$zwpQS+=l6`I1zTmEV-O{ce#2Tn!v~Tz#xO0c-*zz1z zaN#(OvAkvI1=KH*RxCd)v(3xLX-@J9P0!5cOikd&12p1cMsVjS_2W+kw-2=5y2R#=p5Svgd~z{{WwJkb3|C delta 188 zcmbP!$)tO=$p$ak#WO9mH}BMaz%@Cqkh^(Fa{H2GMj&R|z9gCX%&X}QW~?0371&u% zO)v0cNts@7ft7vwhHw^^>A!X|sZK9g&zQR1I)FutX}W?YWfr` zf-Zbq$N@#$s_3Sago>qZtXolhfNn&v8&@tO3gXTrRn)5Bg#+iDd;fF3pZj>wKQif0 zt3Dj&(DMn0kp`JCSt>nQ|CLh;JlRSDa3VmjOw6|;)N-R{q=>?iS9FEyjzbn*0np9EjH<9 zU#)PRxOw=b+1uU8os*)kyVFW+f#Eh3K(_4J1tDTQKU*Y(ql|l~)K`pLy}a($sr=0L%xh z2+o~HVW|8-mwp`!woDTxF(4|7ds##EVYtEMqaxU&CcD>ED( zjpx27AUk5q;o^P9=o-U{!c-F;faiCy2X4Ni>ml8SJj}2hu0aUnYDnM1lRt)nmLlae z8h3+`yw4V}yq?2HgL<)WA@d9^fc{ccUL*>7nf&aNfyRTx;eUb6?;{ToaEi}BQ6q=8*vXY-7_fM8Qz*x`5&9lc;-=3VC9i-&!M9aWL3(5r^pJ*XU z1QIJq31nMo2(oMGHQ4={D&wP9NjZk(G3ShzWDu9vW2ct7<@+g~GXBa*kOz^5A|oLWsQO5?xm5uksj-tXh#(IlGAmFdwo X45!H|P~Q;WKyr}ef{rH#+0KSHIz!%D&7qB1}vq~*dB(n`9Jqed!R09ixrcsBcQ2~dhQ3AK7Q3D57m+<)l zI+u`E1YwiU_6Um+*f95|hvN2$vhd0}_*8um_VKw-1*fTm*5G@JR)SSg-?!Sg-`Q ISg-}uFFKt((EtDd diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts index 2e1165035..a364fa247 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts @@ -26,6 +26,8 @@ import { existsSync } from "fs"; import { FindManyOptions, FindOptions, FindOptionsRelationByString, FindOptionsSelect, FindOptionsSelectByString, FindOptionsWhere } from "typeorm"; import * as console from "node:console"; import { randomUpperString } from "@spacebar/api"; +import { IpDataClient } from "./networking"; +import { TimeSpan } from "./Timespan"; /// Change history: /// 1 - Initial version with HS256 @@ -63,8 +65,8 @@ export const checkToken = ( ipAddress?: string; fingerprint?: string; }, -): Promise => - new Promise((resolve, reject) => { +): Promise => { + return new Promise((resolve, reject) => { token = token.replace("Bot ", ""); // there is no bot distinction in sb token = token.replace("Bearer ", ""); // allow bearer tokens @@ -77,17 +79,25 @@ export const checkToken = ( return rejectAndLog(reject, "Invalid Token meow " + err); } - const user = await User.findOne({ - where: { id: decoded.id }, - select: [...(opts?.select || []), "id", "bot", "disabled", "deleted", "rights", "data"], - relations: opts?.relations, - }); + const [user, session] = await Promise.all([ + User.findOne({ + where: { id: decoded.id }, + select: [...(opts?.select || []), "id", "bot", "disabled", "deleted", "rights", "data"], + relations: opts?.relations, + }), + decoded.did ? Session.findOne({ where: { session_id: decoded.did, user_id: decoded.id } }) : undefined, + ]); if (!user) { logAuth("validateUser rejected: User not found"); return rejectAndLog(reject, "User not found"); } + if (decoded.did && !session) { + logAuth("validateUser rejected: Session not found"); + return rejectAndLog(reject, "Invalid Token"); + } + // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds if (decoded.iat * 1000 < new Date(user.data.valid_tokens_since).setSeconds(0, 0)) { logAuth("validateUser rejected: Token not yet valid"); @@ -110,8 +120,19 @@ export const checkToken = ( return rejectAndLog(reject, "Invalid Token"); } + if (session && TimeSpan.fromDates(session.last_seen.getTime(), new Date().getTime()).totalSeconds >= 15) { + session.last_seen = new Date(); + if (opts?.ipAddress && opts?.ipAddress !== session.last_seen_ip) { + session.last_seen_ip = opts.ipAddress; + let ipInfo = await IpDataClient.getIpInfo(opts.ipAddress); + if (ipInfo?.ip) session.last_seen_location = `${ipInfo.emoji_flag} ${ipInfo.postal} ${ipInfo.city}, ${ipInfo.region}, ${ipInfo.country_name}`; + } + await session.save(); + } + const result: UserTokenData = { decoded, + session: session ?? undefined, user, // v1 can be told apart, v2 cant outside of missing device id and version tokenVersion: decoded.ver ?? legacyVersion ?? 2, @@ -136,6 +157,7 @@ export const checkToken = ( }); } else return reject("Invalid token algorithm"); }); +}; export async function generateToken(id: string, isAdminSession: boolean = false) { const iat = Math.floor(Date.now() / 1000);