diff --git a/packages/express/LICENSE b/packages/express/LICENSE new file mode 100644 index 0000000..6868a4d --- /dev/null +++ b/packages/express/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 @auth-tools + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/express/local/index.ts b/packages/express/local/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/express/package.json b/packages/express/package.json new file mode 100644 index 0000000..c593c14 --- /dev/null +++ b/packages/express/package.json @@ -0,0 +1,32 @@ +{ + "name": "@auth-tools/express", + "version": "0.0.1-alpha.2", + "description": "A structured authentication protocol for Javascript. (express)", + "main": "dist/index.js", + "repository": "https://github.com/auth-tools/auth-tools", + "author": "Laurenz Rausche ", + "license": "MIT", + "private": false, + "scripts": { + "build": "npm run build:remove && tsc", + "build:remove": "rimraf dist", + "local": "npm run build && ts-node-dev --respawn local/index.ts", + "prepublish": "npm run build", + "remove": "npm run build:remove && rimraf node_modules yarn.lock package-lock.json pnpm-lock.yaml" + }, + "dependencies": { + "express": "^4.19.2" + }, + "devDependencies": { + "@auth-tools/server": "^0.0.1-alpha.6", + "@auth-tools/express": "link:.", + "@types/express": "^4.17.21", + "rimraf": "^5.0.5", + "ts-node-dev": "^2.0.0", + "typescript": "^5.4.5" + }, + "peerDependencies": {}, + "files": [ + "dist" + ] +} diff --git a/packages/express/src/express.ts b/packages/express/src/express.ts new file mode 100644 index 0000000..f2833ca --- /dev/null +++ b/packages/express/src/express.ts @@ -0,0 +1,47 @@ +import { AuthServer } from "@auth-tools/server"; +import { RequestHandler, Router } from "express"; +import { register } from "./routes/register"; +import { login } from "./routes/login"; +import { logout } from "./routes/logout"; +import { refresh } from "./routes/refresh"; +import { check } from "./routes/check"; +import { validate } from "./middleWares/validate"; + +export function authExpress(authServer: AuthServer): { + middleWare: { validate?: RequestHandler }; + router: Router; +} { + //create router + const router = Router(); + + //add routes + if (authServer._internal.config.methods.register !== "removed") { + router.post("/register", register(authServer)); + } + + if (authServer._internal.config.methods.login !== "removed") { + router.post("/login", login(authServer)); + } + + if (authServer._internal.config.methods.logout !== "removed") { + router.post("/logout", logout(authServer)); + } + + if (authServer._internal.config.methods.refresh !== "removed") { + router.post("/refresh", refresh(authServer)); + } + + if (authServer._internal.config.methods.check !== "removed") { + router.post("/check", check(authServer)); + } + + return { + middleWare: { + validate: + authServer._internal.config.methods.validate !== "removed" + ? undefined + : validate(authServer), + }, + router, + }; +} diff --git a/packages/express/src/index.ts b/packages/express/src/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/express/src/middleWares/validate.ts b/packages/express/src/middleWares/validate.ts new file mode 100644 index 0000000..c70ef97 --- /dev/null +++ b/packages/express/src/middleWares/validate.ts @@ -0,0 +1,28 @@ +import AuthServer, { AuthServerMethod } from "@auth-tools/server"; +import { RequestHandler } from "express"; + +const HTTP_CODES = { + 1: 403, + 2: 400, + 3: 403, + 5: 500, +}; + +export function validate(authServer: AuthServer): RequestHandler { + return async (req, res, next) => { + const authHeader = req.get("authorization"); + const accessToken = authHeader && authHeader.split(" ")[1]; + + const response = await ( + authServer.methods.validate as AuthServerMethod<"validate"> + )({ accessToken }); + + if (response.error) { + return res.status(HTTP_CODES[response.codes.status]).json(response); + } + + res.locals.payload = { id: response.data.id }; + + next(); + }; +} diff --git a/packages/express/src/routes/check.ts b/packages/express/src/routes/check.ts new file mode 100644 index 0000000..d041d6e --- /dev/null +++ b/packages/express/src/routes/check.ts @@ -0,0 +1,25 @@ +import AuthServer, { AuthServerMethod } from "@auth-tools/server"; +import { RequestHandler } from "express"; + +const HTTP_CODES = { + 0: 200, + 1: 403, + 2: 400, + 3: 403, + 4: 404, + 5: 403, + 9: 403, +}; + +export function check(authServer: AuthServer): RequestHandler { + return async (req, res) => { + const response = await ( + authServer.methods.check as AuthServerMethod<"check"> + )(req.body); + + const httpCodes = + response.errorType === "server" ? 500 : HTTP_CODES[response.codes.status]; + + res.status(httpCodes).json(response); + }; +} diff --git a/packages/express/src/routes/login.ts b/packages/express/src/routes/login.ts new file mode 100644 index 0000000..d568951 --- /dev/null +++ b/packages/express/src/routes/login.ts @@ -0,0 +1,25 @@ +import AuthServer, { AuthServerMethod } from "@auth-tools/server"; +import { RequestHandler } from "express"; + +const HTTP_CODES = { + 0: 201, + 1: 403, + 2: 400, + 3: 404, + 4: 403, + 5: 403, + 9: 403, +}; + +export function login(authServer: AuthServer): RequestHandler { + return async (req, res) => { + const response = await ( + authServer.methods.login as AuthServerMethod<"login"> + )(req.body); + + const httpCodes = + response.errorType === "server" ? 500 : HTTP_CODES[response.codes.status]; + + res.status(httpCodes).json(response); + }; +} diff --git a/packages/express/src/routes/logout.ts b/packages/express/src/routes/logout.ts new file mode 100644 index 0000000..e207eb3 --- /dev/null +++ b/packages/express/src/routes/logout.ts @@ -0,0 +1,24 @@ +import AuthServer, { AuthServerMethod } from "@auth-tools/server"; +import { RequestHandler } from "express"; + +const HTTP_CODES = { + 0: 200, + 1: 403, + 2: 400, + 3: 403, + 4: 404, + 9: 403, +}; + +export function logout(authServer: AuthServer): RequestHandler { + return async (req, res) => { + const response = await ( + authServer.methods.logout as AuthServerMethod<"logout"> + )(req.body); + + const httpCodes = + response.errorType === "server" ? 500 : HTTP_CODES[response.codes.status]; + + res.status(httpCodes).json(response); + }; +} diff --git a/packages/express/src/routes/refresh.ts b/packages/express/src/routes/refresh.ts new file mode 100644 index 0000000..09086fb --- /dev/null +++ b/packages/express/src/routes/refresh.ts @@ -0,0 +1,24 @@ +import AuthServer, { AuthServerMethod } from "@auth-tools/server"; +import { RequestHandler } from "express"; + +const HTTP_CODES = { + 0: 200, + 1: 403, + 2: 400, + 3: 403, + 4: 404, + 9: 403, +}; + +export function refresh(authServer: AuthServer): RequestHandler { + return async (req, res) => { + const response = await ( + authServer.methods.refresh as AuthServerMethod<"refresh"> + )(req.body); + + const httpCodes = + response.errorType === "server" ? 500 : HTTP_CODES[response.codes.status]; + + res.status(httpCodes).json(response); + }; +} diff --git a/packages/express/src/routes/register.ts b/packages/express/src/routes/register.ts new file mode 100644 index 0000000..13e4b70 --- /dev/null +++ b/packages/express/src/routes/register.ts @@ -0,0 +1,27 @@ +import AuthServer, { AuthServerMethod } from "@auth-tools/server"; +import { RequestHandler } from "express"; + +const HTTP_CODES = { + 0: 201, + 1: 403, + 2: 400, + 3: 406, + 4: 406, + 5: 403, + 6: 403, + 7: 403, + 9: 403, +}; + +export function register(authServer: AuthServer): RequestHandler { + return async (req, res) => { + const response = await ( + authServer.methods.register as AuthServerMethod<"register"> + )(req.body); + + const httpCodes = + response.errorType === "server" ? 500 : HTTP_CODES[response.codes.status]; + + res.status(httpCodes).json(response); + }; +} diff --git a/packages/express/tsconfig.json b/packages/express/tsconfig.json new file mode 100644 index 0000000..ce1e038 --- /dev/null +++ b/packages/express/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2016", + "lib": ["ES2016"], + "module": "CommonJS", + "moduleResolution": "Node", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/logger/local/index.ts b/packages/logger/local/index.ts index a841710..e69de29 100644 --- a/packages/logger/local/index.ts +++ b/packages/logger/local/index.ts @@ -1,66 +0,0 @@ -import DefaultLogger, { COLORS, Logger } from "@auth-tools/logger"; - -DefaultLogger.log("debug", `"debug" log with DefaultLogger`); -DefaultLogger.log("info", `"info" log with DefaultLogger`); -DefaultLogger.log("warn", `"warn" log with DefaultLogger`); -DefaultLogger.log("error", `"error" log with DefaultLogger`); - -//set config after creation -DefaultLogger.setConfig({ - disableColor: true, - formatString: "Color disabled: [%l] %d %m", -}); - -//log shorthands -DefaultLogger.debug(`"debug" log with DefaultLogger and updated config`); -DefaultLogger.info(`"info" log with DefaultLogger and updated config`); -DefaultLogger.warn(`"warn" log with DefaultLogger and updated config`); -DefaultLogger.error(`"error" log with DefaultLogger and updated config`); - -//multiple arguments -DefaultLogger.log("error", `Argument 1`, "Argument 2"); - -//update config again -DefaultLogger.setConfig({ disableColor: false, formatString: "Built-In: %m" }); - -//builtin Methods -DefaultLogger.log("debug", DefaultLogger.color("Red", COLORS.error)); -DefaultLogger.log("info", DefaultLogger.twoDigits("1")); -DefaultLogger.log("warn", DefaultLogger.currentTime()); -DefaultLogger.log("error", DefaultLogger.currentDate()); - -//create custom logger instance -const logger2 = new Logger({ - disableColor: false, //set to true to disable all colors - formatString: [ - //all replacement vars: - "Vars: %t = HH:MM:SS", - "Vars: %d = YYYY-MM-DD", - "Vars: %L = LEVEL", - "Vars: %l = level", - "Vars: %m = message", - ].join("\n"), -}); - -logger2.log("error", "Hallo"); - -//update config to disable debug and warn -logger2.setConfig({ - formatString: "Disabled Methods: [%L] %t %m", - methods: { - debug: false, - warn: false, - }, -}); - -logger2.debug("Shouldn't log"); -logger2.info("Should log"); -logger2.warn("Shouldn't log"); -logger2.error("Should log"); - -//format string with current instance config -logger2.setConfig({ - formatString: "Format String: [%m]", -}); -const formatted = logger2.format("info", "MY CUSTOM STRING"); -console.log(formatted); diff --git a/packages/server/local/index.ts b/packages/server/local/index.ts index b8ce04a..e69de29 100644 --- a/packages/server/local/index.ts +++ b/packages/server/local/index.ts @@ -1,85 +0,0 @@ -import { UserData } from "@auth-tools/base"; -import { Logger } from "@auth-tools/logger"; -import { - AuthServer, - AuthServerConfig, - AuthServerMethod, -} from "@auth-tools/server"; - -const Users: UserData[] = []; -const Tokens: string[] = []; - -const logger = new Logger(); - -const authServerConfig: AuthServerConfig = { - secrets: { - accessToken: "SECRET", - refreshToken: "SECRET", - }, -}; - -const authServer = new AuthServer(authServerConfig, logger.log); - -authServer.use("getUserByMail", ({ email }) => { - const user = Users.find((usr) => usr.email === email) || null; - return { serverError: false, user }; -}); - -authServer.use("getUserByName", ({ username }) => { - const user = Users.find((usr) => usr.username === username) || null; - return { serverError: false, user }; -}); - -authServer.use("storeUser", ({ user }) => { - Users.push(user); - return { serverError: false }; -}); - -authServer.use("checkToken", ({ refreshToken }) => { - const exists = Tokens.includes(refreshToken); - return { serverError: false, exists }; -}); - -authServer.use("storeToken", ({ refreshToken }) => { - Tokens.push(refreshToken); - return { serverError: false }; -}); - -authServer.use("deleteToken", ({ refreshToken }) => { - Tokens.splice(Tokens.indexOf(refreshToken), 1); - return { serverError: false }; -}); - -authServer.use("validateMail", ({ email }) => { - const isValid = email.includes("@"); - return { serverError: false, isValid }; -}); - -authServer.use("validatePassword", ({ password }) => { - const isValid = password.length >= 8; - return { serverError: false, isValid }; -}); - -authServer.use("hashPassword", ({ password }) => { - const hashedPassword = password.split("").reverse().join(""); - return { serverError: false, hashedPassword }; -}); - -authServer.use("checkPassword", ({ password, hashedPassword }) => { - const matches = password.split("").reverse().join("") === hashedPassword; - return { serverError: false, matches }; -}); - -authServer.use("genId", ({}) => { - const id = Users.length.toString(); - return { serverError: false, id }; -}); -(async () => { - console.log( - await (authServer.methods.register as AuthServerMethod<"register">)({ - email: "as@as", - username: "as", - password: "sd", - }) - ); -})(); diff --git a/packages/server/package.json b/packages/server/package.json index 2b025f4..f0a2640 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@auth-tools/server", - "version": "0.0.1-alpha.2", + "version": "0.0.1-alpha.6", "description": "A structured authentication protocol for Javascript. (server)", "main": "dist/index.js", "repository": "https://github.com/auth-tools/auth-tools", @@ -15,7 +15,7 @@ "remove": "npm run build:remove && rimraf node_modules yarn.lock package-lock.json pnpm-lock.yaml" }, "dependencies": { - "@auth-tools/base": "^0.0.1-alpha.4", + "@auth-tools/base": "^0.0.1-alpha.8", "jsonwebtoken": "^9.0.2" }, "devDependencies": { diff --git a/packages/server/src/events.ts b/packages/server/src/events.ts index 3c991af..9665c11 100644 --- a/packages/server/src/events.ts +++ b/packages/server/src/events.ts @@ -14,7 +14,7 @@ export function undefinedUseEvent< return (() => { //complain about unset use event callback log("error", `The use "${event}" event is not defined!`); - return { ...returnData, serverError: true }; + return { ...returnData, error: true }; }) as UseEventCallbacks[Event]; } @@ -23,6 +23,6 @@ export function undefinedInterceptEvent< Event extends keyof AuthServerInterceptEvents >(): InterceptEventCallbacks[Event] { return () => { - return { serverError: false, intercepted: false, interceptCode: 0 }; + return { error: false, intercepted: false, interceptCode: 0 }; }; } diff --git a/packages/server/src/getUserByLogin.ts b/packages/server/src/getUserByLogin.ts index 2c9c891..98968d2 100644 --- a/packages/server/src/getUserByLogin.ts +++ b/packages/server/src/getUserByLogin.ts @@ -21,14 +21,14 @@ export default async function ( email: login, }); - if (getUserByMail.serverError) return { serverError: true, user: null }; + if (getUserByMail.error) return { error: true, user: null }; //get user by name with value of login const getUserByName = await internal.useEventCallbacks.getUserByName({ username: login, }); - if (getUserByName.serverError) return { serverError: true, user: null }; + if (getUserByName.error) return { error: true, user: null }; - return { serverError: false, user: getUserByMail.user || getUserByName.user }; + return { error: false, user: getUserByMail.user || getUserByName.user }; } diff --git a/packages/server/src/methods/check.ts b/packages/server/src/methods/check.ts index bc8a0dc..bb42a64 100644 --- a/packages/server/src/methods/check.ts +++ b/packages/server/src/methods/check.ts @@ -49,7 +49,7 @@ export function createCheck( refreshToken, }); - if (checkToken.serverError) return authServerError(); + if (checkToken.error) return authServerError(); if (!checkToken.exists) { internal.log("debug", 'The "refreshToken" does not exist.'); @@ -72,7 +72,7 @@ export function createCheck( payload: { id: decodeAccessToken.payload.id }, }); - if (intercept.serverError) return authServerError(); + if (intercept.error) return authServerError(); if (intercept.intercepted) return authError<"check", 9>( diff --git a/packages/server/src/methods/login.ts b/packages/server/src/methods/login.ts index a257ab1..912dee7 100644 --- a/packages/server/src/methods/login.ts +++ b/packages/server/src/methods/login.ts @@ -38,7 +38,7 @@ export function createLogin( const getUserByLoginLogin = await getUserByLogin(login, internal); - if (getUserByLoginLogin.serverError) return authServerError(); + if (getUserByLoginLogin.error) return authServerError(); if (!getUserByLoginLogin.user) { if (internal.config.sensitive.logs) @@ -60,7 +60,7 @@ export function createLogin( hashedPassword: getUserByLoginLogin.user.hashedPassword, }); - if (checkPassword.serverError) return authServerError(); + if (checkPassword.error) return authServerError(); if (!checkPassword.matches) { if (internal.config.sensitive.logs) @@ -97,7 +97,7 @@ export function createLogin( payload, }); - if (intercept.serverError) return authServerError(); + if (intercept.error) return authServerError(); if (intercept.intercepted) return authError<"login", 9>( @@ -110,7 +110,7 @@ export function createLogin( refreshToken, }); - if (storeToken.serverError) return authServerError(); + if (storeToken.error) return authServerError(); return { error: false, diff --git a/packages/server/src/methods/logout.ts b/packages/server/src/methods/logout.ts index 89ea3d0..190423c 100644 --- a/packages/server/src/methods/logout.ts +++ b/packages/server/src/methods/logout.ts @@ -43,7 +43,7 @@ export function createLogout( refreshToken, }); - if (checkToken.serverError) return authServerError(); + if (checkToken.error) return authServerError(); if (!checkToken.exists) { internal.log("debug", 'The "refreshToken" does not exist.'); @@ -55,7 +55,7 @@ export function createLogout( payload: { id: decodeRefreshToken.payload.id }, }); - if (intercept.serverError) return authServerError(); + if (intercept.error) return authServerError(); if (intercept.intercepted) return authError<"logout", 9>( @@ -68,7 +68,7 @@ export function createLogout( refreshToken, }); - if (deleteToken.serverError) return authServerError(); + if (deleteToken.error) return authServerError(); return { error: false, diff --git a/packages/server/src/methods/refresh.ts b/packages/server/src/methods/refresh.ts index 90012d2..5b982df 100644 --- a/packages/server/src/methods/refresh.ts +++ b/packages/server/src/methods/refresh.ts @@ -43,7 +43,7 @@ export function createRefresh( refreshToken, }); - if (checkToken.serverError) return authServerError(); + if (checkToken.error) return authServerError(); if (!checkToken.exists) { internal.log("debug", 'The "refreshToken" does not exist.'); @@ -55,7 +55,7 @@ export function createRefresh( payload: { id: decodeRefreshToken.payload.id }, }); - if (intercept.serverError) return authServerError(); + if (intercept.error) return authServerError(); if (intercept.intercepted) return authError<"refresh", 9>( diff --git a/packages/server/src/methods/register.ts b/packages/server/src/methods/register.ts index 8b87908..5437061 100644 --- a/packages/server/src/methods/register.ts +++ b/packages/server/src/methods/register.ts @@ -42,7 +42,7 @@ export function createRegister( email, }); - if (validateMail.serverError) return authServerError(); + if (validateMail.error) return authServerError(); if (!validateMail.isValid) { internal.log("debug", 'The "email" is malformed.'); @@ -54,7 +54,7 @@ export function createRegister( password, }); - if (validatePassword.serverError) return authServerError(); + if (validatePassword.error) return authServerError(); if (!validatePassword.isValid) { internal.log("debug", 'The "password" is too weak.'); @@ -63,7 +63,7 @@ export function createRegister( const getUserByLoginEmail = await getUserByLogin(email, internal); - if (getUserByLoginEmail.serverError) return authServerError(); + if (getUserByLoginEmail.error) return authServerError(); if (getUserByLoginEmail.user) { if (internal.config.sensitive.logs) @@ -77,7 +77,7 @@ export function createRegister( const getUserByLoginName = await getUserByLogin(username, internal); - if (getUserByLoginName.serverError) return authServerError(); + if (getUserByLoginName.error) return authServerError(); if (getUserByLoginName.user) { if (internal.config.sensitive.logs) @@ -96,14 +96,14 @@ export function createRegister( password, }); - if (hashPassword.serverError) return authServerError(); + if (hashPassword.error) return authServerError(); const genId = await internal.useEventCallbacks.genId({ email, username, }); - if (genId.serverError) return authServerError(); + if (genId.error) return authServerError(); const user: User<"id" | "email" | "username" | "hashedPassword"> = { id: genId.id, @@ -116,7 +116,7 @@ export function createRegister( user, }); - if (intercept.serverError) return authServerError(); + if (intercept.error) return authServerError(); if (intercept.intercepted) return authError<"register", 9>( @@ -127,7 +127,7 @@ export function createRegister( const storeUser = await internal.useEventCallbacks.storeUser({ user }); - if (storeUser.serverError) return authServerError(); + if (storeUser.error) return authServerError(); return { error: false,