untested WIP
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import {
|
||||
prisma,
|
||||
refreshTokenDuration,
|
||||
sessionDuration,
|
||||
accessTokenDuration,
|
||||
accessTokenPrivateKey,
|
||||
refreshTokenPrivateKey,
|
||||
} from "../constants";
|
||||
import UserController from "../controllers/UserController";
|
||||
import {
|
||||
SessionController,
|
||||
DecodedSession,
|
||||
SessionPayloadObject,
|
||||
SessionTokensObject,
|
||||
} from "../controllers/SessionController";
|
||||
import jwt from "jsonwebtoken";
|
||||
import SessionError from "../Errors/SessionError";
|
||||
import SessionTokenError from "../Errors/SessionTokenError";
|
||||
import ExpiredAccessTokenError from "../Errors/ExpiredAccessTokenError";
|
||||
import ExpiredRefreshTokenError from "../Errors/ExpiredRefreshTokenError";
|
||||
import { events } from "../modules/globalEvents";
|
||||
|
||||
export interface SessionCreationData {
|
||||
user: UserController;
|
||||
}
|
||||
|
||||
export const sessions = {
|
||||
/**
|
||||
* Create a session
|
||||
*
|
||||
* This will create a session instance in the databse that will be linked to both the session token
|
||||
* and the refresh token.
|
||||
*
|
||||
* @param data - The params needed to create the session tokens
|
||||
* @returns {Promise<SessionTokensObject>} Session Token and the Refresh Token
|
||||
*/
|
||||
async create(data: SessionCreationData): Promise<SessionTokensObject> {
|
||||
const session = await prisma.session.create({
|
||||
data: {
|
||||
expires: new Date(Date.now() + sessionDuration),
|
||||
userId: data.user.id,
|
||||
},
|
||||
});
|
||||
|
||||
const controller = new SessionController(session);
|
||||
|
||||
// Trigger Global Event
|
||||
events.emit("session:created", {
|
||||
user: data.user,
|
||||
session: controller,
|
||||
});
|
||||
|
||||
let tokens: SessionTokensObject = await controller.generateTokens();
|
||||
|
||||
return tokens;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch a session
|
||||
*
|
||||
* This method is designed to be as versitile as possible, if you are looking for a session, use this method with
|
||||
* your choice of `accessToken`, `refreshToken`, `id`, or `sessionKey`. The identifier value is a partial type.
|
||||
*
|
||||
* @param identifier - An object allowing you to put either session token, sessionKey, or id in to fetch the desired session.
|
||||
* @returns {Promise<SessionController>} The controller for the desired session.
|
||||
*/
|
||||
async fetch(
|
||||
identifier: Partial<{
|
||||
refreshToken: string;
|
||||
accessToken: string;
|
||||
id: string;
|
||||
sessionKey: string;
|
||||
}>
|
||||
) {
|
||||
if (identifier.refreshToken || identifier.accessToken) {
|
||||
const decodedJWT = identifier.refreshToken
|
||||
? ((await new Promise((res, rej) =>
|
||||
jwt.verify(
|
||||
identifier.refreshToken!,
|
||||
refreshTokenPrivateKey,
|
||||
{
|
||||
algorithms: ["RS256"],
|
||||
},
|
||||
async (err, decode) => {
|
||||
if (
|
||||
err &&
|
||||
(err.name == "TokenExpiredError" ||
|
||||
err.message == "invalid signature")
|
||||
) {
|
||||
let sessionDat = await prisma.session.findFirst({
|
||||
where: {
|
||||
sessionKey: (
|
||||
jwt.decode(identifier.refreshToken!) as DecodedSession
|
||||
).sessionKey,
|
||||
},
|
||||
});
|
||||
|
||||
if (!sessionDat)
|
||||
return rej(new SessionError("Invalid session."));
|
||||
let session = new SessionController(sessionDat);
|
||||
|
||||
await session.terminate();
|
||||
if (err.message == "invalid signature")
|
||||
return rej(new SessionError("Invalid session."));
|
||||
|
||||
return rej(new ExpiredRefreshTokenError("It epired."));
|
||||
}
|
||||
if (err) return rej(err);
|
||||
|
||||
return res(decode as DecodedSession);
|
||||
}
|
||||
)
|
||||
)) as DecodedSession)
|
||||
: ((await new Promise((res, rej) =>
|
||||
jwt.verify(
|
||||
identifier.accessToken!,
|
||||
accessTokenPrivateKey,
|
||||
{
|
||||
algorithms: ["RS256"],
|
||||
},
|
||||
(err, decode) => {
|
||||
if (err && err.name == "TokenExpiredError")
|
||||
return rej(new ExpiredAccessTokenError());
|
||||
if (err) return rej(err);
|
||||
return res(decode as DecodedSession);
|
||||
}
|
||||
)
|
||||
)) as DecodedSession);
|
||||
|
||||
const sessionData = await prisma.session.findFirst({
|
||||
where: { sessionKey: decodedJWT.sessionKey },
|
||||
});
|
||||
|
||||
if (!sessionData) throw new SessionError("Invalid Session");
|
||||
if (identifier.accessToken && decodedJWT.exp > Date.now())
|
||||
throw new ExpiredAccessTokenError();
|
||||
|
||||
if (identifier.refreshToken && decodedJWT.exp > Date.now()) {
|
||||
let sess = new SessionController(sessionData);
|
||||
await sess.terminate();
|
||||
throw new SessionError("Invalid Session...", "Expired Refresh Token");
|
||||
}
|
||||
|
||||
return new SessionController(sessionData);
|
||||
}
|
||||
|
||||
const sessionData = await prisma.session.findFirst({
|
||||
where: { OR: [{ sessionKey: identifier.sessionKey, id: identifier.id }] },
|
||||
});
|
||||
|
||||
if (!sessionData) throw new SessionError("Invalid Session");
|
||||
|
||||
return new SessionController(sessionData);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @TODO As a consequence of the above, need to setup pgBoss for cron and event loop.
|
||||
*/
|
||||
Reference in New Issue
Block a user