11import axios from "axios" ;
22import bcrypt from "bcrypt" ;
3+ import crypto from "crypto" ;
34import { FastifyInstance , FastifyReply , FastifyRequest } from "fastify" ;
45import jwt from "jsonwebtoken" ;
56import { LRUCache } from "lru-cache" ;
@@ -8,6 +9,7 @@ import { AuthorizationCode } from "simple-oauth2";
89import { getOAuthProvider , getOidcConfig } from "../lib/auth" ;
910import { track } from "../lib/hog" ;
1011import { forgotPassword } from "../lib/nodemailer/auth/forgot-password" ;
12+ import { requirePermission } from "../lib/roles" ;
1113import { checkSession } from "../lib/session" ;
1214import { getOAuthClient } from "../lib/utils/oauth_client" ;
1315import { getOidcClient } from "../lib/utils/oidc_client" ;
@@ -320,22 +322,31 @@ export function authRoutes(fastify: FastifyInstance) {
320322 throw new Error ( "Password is not valid" ) ;
321323 }
322324
323- var b64string = process . env . SECRET ;
324- var buf = new Buffer ( b64string ! , "base64" ) ; // Ta-da
325-
326- let token = jwt . sign (
325+ // Generate a secure session token
326+ var secret = Buffer . from ( process . env . SECRET ! , "base64" ) ;
327+ const token = jwt . sign (
327328 {
328- data : { id : user ! . id } ,
329+ data : {
330+ id : user ! . id ,
331+ // Add a unique identifier for this session
332+ sessionId : crypto . randomBytes ( 32 ) . toString ( "hex" ) ,
333+ } ,
329334 } ,
330- buf ,
331- { expiresIn : "7d" }
335+ secret ,
336+ {
337+ expiresIn : "8h" ,
338+ algorithm : "HS256" ,
339+ }
332340 ) ;
333341
342+ // Store session with additional security info
334343 await prisma . session . create ( {
335344 data : {
336345 userId : user ! . id ,
337346 sessionToken : token ,
338- expires : new Date ( Date . now ( ) + 60 * 60 * 1000 ) ,
347+ expires : new Date ( Date . now ( ) + 8 * 60 * 60 * 1000 ) , // 8 hours
348+ userAgent : request . headers [ "user-agent" ] || "" ,
349+ ipAddress : request . ip ,
339350 } ,
340351 } ) ;
341352
@@ -687,6 +698,9 @@ export function authRoutes(fastify: FastifyInstance) {
687698 // Delete a user
688699 fastify . delete (
689700 "/api/v1/auth/user/:id" ,
701+ {
702+ preHandler : requirePermission ( [ "user::delete" ] ) ,
703+ } ,
690704 async ( request : FastifyRequest , reply : FastifyReply ) => {
691705 const { id } = request . params as { id : string } ;
692706
@@ -763,18 +777,12 @@ export function authRoutes(fastify: FastifyInstance) {
763777 password : string ;
764778 } ;
765779
766- const bearer = request . headers . authorization ! . split ( " " ) [ 1 ] ;
767-
768- let session = await prisma . session . findUnique ( {
769- where : {
770- sessionToken : bearer ,
771- } ,
772- } ) ;
780+ const session = await checkSession ( request ) ;
773781
774782 const hashedPass = await bcrypt . hash ( password , 10 ) ;
775783
776784 await prisma . user . update ( {
777- where : { id : session ?. userId } ,
785+ where : { id : session ?. id } ,
778786 data : {
779787 password : hashedPass ,
780788 } ,
@@ -789,6 +797,9 @@ export function authRoutes(fastify: FastifyInstance) {
789797 // Reset password by admin
790798 fastify . post (
791799 "/api/v1/auth/admin/reset-password" ,
800+ {
801+ preHandler : requirePermission ( [ "user::manage" ] ) ,
802+ } ,
792803 async ( request : FastifyRequest , reply : FastifyReply ) => {
793804 let { password, user } = request . body as {
794805 password : string ;
@@ -830,14 +841,11 @@ export function authRoutes(fastify: FastifyInstance) {
830841 // Update a users profile/config
831842 fastify . put (
832843 "/api/v1/auth/profile" ,
844+ {
845+ preHandler : requirePermission ( [ "user::update" ] ) ,
846+ } ,
833847 async ( request : FastifyRequest , reply : FastifyReply ) => {
834- const bearer = request . headers . authorization ! . split ( " " ) [ 1 ] ;
835-
836- let session = await prisma . session . findUnique ( {
837- where : {
838- sessionToken : bearer ,
839- } ,
840- } ) ;
848+ const session = await checkSession ( request ) ;
841849
842850 const { name, email, language } = request . body as {
843851 name : string ;
@@ -846,7 +854,7 @@ export function authRoutes(fastify: FastifyInstance) {
846854 } ;
847855
848856 let user = await prisma . user . update ( {
849- where : { id : session ?. userId } ,
857+ where : { id : session ?. id } ,
850858 data : {
851859 name : name ,
852860 email : email ,
@@ -863,13 +871,11 @@ export function authRoutes(fastify: FastifyInstance) {
863871 // Update a users Email notification settings
864872 fastify . put (
865873 "/api/v1/auth/profile/notifcations/emails" ,
874+ {
875+ preHandler : requirePermission ( [ "user::update" ] ) ,
876+ } ,
866877 async ( request : FastifyRequest , reply : FastifyReply ) => {
867- const bearer = request . headers . authorization ! . split ( " " ) [ 1 ] ;
868- let session = await prisma . session . findUnique ( {
869- where : {
870- sessionToken : bearer ,
871- } ,
872- } ) ;
878+ const session = await checkSession ( request ) ;
873879
874880 const {
875881 notify_ticket_created,
@@ -879,7 +885,7 @@ export function authRoutes(fastify: FastifyInstance) {
879885 } = request . body as any ;
880886
881887 let user = await prisma . user . update ( {
882- where : { id : session ?. userId } ,
888+ where : { id : session ?. id } ,
883889 data : {
884890 notify_ticket_created : notify_ticket_created ,
885891 notify_ticket_assigned : notify_ticket_assigned ,
@@ -911,29 +917,40 @@ export function authRoutes(fastify: FastifyInstance) {
911917 // Update a users role
912918 fastify . put (
913919 "/api/v1/auth/user/role" ,
920+ {
921+ preHandler : requirePermission ( [ "user::manage" ] ) ,
922+ } ,
914923 async ( request : FastifyRequest , reply : FastifyReply ) => {
915- const { id, role } = request . body as { id : string ; role : boolean } ;
916- // check for atleast one admin on role downgrade
917- if ( role === false ) {
918- const admins = await prisma . user . findMany ( {
919- where : { isAdmin : true } ,
920- } ) ;
921- if ( admins . length === 1 ) {
922- reply . code ( 400 ) . send ( {
923- message : "Atleast one admin is required" ,
924- success : false ,
924+ const session = await checkSession ( request ) ;
925+
926+ if ( session ?. isAdmin ) {
927+ const { id, role } = request . body as { id : string ; role : boolean } ;
928+ if ( role === false ) {
929+ const admins = await prisma . user . findMany ( {
930+ where : { isAdmin : true } ,
925931 } ) ;
926- return ;
932+ if ( admins . length === 1 ) {
933+ reply . code ( 400 ) . send ( {
934+ message : "Atleast one admin is required" ,
935+ success : false ,
936+ } ) ;
937+ return ;
938+ }
927939 }
928- }
929- await prisma . user . update ( {
930- where : { id } ,
931- data : {
932- isAdmin : role ,
933- } ,
934- } ) ;
940+ await prisma . user . update ( {
941+ where : { id } ,
942+ data : {
943+ isAdmin : role ,
944+ } ,
945+ } ) ;
935946
936- reply . send ( { success : true } ) ;
947+ reply . send ( { success : true } ) ;
948+ } else {
949+ reply . code ( 401 ) . send ( {
950+ message : "Unauthorized" ,
951+ success : false ,
952+ } ) ;
953+ }
937954 }
938955 ) ;
939956
@@ -955,4 +972,59 @@ export function authRoutes(fastify: FastifyInstance) {
955972 reply . send ( { success : true } ) ;
956973 }
957974 ) ;
975+
976+ // Add a new endpoint to list and manage active sessions
977+ fastify . get (
978+ "/api/v1/auth/sessions" ,
979+ async ( request : FastifyRequest , reply : FastifyReply ) => {
980+ const currentUser = await checkSession ( request ) ;
981+ if ( ! currentUser ) {
982+ return reply . code ( 401 ) . send ( { message : "Unauthorized" } ) ;
983+ }
984+
985+ const sessions = await prisma . session . findMany ( {
986+ where : { userId : currentUser . id } ,
987+ select : {
988+ id : true ,
989+ userAgent : true ,
990+ ipAddress : true ,
991+ createdAt : true ,
992+ expires : true ,
993+ } ,
994+ } ) ;
995+
996+ reply . send ( { sessions } ) ;
997+ }
998+ ) ;
999+
1000+ // Add ability to revoke specific sessions
1001+ fastify . delete (
1002+ "/api/v1/auth/sessions/:sessionId" ,
1003+ async ( request : FastifyRequest , reply : FastifyReply ) => {
1004+ const currentUser = await checkSession ( request ) ;
1005+ if ( ! currentUser ) {
1006+ return reply . code ( 401 ) . send ( { message : "Unauthorized" } ) ;
1007+ }
1008+
1009+ const { sessionId } = request . params as { sessionId : string } ;
1010+
1011+ // Only allow users to delete their own sessions
1012+ const session = await prisma . session . findFirst ( {
1013+ where : {
1014+ id : sessionId ,
1015+ userId : currentUser . id ,
1016+ } ,
1017+ } ) ;
1018+
1019+ if ( ! session ) {
1020+ return reply . code ( 404 ) . send ( { message : "Session not found" } ) ;
1021+ }
1022+
1023+ await prisma . session . delete ( {
1024+ where : { id : sessionId } ,
1025+ } ) ;
1026+
1027+ reply . send ( { success : true } ) ;
1028+ }
1029+ ) ;
9581030}
0 commit comments