Updating dependencies and switching to ESModules where possible
Some checks failed
CI / build (push) Has been cancelled

This commit is contained in:
Fabian Stamm
2025-09-15 22:04:57 +02:00
parent 8135190cd8
commit c6158fe2e2
66 changed files with 4540 additions and 3752 deletions

View File

@ -1,9 +1,12 @@
nodeLinker: node-modules
npmRegistryServer: "https://npm.hibas123.de"
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.5.0.cjs
nodeLinker: node-modules
npmRegistryServer: "https://npm.hibas123.de"
npmScopes:
"hibas123":
npmRegistryServer: "https://git.hibas.dev/api/packages/hibas123/npm/"
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.5.0.cjs

View File

@ -3,12 +3,15 @@
"main": "lib/index.js",
"author": "Fabian Stamm <dev@fabianstamm.de>",
"license": "MIT",
"type": "module",
"scripts": {
"build": "run-s build-ts build-doc",
"build-doc": "apidoc -i src/ -p apidoc/",
"build-ts": "tsc",
"start": "node lib/index.js",
"dev": "nodemon -e ts --exec ts-node src/index.ts",
"dev:js": "nodemon lib/index.ts",
"dev:ts": "tsc --watch",
"dev": "concurrently 'yarn run dev:js' 'yarn run dev:ts'",
"format": "prettier --write \"src/**\""
},
"pipelines": {
@ -20,58 +23,59 @@
]
},
"devDependencies": {
"@types/body-parser": "^1.19.2",
"@types/compression": "^1.7.2",
"@types/cookie-parser": "^1.4.3",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.17",
"@types/express-session": "^1.17.7",
"@types/i18n": "^0.13.6",
"@types/ini": "^1.3.31",
"@types/jsonwebtoken": "^9.0.1",
"@types/body-parser": "^1.19.6",
"@types/compression": "^1.8.1",
"@types/cookie-parser": "^1.4.9",
"@types/dotenv": "^8.2.3",
"@types/express": "^5.0.3",
"@types/express-serve-static-core": "^5.0.7",
"@types/express-session": "^1.18.2",
"@types/i18n": "^0.13.12",
"@types/ini": "^4.1.1",
"@types/jsonwebtoken": "^9.0.10",
"@types/mongodb": "^4.0.7",
"@types/node": "^18.15.11",
"@types/node-rsa": "^1.1.1",
"@types/qrcode": "^1.5.0",
"@types/speakeasy": "^2.0.7",
"@types/uuid": "^9.0.1",
"apidoc": "^0.54.0",
"concurrently": "^8.2.2",
"nodemon": "^3.0.1",
"prettier": "^2.8.7",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
"@types/node": "^24.4.0",
"@types/node-rsa": "^1.1.4",
"@types/qrcode": "^1.5.5",
"@types/speakeasy": "^2.0.10",
"@types/uuid": "^10.0.0",
"apidoc": "^1.2.0",
"concurrently": "^9.2.1",
"nodemon": "^3.1.10",
"prettier": "^3.6.2",
"ts-node": "^10.9.2",
"typescript": "^5.9.2"
},
"dependencies": {
"@hibas123/config": "^1.1.2",
"@hibas123/nodelogging": "^3.1.3",
"@hibas123/nodelogging": "^4.0.0",
"@hibas123/nodeloggingserver_client": "^1.1.2",
"@hibas123/openauth-internalapi": "workspace:^",
"@hibas123/openauth-views-v1": "workspace:^",
"@hibas123/safe_mongo": "2.0.1",
"@simplewebauthn/server": "^7.2.0",
"body-parser": "^1.20.2",
"compression": "^1.7.4",
"connect-mongo": "^5.0.0",
"cookie-parser": "^1.4.6",
"@hibas123/safe_mongo": "2.1.0",
"@simplewebauthn/server": "^13.2.0",
"body-parser": "^2.2.0",
"compression": "^1.8.1",
"connect-mongo": "^5.1.0",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"express-session": "^1.17.3",
"handlebars": "^4.7.7",
"dotenv": "^17.2.2",
"express": "^5.1.0",
"express-session": "^1.18.2",
"handlebars": "^4.7.8",
"i18n": "^0.15.1",
"ini": "^4.1.1",
"joi": "^17.11.0",
"jsonwebtoken": "^9.0.0",
"moment": "^2.29.4",
"mongodb": "^5.2.0",
"ini": "^5.0.0",
"joi": "^18.0.1",
"jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"mongodb": "^6.19.0",
"node-rsa": "^1.1.1",
"npm-run-all": "^4.1.5",
"qrcode": "^1.5.3",
"reflect-metadata": "^0.1.13",
"qrcode": "^1.5.4",
"reflect-metadata": "^0.2.2",
"speakeasy": "^2.0.0",
"u2f": "^0.1.3",
"uuid": "^9.0.1"
"uuid": "^13.0.0"
},
"packageManager": "yarn@3.5.0"
}

View File

@ -1,191 +1,191 @@
import { Router, Request } from "express";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import promiseMiddleware from "../../helper/promiseMiddleware";
import Client from "../../models/client";
import verify, { Types } from "../middlewares/verify";
import { randomBytes } from "crypto";
const ClientRouter: Router = Router();
ClientRouter.route("/")
/**
* @api {get} /admin/client
* @apiName AdminGetClients
*
* @apiGroup admin_client
* @apiPermission admin
*
* @apiSuccess {Object[]} clients
* @apiSuccess {String} clients._id The internally used id
* @apiSuccess {String} clients.maintainer
* @apiSuccess {Boolean} clients.internal
* @apiSuccess {String} clients.name
* @apiSuccess {String} clients.redirect_url
* @apiSuccess {String} clients.website
* @apiSuccess {String} clients.logo
* @apiSuccess {String} clients.client_id Client ID used outside of DB
* @apiSuccess {String} clients.client_secret
*/
.get(
promiseMiddleware(async (req, res) => {
let clients = await Client.find({});
//ToDo check if user is required!
res.json(clients);
})
)
/**
* @api {get} /admin/client
* @apiName AdminAddClients
*
* @apiGroup admin_client
* @apiPermission admin
*
* @apiParam {Boolean} internal Is it an internal app
* @apiParam {String} name
* @apiParam {String} redirect_url
* @apiParam {String} website
* @apiParam {String} logo
*
* @apiSuccess {Object[]} clients
* @apiSuccess {String} clients._id The internally used id
* @apiSuccess {String} clients.maintainer
* @apiSuccess {Boolean} clients.internal
* @apiSuccess {String} clients.name
* @apiSuccess {String} clients.redirect_url
* @apiSuccess {String} clients.website
* @apiSuccess {String} clients.logo
* @apiSuccess {String} clients.client_id Client ID used outside of DB
* @apiSuccess {String} clients.client_secret
*/
.post(
verify(
{
internal: {
type: Types.BOOLEAN,
optional: true,
},
name: {
type: Types.STRING,
},
redirect_url: {
type: Types.STRING,
},
website: {
type: Types.STRING,
},
logo: {
type: Types.STRING,
optional: true,
},
featured: {
type: Types.BOOLEAN,
optional: true,
},
description: {
type: Types.STRING,
optional: true,
},
},
true
),
promiseMiddleware(async (req, res) => {
req.body.client_secret = randomBytes(32).toString("hex");
let client = Client.new(req.body);
client.maintainer = req.user._id;
await Client.save(client);
res.json(client);
})
);
ClientRouter.route("/:id")
/**
* @api {delete} /admin/client/:id
* @apiParam {String} id Client _id
* @apiName AdminDeleteClient
*
* @apiGroup admin_client
* @apiPermission admin
*
* @apiSuccess {Boolean} success
*/
.delete(
promiseMiddleware(async (req, res) => {
let { id } = req.params;
await Client.delete(id);
res.json({ success: true });
})
)
/**
* @api {put} /admin/client/:id
* @apiParam {String} id Client _id
* @apiName AdminUpdateClient
*
* @apiGroup admin_client
* @apiPermission admin
*
* @apiParam {Boolean} internal Is it an internal app
* @apiParam {String} name
* @apiParam {String} redirect_url
* @apiParam {String} website
* @apiParam {String} logo
*
* @apiSuccess {String} _id The internally used id
* @apiSuccess {String} maintainer UserID of client maintainer
* @apiSuccess {Boolean} internal Defines if it is a internal client
* @apiSuccess {String} name The name of the Client
* @apiSuccess {String} redirect_url Redirect URL after login
* @apiSuccess {String} website Website of Client
* @apiSuccess {String} logo The Logo of the Client (optional)
* @apiSuccess {String} client_id Client ID used outside of DB
* @apiSuccess {String} client_secret The client secret, that can be used to obtain token
*/
.put(
verify(
{
internal: {
type: Types.BOOLEAN,
optional: true,
},
name: {
type: Types.STRING,
optional: true,
},
redirect_url: {
type: Types.STRING,
optional: true,
},
website: {
type: Types.STRING,
optional: true,
},
logo: {
type: Types.STRING,
optional: true,
},
featured: {
type: Types.BOOLEAN,
optional: true,
},
description: {
type: Types.STRING,
optional: true,
},
},
true
),
promiseMiddleware(async (req, res) => {
let { id } = req.query as { [key: string]: string };
let client = await Client.findById(id);
if (!client)
throw new RequestError(
req.__("Client not found"),
HttpStatusCode.BAD_REQUEST
);
for (let key in req.body) {
client[key] = req.body[key];
}
await Client.save(client);
res.json(client);
})
);
export default ClientRouter;
import { Router, Request } from "express";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
import Client from "../../models/client.js";
import verify, { Types } from "../middlewares/verify.js";
import { randomBytes } from "crypto";
const ClientRouter: Router = Router();
ClientRouter.route("/")
/**
* @api {get} /admin/client
* @apiName AdminGetClients
*
* @apiGroup admin_client
* @apiPermission admin
*
* @apiSuccess {Object[]} clients
* @apiSuccess {String} clients._id The internally used id
* @apiSuccess {String} clients.maintainer
* @apiSuccess {Boolean} clients.internal
* @apiSuccess {String} clients.name
* @apiSuccess {String} clients.redirect_url
* @apiSuccess {String} clients.website
* @apiSuccess {String} clients.logo
* @apiSuccess {String} clients.client_id Client ID used outside of DB
* @apiSuccess {String} clients.client_secret
*/
.get(
promiseMiddleware(async (req, res) => {
let clients = await Client.find({});
//ToDo check if user is required!
res.json(clients);
})
)
/**
* @api {get} /admin/client
* @apiName AdminAddClients
*
* @apiGroup admin_client
* @apiPermission admin
*
* @apiParam {Boolean} internal Is it an internal app
* @apiParam {String} name
* @apiParam {String} redirect_url
* @apiParam {String} website
* @apiParam {String} logo
*
* @apiSuccess {Object[]} clients
* @apiSuccess {String} clients._id The internally used id
* @apiSuccess {String} clients.maintainer
* @apiSuccess {Boolean} clients.internal
* @apiSuccess {String} clients.name
* @apiSuccess {String} clients.redirect_url
* @apiSuccess {String} clients.website
* @apiSuccess {String} clients.logo
* @apiSuccess {String} clients.client_id Client ID used outside of DB
* @apiSuccess {String} clients.client_secret
*/
.post(
verify(
{
internal: {
type: Types.BOOLEAN,
optional: true,
},
name: {
type: Types.STRING,
},
redirect_url: {
type: Types.STRING,
},
website: {
type: Types.STRING,
},
logo: {
type: Types.STRING,
optional: true,
},
featured: {
type: Types.BOOLEAN,
optional: true,
},
description: {
type: Types.STRING,
optional: true,
},
},
true
),
promiseMiddleware(async (req, res) => {
req.body.client_secret = randomBytes(32).toString("hex");
let client = Client.new(req.body);
client.maintainer = req.user._id;
await Client.save(client);
res.json(client);
})
);
ClientRouter.route("/:id")
/**
* @api {delete} /admin/client/:id
* @apiParam {String} id Client _id
* @apiName AdminDeleteClient
*
* @apiGroup admin_client
* @apiPermission admin
*
* @apiSuccess {Boolean} success
*/
.delete(
promiseMiddleware(async (req, res) => {
let { id } = req.params;
await Client.delete(id);
res.json({ success: true });
})
)
/**
* @api {put} /admin/client/:id
* @apiParam {String} id Client _id
* @apiName AdminUpdateClient
*
* @apiGroup admin_client
* @apiPermission admin
*
* @apiParam {Boolean} internal Is it an internal app
* @apiParam {String} name
* @apiParam {String} redirect_url
* @apiParam {String} website
* @apiParam {String} logo
*
* @apiSuccess {String} _id The internally used id
* @apiSuccess {String} maintainer UserID of client maintainer
* @apiSuccess {Boolean} internal Defines if it is a internal client
* @apiSuccess {String} name The name of the Client
* @apiSuccess {String} redirect_url Redirect URL after login
* @apiSuccess {String} website Website of Client
* @apiSuccess {String} logo The Logo of the Client (optional)
* @apiSuccess {String} client_id Client ID used outside of DB
* @apiSuccess {String} client_secret The client secret, that can be used to obtain token
*/
.put(
verify(
{
internal: {
type: Types.BOOLEAN,
optional: true,
},
name: {
type: Types.STRING,
optional: true,
},
redirect_url: {
type: Types.STRING,
optional: true,
},
website: {
type: Types.STRING,
optional: true,
},
logo: {
type: Types.STRING,
optional: true,
},
featured: {
type: Types.BOOLEAN,
optional: true,
},
description: {
type: Types.STRING,
optional: true,
},
},
true
),
promiseMiddleware(async (req, res) => {
let { id } = req.query as { [key: string]: string };
let client = await Client.findById(id);
if (!client)
throw new RequestError(
req.__("Client not found"),
HttpStatusCode.BAD_REQUEST
);
for (let key in req.body) {
client[key] = req.body[key];
}
await Client.save(client);
res.json(client);
})
);
export default ClientRouter;

View File

@ -1,24 +1,24 @@
import { Request, Router } from "express";
import ClientRoute from "./client";
import UserRoute from "./user";
import RegCodeRoute from "./regcode";
import PermissionRoute from "./permission";
import { GetUserMiddleware } from "../middlewares/user";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
const AdminRoute: Router = Router();
AdminRoute.use(GetUserMiddleware(true, true), (req: Request, res, next) => {
if (!req.isAdmin)
throw new RequestError(
"You have no permission to access this API",
HttpStatusCode.FORBIDDEN
);
else next();
});
AdminRoute.use("/client", ClientRoute);
AdminRoute.use("/regcode", RegCodeRoute);
AdminRoute.use("/user", UserRoute);
AdminRoute.use("/permission", PermissionRoute);
export default AdminRoute;
import { Request, Router } from "express";
import ClientRoute from "./client.js";
import UserRoute from "./user.js";
import RegCodeRoute from "./regcode.js";
import PermissionRoute from "./permission.js";
import { GetUserMiddleware } from "../middlewares/user.js";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
const AdminRoute: Router = Router();
AdminRoute.use(GetUserMiddleware(true, true), (req: Request, res, next) => {
if (!req.isAdmin)
throw new RequestError(
"You have no permission to access this API",
HttpStatusCode.FORBIDDEN
);
else next();
});
AdminRoute.use("/client", ClientRoute);
AdminRoute.use("/regcode", RegCodeRoute);
AdminRoute.use("/user", UserRoute);
AdminRoute.use("/permission", PermissionRoute);
export default AdminRoute;

View File

@ -1,10 +1,9 @@
import { Request, Router } from "express";
import { GetUserMiddleware } from "../middlewares/user";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import promiseMiddleware from "../../helper/promiseMiddleware";
import Permission from "../../models/permissions";
import verify, { Types } from "../middlewares/verify";
import Client from "../../models/client";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
import Permission from "../../models/permissions.js";
import verify, { Types } from "../middlewares/verify.js";
import Client from "../../models/client.js";
import { ObjectId } from "bson";
const PermissionRoute: Router = Router();

View File

@ -1,69 +1,67 @@
import { Request, Router } from "express";
import promiseMiddleware from "../../helper/promiseMiddleware";
import RegCode from "../../models/regcodes";
import { randomBytes } from "crypto";
import moment = require("moment");
import { GetUserMiddleware } from "../middlewares/user";
import { HttpStatusCode } from "../../helper/request_error";
const RegCodeRoute: Router = Router();
RegCodeRoute.route("/")
/**
* @api {get} /admin/regcode
* @apiName AdminGetRegcodes
*
* @apiGroup admin_regcode
* @apiPermission admin
*
* @apiSuccess {Object[]} regcodes
* @apiSuccess {String} permissions._id The ID
* @apiSuccess {String} permissions.token The Regcode Token
* @apiSuccess {String} permissions.valid Defines if the Regcode is valid
* @apiSuccess {String} permissions.validTill Expiration date of RegCode
*/
.get(
promiseMiddleware(async (req, res) => {
let regcodes = await RegCode.find({});
res.json(regcodes);
})
)
/**
* @api {delete} /admin/regcode
* @apiName AdminDeleteRegcode
*
* @apiParam {String} id The id of the RegCode
*
* @apiGroup admin_regcode
* @apiPermission admin
*
* @apiSuccess {Boolean} success
*/
.delete(
promiseMiddleware(async (req, res) => {
let { id } = req.query as { [key: string]: string };
await RegCode.delete(id);
res.json({ success: true });
})
)
/**
* @api {post} /admin/regcode
* @apiName AdminAddRegcode
*
* @apiGroup admin_regcode
* @apiPermission admin
*
* @apiSuccess {String} code The newly created code
*/
.post(
promiseMiddleware(async (req, res) => {
let regcode = RegCode.new({
token: randomBytes(10).toString("hex"),
valid: true,
validTill: moment().add("1", "month").toDate(),
});
await RegCode.save(regcode);
res.json({ code: regcode.token });
})
);
export default RegCodeRoute;
import { Request, Router } from "express";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
import RegCode from "../../models/regcodes.js";
import { randomBytes } from "crypto";
import moment = require("moment");
const RegCodeRoute: Router = Router();
RegCodeRoute.route("/")
/**
* @api {get} /admin/regcode
* @apiName AdminGetRegcodes
*
* @apiGroup admin_regcode
* @apiPermission admin
*
* @apiSuccess {Object[]} regcodes
* @apiSuccess {String} permissions._id The ID
* @apiSuccess {String} permissions.token The Regcode Token
* @apiSuccess {String} permissions.valid Defines if the Regcode is valid
* @apiSuccess {String} permissions.validTill Expiration date of RegCode
*/
.get(
promiseMiddleware(async (req, res) => {
let regcodes = await RegCode.find({});
res.json(regcodes);
})
)
/**
* @api {delete} /admin/regcode
* @apiName AdminDeleteRegcode
*
* @apiParam {String} id The id of the RegCode
*
* @apiGroup admin_regcode
* @apiPermission admin
*
* @apiSuccess {Boolean} success
*/
.delete(
promiseMiddleware(async (req, res) => {
let { id } = req.query as { [key: string]: string };
await RegCode.delete(id);
res.json({ success: true });
})
)
/**
* @api {post} /admin/regcode
* @apiName AdminAddRegcode
*
* @apiGroup admin_regcode
* @apiPermission admin
*
* @apiSuccess {String} code The newly created code
*/
.post(
promiseMiddleware(async (req, res) => {
let regcode = RegCode.new({
token: randomBytes(10).toString("hex"),
valid: true,
validTill: moment().add("1", "month").toDate(),
});
await RegCode.save(regcode);
res.json({ code: regcode.token });
})
);
export default RegCodeRoute;

View File

@ -1,93 +1,93 @@
import { Request, Router } from "express";
import { GetUserMiddleware } from "../middlewares/user";
import { HttpStatusCode } from "../../helper/request_error";
import promiseMiddleware from "../../helper/promiseMiddleware";
import User from "../../models/user";
import Mail from "../../models/mail";
import RefreshToken from "../../models/refresh_token";
import LoginToken from "../../models/login_token";
const UserRoute: Router = Router();
UserRoute.use(GetUserMiddleware(true, true), (req: Request, res, next) => {
if (!req.isAdmin) res.sendStatus(HttpStatusCode.FORBIDDEN);
else next();
});
UserRoute.route("/")
/**
* @api {get} /admin/user
* @apiName AdminGetUsers
*
* @apiGroup admin_user
* @apiPermission admin
* @apiSuccess {Object[]} user
* @apiSuccess {String} user._id The internal id of the user
* @apiSuccess {String} user.uid The public UID of the user
* @apiSuccess {String} user.username The username
* @apiSuccess {String} user.name The real name
* @apiSuccess {Date} user.birthday The birthday
* @apiSuccess {Number} user.gender 0 = none, 1 = male, 2 = female, 3 = other
* @apiSuccess {Boolean} user.admin Is admin or not
*/
.get(
promiseMiddleware(async (req, res) => {
let users = await User.find({});
users.forEach(
(e) => delete e.password && delete e.salt && delete e.encryption_key
);
res.json(users);
})
)
/**
* @api {delete} /admin/user
* @apiName AdminDeleteUser
*
* @apiParam {String} id The User ID
*
* @apiGroup admin_user
* @apiPermission admin
*
* @apiSuccess {Boolean} success
*/
.delete(
promiseMiddleware(async (req, res) => {
let { id } = req.query as { [key: string]: string };
let user = await User.findById(id);
await Promise.all([
user.mails.map((mail) => Mail.delete(mail)),
[
RefreshToken.deleteFilter({ user: user._id }),
LoginToken.deleteFilter({ user: user._id }),
],
]);
await User.delete(user);
res.json({ success: true });
})
)
/**
* @api {put} /admin/user
* @apiName AdminChangeUser
*
* @apiParam {String} id The User ID
*
* @apiGroup admin_user
* @apiPermission admin
*
* @apiSuccess {Boolean} success
*
* @apiDescription Flipps the user role:
* admin -> user
* user -> admin
*/
.put(
promiseMiddleware(async (req, res) => {
let { id } = req.query as { [key: string]: string };
let user = await User.findById(id);
user.admin = !user.admin;
await User.save(user);
res.json({ success: true });
})
);
export default UserRoute;
import { Request, Router } from "express";
import { GetUserMiddleware } from "../middlewares/user.js";
import { HttpStatusCode } from "../../helper/request_error.js";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
import User from "../../models/user.js";
import Mail from "../../models/mail.js";
import RefreshToken from "../../models/refresh_token.js";
import LoginToken from "../../models/login_token.js";
const UserRoute: Router = Router();
UserRoute.use(GetUserMiddleware(true, true), (req: Request, res, next) => {
if (!req.isAdmin) res.sendStatus(HttpStatusCode.FORBIDDEN);
else next();
});
UserRoute.route("/")
/**
* @api {get} /admin/user
* @apiName AdminGetUsers
*
* @apiGroup admin_user
* @apiPermission admin
* @apiSuccess {Object[]} user
* @apiSuccess {String} user._id The internal id of the user
* @apiSuccess {String} user.uid The public UID of the user
* @apiSuccess {String} user.username The username
* @apiSuccess {String} user.name The real name
* @apiSuccess {Date} user.birthday The birthday
* @apiSuccess {Number} user.gender 0 = none, 1 = male, 2 = female, 3 = other
* @apiSuccess {Boolean} user.admin Is admin or not
*/
.get(
promiseMiddleware(async (req, res) => {
let users = await User.find({});
users.forEach(
(e) => delete e.password && delete e.salt && delete e.encryption_key
);
res.json(users);
})
)
/**
* @api {delete} /admin/user
* @apiName AdminDeleteUser
*
* @apiParam {String} id The User ID
*
* @apiGroup admin_user
* @apiPermission admin
*
* @apiSuccess {Boolean} success
*/
.delete(
promiseMiddleware(async (req, res) => {
let { id } = req.query as { [key: string]: string };
let user = await User.findById(id);
await Promise.all([
user.mails.map((mail) => Mail.delete(mail)),
[
RefreshToken.deleteFilter({ user: user._id }),
LoginToken.deleteFilter({ user: user._id }),
],
]);
await User.delete(user);
res.json({ success: true });
})
)
/**
* @api {put} /admin/user
* @apiName AdminChangeUser
*
* @apiParam {String} id The User ID
*
* @apiGroup admin_user
* @apiPermission admin
*
* @apiSuccess {Boolean} success
*
* @apiDescription Flipps the user role:
* admin -> user
* user -> admin
*/
.put(
promiseMiddleware(async (req, res) => {
let { id } = req.query as { [key: string]: string };
let user = await User.findById(id);
user.admin = !user.admin;
await User.save(user);
res.json({ success: true });
})
);
export default UserRoute;

View File

@ -1,110 +1,110 @@
import { Request, Response, Router } from "express";
import Stacker from "../middlewares/stacker";
import {
GetClientAuthMiddleware,
GetClientApiAuthMiddleware,
} from "../middlewares/client";
import { GetUserMiddleware } from "../middlewares/user";
import { createJWT } from "../../keys";
import Client from "../../models/client";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import config from "../../config";
import Mail from "../../models/mail";
const ClientRouter = Router();
/**
* @api {get} /client/user
*
* @apiDescription Can be used for simple authentication of user. It will redirect the user to the redirect URI with a very short lived jwt.
*
* @apiParam {String} redirect_uri URL to redirect to on success
* @apiParam {String} state A optional state, that will be included in the JWT and redirect_uri as parameter
*
* @apiName ClientUser
* @apiGroup client
*
* @apiPermission user_client Requires ClientID and Authenticated User
*/
ClientRouter.get(
"/user",
Stacker(
GetClientAuthMiddleware(false),
GetUserMiddleware(false, false),
async (req: Request, res: Response) => {
let { redirect_uri, state } = req.query;
if (redirect_uri !== req.client.redirect_url)
throw new RequestError(
"Invalid redirect URI",
HttpStatusCode.BAD_REQUEST
);
let jwt = await createJWT(
{
client: req.client.client_id,
uid: req.user.uid,
username: req.user.username,
state: state,
},
{
expiresIn: 30,
issuer: config.core.url,
algorithm: "RS256",
subject: req.user.uid,
audience: req.client.client_id,
}
); //after 30 seconds this token is invalid
res.redirect(
redirect_uri + "?jwt=" + jwt + (state ? `&state=${state}` : "")
);
}
)
);
ClientRouter.get(
"/account",
Stacker(GetClientApiAuthMiddleware(), async (req: Request, res) => {
let mails = await Promise.all(
req.user.mails.map((id) => Mail.findById(id))
);
let mail = mails.find((e) => e.primary) || mails[0];
res.json({
user: {
username: req.user.username,
name: req.user.name,
email: mail,
},
});
})
);
/**
* @api {get} /client/featured
*
* @apiDescription Get a list of clients, that want to be featured on the home page
*
* @apiName GetFeaturedClients
* @apiGroup client
*/
ClientRouter.get(
"/featured",
Stacker(async (req: Request, res) => {
let clients = await Client.find({
featured: true,
});
res.json({
clients: clients.map(({ name, logo, website, description }) => ({
name,
logo,
website,
description,
})),
});
})
);
export default ClientRouter;
import { Request, Response, Router } from "express";
import Stacker from "../middlewares/stacker.js";
import {
GetClientAuthMiddleware,
GetClientApiAuthMiddleware,
} from "../middlewares/client.js";
import { GetUserMiddleware } from "../middlewares/user.js";
import { createJWT } from "../../keys.js";
import Client from "../../models/client.js";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import config from "../../config.js";
import Mail from "../../models/mail.js";
const ClientRouter = Router();
/**
* @api {get} /client/user
*
* @apiDescription Can be used for simple authentication of user. It will redirect the user to the redirect URI with a very short lived jwt.
*
* @apiParam {String} redirect_uri URL to redirect to on success
* @apiParam {String} state A optional state, that will be included in the JWT and redirect_uri as parameter
*
* @apiName ClientUser
* @apiGroup client
*
* @apiPermission user_client Requires ClientID and Authenticated User
*/
ClientRouter.get(
"/user",
Stacker(
GetClientAuthMiddleware(false),
GetUserMiddleware(false, false),
async (req: Request, res: Response) => {
let { redirect_uri, state } = req.query;
if (redirect_uri !== req.client.redirect_url)
throw new RequestError(
"Invalid redirect URI",
HttpStatusCode.BAD_REQUEST
);
let jwt = await createJWT(
{
client: req.client.client_id,
uid: req.user.uid,
username: req.user.username,
state: state,
},
{
expiresIn: 30,
issuer: config.core.url,
algorithm: "RS256",
subject: req.user.uid,
audience: req.client.client_id,
}
); //after 30 seconds this token is invalid
res.redirect(
redirect_uri + "?jwt=" + jwt + (state ? `&state=${state}` : "")
);
}
)
);
ClientRouter.get(
"/account",
Stacker(GetClientApiAuthMiddleware(), async (req: Request, res) => {
let mails = await Promise.all(
req.user.mails.map((id) => Mail.findById(id))
);
let mail = mails.find((e) => e.primary) || mails[0];
res.json({
user: {
username: req.user.username,
name: req.user.name,
email: mail,
},
});
})
);
/**
* @api {get} /client/featured
*
* @apiDescription Get a list of clients, that want to be featured on the home page
*
* @apiName GetFeaturedClients
* @apiGroup client
*/
ClientRouter.get(
"/featured",
Stacker(async (req: Request, res) => {
let clients = await Client.find({
featured: true,
});
res.json({
clients: clients.map(({ name, logo, website, description }) => ({
name,
logo,
website,
description,
})),
});
})
);
export default ClientRouter;

View File

@ -1,14 +1,14 @@
import { Request, Response } from "express";
import Stacker from "../middlewares/stacker";
import Stacker from "../middlewares/stacker.js";
import {
ClientAuthMiddleware,
GetClientAuthMiddleware,
} from "../middlewares/client";
import Permission from "../../models/permissions";
import User from "../../models/user";
} from "../middlewares/client.js";
import Permission from "../../models/permissions.js";
import User from "../../models/user.js";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import Grant from "../../models/grants";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import Grant from "../../models/grants.js";
import { ObjectId } from "mongodb";
export const GetPermissions = Stacker(

View File

@ -1,12 +1,12 @@
import * as express from "express";
import AdminRoute from "./admin";
import UserRoute from "./user";
import InternalRoute from "./internal";
import ClientRouter from "./client";
import AdminRoute from "./admin/index.js";
import UserRoute from "./user/index.js";
import InternalRoute from "./internal/index.js";
import ClientRouter from "./client/index.js";
import cors from "cors";
import OAuthRoute from "./oauth";
import config from "../config";
import JRPCEndpoint from "./jrpc";
import OAuthRoute from "./oauth/index.js";
import config from "../config.js";
import JRPCEndpoint from "./jrpc/index.js";
const ApiRouter: express.IRouter = express.Router();
ApiRouter.use("/admin", AdminRoute);
@ -41,10 +41,10 @@ ApiRouter.post("/jrpc", JRPCEndpoint);
ApiRouter.use("/", ClientRouter);
ApiRouter.get("/config.json", (req, res) => {
return res.json({
name: config.core.name,
url: config.core.url,
});
return res.json({
name: config.core.name,
url: config.core.url,
});
});
export default ApiRouter;

View File

@ -1,30 +1,30 @@
import { Router } from "express";
import { OAuthInternalApp } from "./oauth";
import PasswordAuth from "./password";
const InternalRoute: Router = Router();
/**
* @api {get} /internal/oauth
* @apiName ClientInteralOAuth
*
* @apiGroup client_internal
* @apiPermission client_internal Only ClientID
*
* @apiParam {String} redirect_uri Redirect URI called after success
* @apiParam {String} state State will be set in RedirectURI for the client to check
*/
InternalRoute.get("/oauth", OAuthInternalApp);
/**
* @api {post} /internal/password
* @apiName ClientInteralPassword
*
* @apiGroup client_internal
* @apiPermission client_internal Requires ClientID and Secret
*
* @apiParam {String} username Username (either username or UID)
* @apiParam {String} uid User ID (either username or UID)
* @apiParam {String} password Hashed and Salted according to specification
*/
InternalRoute.post("/password", PasswordAuth);
export default InternalRoute;
import { Router } from "express";
import { OAuthInternalApp } from "./oauth.js";
import PasswordAuth from "./password.js";
const InternalRoute: Router = Router();
/**
* @api {get} /internal/oauth
* @apiName ClientInteralOAuth
*
* @apiGroup client_internal
* @apiPermission client_internal Only ClientID
*
* @apiParam {String} redirect_uri Redirect URI called after success
* @apiParam {String} state State will be set in RedirectURI for the client to check
*/
InternalRoute.get("/oauth", OAuthInternalApp);
/**
* @api {post} /internal/password
* @apiName ClientInteralPassword
*
* @apiGroup client_internal
* @apiPermission client_internal Requires ClientID and Secret
*
* @apiParam {String} username Username (either username or UID)
* @apiParam {String} uid User ID (either username or UID)
* @apiParam {String} password Hashed and Salted according to specification
*/
InternalRoute.post("/password", PasswordAuth);
export default InternalRoute;

View File

@ -1,9 +1,9 @@
import { Request, Response, NextFunction } from "express";
import Stacker from "../middlewares/stacker";
import { GetClientAuthMiddleware } from "../middlewares/client";
import { UserMiddleware } from "../middlewares/user";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import ClientCode from "../../models/client_code";
import Stacker from "../middlewares/stacker.js";
import { GetClientAuthMiddleware } from "../middlewares/client.js";
import { UserMiddleware } from "../middlewares/user.js";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import ClientCode from "../../models/client_code.js";
import moment = require("moment");
import { randomBytes } from "crypto";
export const OAuthInternalApp = Stacker(

View File

@ -1,35 +1,35 @@
import { Request, Response, NextFunction } from "express";
import { GetClientAuthMiddleware } from "../middlewares/client";
import Stacker from "../middlewares/stacker";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import User from "../../models/user";
const PasswordAuth = Stacker(
GetClientAuthMiddleware(true, true),
async (req: Request, res: Response) => {
let {
username,
password,
uid,
}: { username: string; password: string; uid: string } = req.body;
let query: any = { password: password };
if (username) {
query.username = username.toLowerCase();
} else if (uid) {
query.uid = uid;
} else {
throw new RequestError(
req.__("No username or uid set"),
HttpStatusCode.BAD_REQUEST
);
}
let user = await User.findOne(query);
if (!user) {
res.json({ error: req.__("Password or username wrong") });
} else {
res.json({ success: true, uid: user.uid });
}
}
);
export default PasswordAuth;
import { Request, Response, NextFunction } from "express";
import { GetClientAuthMiddleware } from "../middlewares/client.js";
import Stacker from "../middlewares/stacker.js";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import User from "../../models/user.js";
const PasswordAuth = Stacker(
GetClientAuthMiddleware(true, true),
async (req: Request, res: Response) => {
let {
username,
password,
uid,
}: { username: string; password: string; uid: string } = req.body;
let query: any = { password: password };
if (username) {
query.username = username.toLowerCase();
} else if (uid) {
query.uid = uid;
} else {
throw new RequestError(
req.__("No username or uid set"),
HttpStatusCode.BAD_REQUEST
);
}
let user = await User.findOne(query);
if (!user) {
res.json({ error: req.__("Password or username wrong") });
} else {
res.json({ success: true, uid: user.uid });
}
}
);
export default PasswordAuth;

View File

@ -1,13 +1,13 @@
import { Format } from "@hibas123/logging";
import Logging from "@hibas123/nodelogging";
import { Server, } from "@hibas123/openauth-internalapi";
import { RequestObject, ResponseObject } from "@hibas123/openauth-internalapi/lib/service_base";
import { RequestObject, ResponseObject } from "@hibas123/openauth-internalapi/lib/service_base.js";
import { Request, Response } from "express";
import Stacker from "../middlewares/stacker";
import AccountService from "./services/account";
import LoginService from "./services/login";
import SecurityService from "./services/security";
import TFAService from "./services/twofactor";
import Stacker from "../middlewares/stacker.js";
import AccountService from "./services/account.js";
import LoginService from "./services/login.js";
import SecurityService from "./services/security.js";
import TFAService from "./services/twofactor.js";
export type SessionContext = Request;

View File

@ -1,8 +1,8 @@
import { Profile, ContactInfo, Gender, Server, UserRegisterInfo } from "@hibas123/openauth-internalapi";
import type { SessionContext } from "../index";
import Mail from "../../../models/mail";
import User from "../../../models/user";
import { RequireLogin } from "../../../helper/login";
import type { SessionContext } from "../index.js";
import Mail from "../../../models/mail.js";
import User from "../../../models/user.js";
import { RequireLogin } from "../../../helper/login.js";
export default class AccountService extends Server.AccountService<SessionContext> {
Register(regcode: string, info: UserRegisterInfo, ctx: SessionContext): Promise<void> {

View File

@ -1,13 +1,13 @@
import { Server, LoginState, TFAOption, TFAType } from "@hibas123/openauth-internalapi";
import type { SessionContext } from "../index";
import type { SessionContext } from "../index.js";
import Logging from "@hibas123/nodelogging";
import User, { IUser } from "../../../models/user";
import User, { IUser } from "../../../models/user.js";
import moment from "moment";
import crypto from "node:crypto";
import TwoFactor, { ITwoFactor, IWebAuthn } from "../../../models/twofactor";
import TwoFactor, { ITwoFactor, IWebAuthn } from "../../../models/twofactor.js";
import speakeasy from "speakeasy";
import { generateAuthenticationOptions, verifyAuthenticationResponse } from "@simplewebauthn/server";
import config from "../../../config";
import config from "../../../config.js";
//FIXME: There are a lot of uneccessary database requests happening here. Since this is not a "hot" path, it should not matter to much, but it should be fixed nontheless.
@ -212,13 +212,12 @@ export default class LoginService extends Server.LoginService<SessionContext> {
const rpID = new URL(config.core.url).hostname;
let options = generateAuthenticationOptions({
let options = await generateAuthenticationOptions({
timeout: 60000,
userVerification: "discouraged",
rpID,
allowCredentials: [{
id: tfa.data.device.credentialID.buffer,
type: "public-key",
id: typeof tfa.data.device.credentialID === "string" ? tfa.data.device.credentialID : Buffer.from(tfa.data.device.credentialID.buffer).toString("base64url"),
transports: tfa.data.device.transports
}]
})
@ -241,10 +240,10 @@ export default class LoginService extends Server.LoginService<SessionContext> {
let verification = await verifyAuthenticationResponse({
response: JSON.parse(response),
authenticator: {
credential: {
id: typeof tfa.data.device.credentialID === "string" ? tfa.data.device.credentialID : Buffer.from(tfa.data.device.credentialID.buffer).toString("base64url"),
publicKey: Buffer.from(tfa.data.device.credentialPublicKey.buffer),
counter: tfa.data.device.counter,
credentialID: tfa.data.device.credentialID.buffer,
credentialPublicKey: tfa.data.device.credentialPublicKey.buffer,
transports: tfa.data.device.transports
},
expectedChallenge: ctx.session.login_state.webauthn_challenge,

View File

@ -1,9 +1,9 @@
import { Server, Session } from "@hibas123/openauth-internalapi";
import type { SessionContext } from "../index";
import type { SessionContext } from "../index.js";
import Logging from "@hibas123/nodelogging";
import { RequireLogin } from "../../../helper/login";
import { RequireLogin } from "../../../helper/login.js";
import crypto from "node:crypto";
import User from "../../../models/user";
import User from "../../../models/user.js";
export default class SecurityService extends Server.SecurityService<SessionContext> {
@RequireLogin()

View File

@ -1,15 +1,15 @@
import { TFANewTOTP, Server, TFAOption, UserRegisterInfo, TFAWebAuthRegister } from "@hibas123/openauth-internalapi";
import type { SessionContext } from "../index";
import TwoFactorModel, { ITOTP, IWebAuthn, TFATypes } from "../../../models/twofactor";
import type { SessionContext } from "../index.js";
import TwoFactorModel, { ITOTP, IWebAuthn, TFATypes } from "../../../models/twofactor.js";
import moment = require("moment");
import * as speakeasy from "speakeasy";
import * as qrcode from "qrcode";
import config from "../../../config";
import config from "../../../config.js";
import { generateRegistrationOptions, verifyRegistrationResponse } from '@simplewebauthn/server';
import type { RegistrationResponseJSON } from '@simplewebauthn/typescript-types';
// import type { RegistrationResponseJSON } from '@simplewebauthn/typescript-types';
import Logging from "@hibas123/nodelogging";
import { Binary } from "mongodb";
import { RequireLogin } from "../../../helper/login";
import { RequireLogin } from "../../../helper/login.js";
export default class TFAService extends Server.TFAService<SessionContext> {
@ -111,10 +111,10 @@ export default class TFAService extends Server.TFAService<SessionContext> {
// TODO: Get already registered options
const rpID = new URL(config.core.url).hostname;
const options = generateRegistrationOptions({
const options = await generateRegistrationOptions({
rpName: config.core.name,
rpID,
userID: ctx.user.uid,
userID: Buffer.from(ctx.user.uid, "utf-8"),
userName: ctx.user.username,
attestationType: 'direct',
userDisplayName: ctx.user.name,
@ -156,7 +156,7 @@ export default class TFAService extends Server.TFAService<SessionContext> {
const rpID = new URL(config.core.url).hostname;
const response = JSON.parse(registration) as RegistrationResponseJSON;
const response = JSON.parse(registration); // as RegistrationResponseJSON;
let verification = await verifyRegistrationResponse({
response,
@ -167,7 +167,7 @@ export default class TFAService extends Server.TFAService<SessionContext> {
});
if (verification.verified) {
const { credentialPublicKey, credentialID, counter } = verification.registrationInfo;
const { credential, } = verification.registrationInfo;
//TODO: Check if already registered!
// TwoFactorModel.find({
@ -177,10 +177,11 @@ export default class TFAService extends Server.TFAService<SessionContext> {
twofactor.data = {
device: {
credentialPublicKey: new Binary(credentialPublicKey),
credentialID: new Binary(credentialID),
counter: verification.registrationInfo.counter,
transports: response.response.transports as any[]
counter: credential.counter,
credentialPublicKey: new Binary(credential.publicKey),
credentialID: credential.id,
// counter: verification.registrationInfo.counter,
transports: response.response.transports as any[],
}
}

View File

@ -1,123 +1,122 @@
import { NextFunction, Request, Response } from "express";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import Client from "../../models/client";
import { validateJWT } from "../../keys";
import User from "../../models/user";
import Mail from "../../models/mail";
import { OAuthJWT } from "../../helper/jwt";
import Logging from "@hibas123/nodelogging";
export function GetClientAuthMiddleware(
checksecret = true,
internal = false,
checksecret_if_available = false
) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
let client_id = req.query.client_id || req.body.client_id;
let client_secret = req.query.client_secret || req.body.client_secret;
if (!client_id && !client_secret && req.headers.authorization) {
let header = req.headers.authorization;
let [type, val] = header.split(" ");
if (val) {
let str = Buffer.from(val, "base64").toString("utf-8");
let [id, secret] = str.split(":");
client_id = id;
client_secret = secret;
}
}
if (!client_id || (!client_secret && checksecret)) {
throw new RequestError(
"No client credentials",
HttpStatusCode.BAD_REQUEST
);
}
let w = { client_id: client_id, client_secret: client_secret };
if (!checksecret && !(checksecret_if_available && client_secret))
delete w.client_secret;
let client = await Client.findOne(w);
if (!client) {
throw new RequestError(
"Invalid client_id" + (checksecret ? "or client_secret" : ""),
HttpStatusCode.BAD_REQUEST
);
}
if (internal && !client.internal) {
throw new RequestError(
req.__("Client has no permission for access"),
HttpStatusCode.FORBIDDEN
);
}
req.client = client;
next();
} catch (e) {
if (next) next(e);
else throw e;
}
};
}
export const ClientAuthMiddleware = GetClientAuthMiddleware();
export function GetClientApiAuthMiddleware(permissions?: string[]) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const invalid_err = new RequestError(
req.__("Unauthorized"),
HttpStatusCode.UNAUTHORIZED
);
let token =
(req.query.access_token as string) ||
(req.headers.authorization as string);
if (!token) {
Logging.debug("No token found. Searched in query (access_token) and header (authorization)");
throw invalid_err;
}
if (token.toLowerCase().startsWith("bearer "))
token = token.substring(7);
let data: OAuthJWT;
try {
data = await validateJWT(token);
} catch (err) {
Logging.debug("Invalid JWT", err.message);
throw invalid_err;
}
let user = await User.findOne({ uid: data.user });
if (!user) {
Logging.debug("User not found");
throw invalid_err;
}
let client = await Client.findOne({ client_id: data.application });
if (!client) {
Logging.debug("Client not found");
throw invalid_err;
}
if (
permissions &&
(!data.permissions ||
!permissions.every((e) => data.permissions.indexOf(e) >= 0))
) {
Logging.debug("Invalid permissions");
throw invalid_err;
}
req.user = user;
req.client = client;
next();
} catch (e) {
if (next) next(e);
else throw e;
}
};
}
import { NextFunction, Request, Response } from "express";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import Client from "../../models/client.js";
import { validateJWT } from "../../keys.js";
import User from "../../models/user.js";
import { OAuthJWT } from "../../helper/jwt.js";
import Logging from "@hibas123/nodelogging";
export function GetClientAuthMiddleware(
checksecret = true,
internal = false,
checksecret_if_available = false
) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
let client_id = req.query.client_id || req.body.client_id;
let client_secret = req.query.client_secret || req.body.client_secret;
if (!client_id && !client_secret && req.headers.authorization) {
let header = req.headers.authorization;
let [type, val] = header.split(" ");
if (val) {
let str = Buffer.from(val, "base64").toString("utf-8");
let [id, secret] = str.split(":");
client_id = id;
client_secret = secret;
}
}
if (!client_id || (!client_secret && checksecret)) {
throw new RequestError(
"No client credentials",
HttpStatusCode.BAD_REQUEST
);
}
let w = { client_id: client_id, client_secret: client_secret };
if (!checksecret && !(checksecret_if_available && client_secret))
delete w.client_secret;
let client = await Client.findOne(w);
if (!client) {
throw new RequestError(
"Invalid client_id" + (checksecret ? "or client_secret" : ""),
HttpStatusCode.BAD_REQUEST
);
}
if (internal && !client.internal) {
throw new RequestError(
req.__("Client has no permission for access"),
HttpStatusCode.FORBIDDEN
);
}
req.client = client;
next();
} catch (e) {
if (next) next(e);
else throw e;
}
};
}
export const ClientAuthMiddleware = GetClientAuthMiddleware();
export function GetClientApiAuthMiddleware(permissions?: string[]) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const invalid_err = new RequestError(
req.__("Unauthorized"),
HttpStatusCode.UNAUTHORIZED
);
let token =
(req.query.access_token as string) ||
(req.headers.authorization as string);
if (!token) {
Logging.debug("No token found. Searched in query (access_token) and header (authorization)");
throw invalid_err;
}
if (token.toLowerCase().startsWith("bearer "))
token = token.substring(7);
let data: OAuthJWT;
try {
data = await validateJWT(token);
} catch (err) {
Logging.debug("Invalid JWT", err.message);
throw invalid_err;
}
let user = await User.findOne({ uid: data.user });
if (!user) {
Logging.debug("User not found");
throw invalid_err;
}
let client = await Client.findOne({ client_id: data.application });
if (!client) {
Logging.debug("Client not found");
throw invalid_err;
}
if (
permissions &&
(!data.permissions ||
!permissions.every((e) => data.permissions.indexOf(e) >= 0))
) {
Logging.debug("Invalid permissions");
throw invalid_err;
}
req.user = user;
req.client = client;
next();
} catch (e) {
if (next) next(e);
else throw e;
}
};
}

View File

@ -1,28 +1,28 @@
import { Request, Response, NextFunction, RequestHandler } from "express";
import promiseMiddleware from "../../helper/promiseMiddleware";
type RH = (req: Request, res: Response, next?: NextFunction) => any;
function call(handler: RH, req: Request, res: Response) {
return new Promise<void>((yes, no) => {
let p = handler(req, res, (err) => {
if (err) no(err);
else yes();
});
if (p && p.catch) p.catch((err) => no(err));
});
}
const Stacker = (...handler: RH[]) => {
return promiseMiddleware(
async (req: Request, res: Response, next: NextFunction) => {
let hc = handler.concat();
while (hc.length > 0) {
let h = hc.shift();
await call(h, req, res);
}
next();
}
);
};
export default Stacker;
import { Request, Response, NextFunction, RequestHandler } from "express";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
type RH = (req: Request, res: Response, next?: NextFunction) => any;
function call(handler: RH, req: Request, res: Response) {
return new Promise<void>((yes, no) => {
let p = handler(req, res, (err) => {
if (err) no(err);
else yes();
});
if (p && p.catch) p.catch((err) => no(err));
});
}
const Stacker = (...handler: RH[]) => {
return promiseMiddleware(
async (req: Request, res: Response, next: NextFunction) => {
let hc = handler.concat();
while (hc.length > 0) {
let h = hc.shift();
await call(h, req, res);
}
next();
}
);
};
export default Stacker;

View File

@ -1,8 +1,8 @@
import { NextFunction, Request, Response } from "express";
import Logging from "@hibas123/nodelogging";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import promiseMiddleware from "../../helper/promiseMiddleware";
import { requireLoginState } from "../../helper/login";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
import { requireLoginState } from "../../helper/login.js";
class Invalid extends Error { }

View File

@ -1,142 +1,141 @@
import { Request, Response, NextFunction } from "express";
import Logging from "@hibas123/nodelogging";
import {
isString,
isDate,
} from "util";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
export enum Types {
STRING,
NUMBER,
BOOLEAN,
EMAIL,
OBJECT,
DATE,
ARRAY,
ENUM,
}
function isEmail(value: any): boolean {
return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
);
}
export interface CheckObject {
type: Types;
query?: boolean;
optional?: boolean;
/**
* Only when Type.ENUM
*
* values to check before
*/
values?: string[];
/**
* Only when Type.STRING
*/
notempty?: boolean; // Only STRING
}
export interface Checks {
[index: string]: CheckObject; // | Types
}
// req: Request, res: Response, next: NextFunction
export default function (fields: Checks, noadditional = false) {
return (req: Request, res: Response, next: NextFunction) => {
let errors: { message: string; field: string }[] = [];
function check(data: any, field_name: string, field: CheckObject) {
if (data !== undefined && data !== null) {
switch (field.type) {
case Types.STRING:
if (isString(data)) {
if (!field.notempty) return;
if (data !== "") return;
}
break;
case Types.NUMBER:
if (typeof data == "number") return;
break;
case Types.EMAIL:
if (isEmail(data)) return;
break;
case Types.BOOLEAN:
if (typeof data == "boolean") return;
break;
case Types.OBJECT:
if (typeof data == "object") return;
break;
case Types.ARRAY:
if (Array.isArray(data)) return;
break;
case Types.DATE:
if (isDate(data)) return;
break;
case Types.ENUM:
if (typeof data == "string") {
if (field.values.indexOf(data) >= 0) return;
}
break;
default:
Logging.error(
`Invalid type to check: ${field.type} ${Types[field.type]}`
);
}
errors.push({
message: res.__(
"Field {{field}} has wrong type. It should be from type {{type}}",
{ field: field_name, type: Types[field.type].toLowerCase() }
),
field: field_name,
});
} else {
if (!field.optional)
errors.push({
message: res.__("Field {{field}} is not defined", {
field: field_name,
}),
field: field_name,
});
}
}
for (let field_name in fields) {
let field = fields[field_name];
let data = fields[field_name].query
? req.query[field_name]
: req.body[field_name];
check(data, field_name, field);
}
if (noadditional) {
//Checks if the data given has additional parameters
let should = Object.keys(fields);
should = should.filter((e) => !fields[e].query); //Query parameters should not exist on body
let has = Object.keys(req.body);
has.every((e) => {
if (should.indexOf(e) >= 0) {
return true;
} else {
errors.push({
message: res.__("Field {{field}} should not be there", {
field: e,
}),
field: e,
});
return false;
}
});
}
if (errors.length > 0) {
let err = new RequestError(errors, HttpStatusCode.BAD_REQUEST, true);
next(err);
} else next();
};
}
import { Request, Response, NextFunction } from "express";
import Logging from "@hibas123/nodelogging";
import {
types
} from "util";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
export enum Types {
STRING,
NUMBER,
BOOLEAN,
EMAIL,
OBJECT,
DATE,
ARRAY,
ENUM,
}
function isEmail(value: any): boolean {
return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
);
}
export interface CheckObject {
type: Types;
query?: boolean;
optional?: boolean;
/**
* Only when Type.ENUM
*
* values to check before
*/
values?: string[];
/**
* Only when Type.STRING
*/
notempty?: boolean; // Only STRING
}
export interface Checks {
[index: string]: CheckObject; // | Types
}
// req: Request, res: Response, next: NextFunction
export default function (fields: Checks, noadditional = false) {
return (req: Request, res: Response, next: NextFunction) => {
let errors: { message: string; field: string }[] = [];
function check(data: any, field_name: string, field: CheckObject) {
if (data !== undefined && data !== null) {
switch (field.type) {
case Types.STRING:
if (typeof data === "string") {
if (!field.notempty) return;
if (data !== "") return;
}
break;
case Types.NUMBER:
if (typeof data == "number") return;
break;
case Types.EMAIL:
if (isEmail(data)) return;
break;
case Types.BOOLEAN:
if (typeof data == "boolean") return;
break;
case Types.OBJECT:
if (typeof data == "object") return;
break;
case Types.ARRAY:
if (Array.isArray(data)) return;
break;
case Types.DATE:
if (types.isDate(data)) return;
break;
case Types.ENUM:
if (typeof data == "string") {
if (field.values.indexOf(data) >= 0) return;
}
break;
default:
Logging.error(
`Invalid type to check: ${field.type} ${Types[field.type]}`
);
}
errors.push({
message: res.__(
"Field {{field}} has wrong type. It should be from type {{type}}",
{ field: field_name, type: Types[field.type].toLowerCase() }
),
field: field_name,
});
} else {
if (!field.optional)
errors.push({
message: res.__("Field {{field}} is not defined", {
field: field_name,
}),
field: field_name,
});
}
}
for (let field_name in fields) {
let field = fields[field_name];
let data = fields[field_name].query
? req.query[field_name]
: req.body[field_name];
check(data, field_name, field);
}
if (noadditional) {
//Checks if the data given has additional parameters
let should = Object.keys(fields);
should = should.filter((e) => !fields[e].query); //Query parameters should not exist on body
let has = Object.keys(req.body);
has.every((e) => {
if (should.indexOf(e) >= 0) {
return true;
} else {
errors.push({
message: res.__("Field {{field}} should not be there", {
field: e,
}),
field: e,
});
return false;
}
});
}
if (errors.length > 0) {
let err = new RequestError(errors, HttpStatusCode.BAD_REQUEST, true);
next(err);
} else next();
};
}

View File

@ -1,15 +1,15 @@
import Stacker from "../middlewares/stacker";
import { GetUserMiddleware } from "../middlewares/user";
import Stacker from "../middlewares/stacker.js";
import { GetUserMiddleware } from "../middlewares/user.js";
import { Request, Response } from "express";
import Client from "../../models/client";
import Client from "../../models/client.js";
import Logging from "@hibas123/nodelogging";
import Permission, { IPermission } from "../../models/permissions";
import ClientCode from "../../models/client_code";
import Permission, { IPermission } from "../../models/permissions.js";
import ClientCode from "../../models/client_code.js";
import moment = require("moment");
import { randomBytes } from "crypto";
// import { ObjectId } from "bson";
import Grant, { IGrant } from "../../models/grants";
import GetAuthPage from "../../views/authorize";
import Grant, { IGrant } from "../../models/grants.js";
import GetAuthPage from "../../views/authorize.js";
import { ObjectId } from "mongodb";
// const AuthRoute = Stacker(GetUserMiddleware(true), async (req: Request, res: Response) => {

View File

@ -1,73 +1,73 @@
import { Router } from "express";
import GetAuthRoute from "./auth";
import JWTRoute from "./jwt";
import Public from "./public";
import RefreshTokenRoute from "./refresh";
import ProfileRoute from "./profile";
const OAuthRoute: Router = Router();
/**
* @api {post} /oauth/auth
* @apiName OAuthAuth
*
* @apiGroup oauth
* @apiPermission user Special required
*
* @apiParam {String} response_type must be "code" others are not supported
* @apiParam {String} client_id ClientID
* @apiParam {String} redirect_uri The URI to redirect with code
* @apiParam {String} scope Scope that contains the requested permissions (comma seperated list of permissions)
* @apiParam {String} state State, that will be passed to redirect_uri for client
* @apiParam {String} nored Deactivates the Redirect response from server and instead returns the redirect URI in JSON response
*/
OAuthRoute.post("/auth", GetAuthRoute(false));
/**
* @api {get} /oauth/jwt
* @apiName OAuthJwt
*
* @apiGroup oauth
* @apiPermission none
*
* @apiParam {String} refreshtoken
*
* @apiSuccess {String} token The JWT that allowes the application to access the recources granted for refresh token
*/
OAuthRoute.get("/jwt", JWTRoute);
/**
* @api {get} /oauth/public
* @apiName OAuthPublic
*
* @apiGroup oauth
* @apiPermission none
*
* @apiSuccess {String} public_key The applications public_key. Used to verify JWT.
*/
OAuthRoute.get("/public", Public);
/**
* @api {get} /oauth/refresh
* @apiName OAuthRefreshGet
*
* @apiGroup oauth
*/
OAuthRoute.get("/refresh", RefreshTokenRoute);
/**
* @api {post} /oauth/refresh
* @apiName OAuthRefreshPost
*
* @apiGroup oauth
*/
OAuthRoute.post("/refresh", RefreshTokenRoute);
/**
* @api {get} /oauth/profile
* @apiName OAuthProfile
*
* @apiGroup oauth
*/
OAuthRoute.get("/profile", ProfileRoute);
export default OAuthRoute;
import { Router } from "express";
import GetAuthRoute from "./auth.js";
import JWTRoute from "./jwt.js";
import Public from "./public.js";
import RefreshTokenRoute from "./refresh.js";
import ProfileRoute from "./profile.js";
const OAuthRoute: Router = Router();
/**
* @api {post} /oauth/auth
* @apiName OAuthAuth
*
* @apiGroup oauth
* @apiPermission user Special required
*
* @apiParam {String} response_type must be "code" others are not supported
* @apiParam {String} client_id ClientID
* @apiParam {String} redirect_uri The URI to redirect with code
* @apiParam {String} scope Scope that contains the requested permissions (comma seperated list of permissions)
* @apiParam {String} state State, that will be passed to redirect_uri for client
* @apiParam {String} nored Deactivates the Redirect response from server and instead returns the redirect URI in JSON response
*/
OAuthRoute.post("/auth", GetAuthRoute(false));
/**
* @api {get} /oauth/jwt
* @apiName OAuthJwt
*
* @apiGroup oauth
* @apiPermission none
*
* @apiParam {String} refreshtoken
*
* @apiSuccess {String} token The JWT that allowes the application to access the recources granted for refresh token
*/
OAuthRoute.get("/jwt", JWTRoute);
/**
* @api {get} /oauth/public
* @apiName OAuthPublic
*
* @apiGroup oauth
* @apiPermission none
*
* @apiSuccess {String} public_key The applications public_key. Used to verify JWT.
*/
OAuthRoute.get("/public", Public);
/**
* @api {get} /oauth/refresh
* @apiName OAuthRefreshGet
*
* @apiGroup oauth
*/
OAuthRoute.get("/refresh", RefreshTokenRoute);
/**
* @api {post} /oauth/refresh
* @apiName OAuthRefreshPost
*
* @apiGroup oauth
*/
OAuthRoute.post("/refresh", RefreshTokenRoute);
/**
* @api {get} /oauth/profile
* @apiName OAuthProfile
*
* @apiGroup oauth
*/
OAuthRoute.get("/profile", ProfileRoute);
export default OAuthRoute;

View File

@ -1,43 +1,43 @@
import { Request, Response } from "express";
import promiseMiddleware from "../../helper/promiseMiddleware";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import RefreshToken from "../../models/refresh_token";
import User from "../../models/user";
import Client from "../../models/client";
import { getAccessTokenJWT } from "../../helper/jwt";
const JWTRoute = promiseMiddleware(async (req: Request, res: Response) => {
let { refreshtoken } = req.query as { [key: string]: string };
if (!refreshtoken)
throw new RequestError(
req.__("Refresh token not set"),
HttpStatusCode.BAD_REQUEST
);
let token = await RefreshToken.findOne({ token: refreshtoken });
if (!token)
throw new RequestError(
req.__("Invalid token"),
HttpStatusCode.BAD_REQUEST
);
let user = await User.findById(token.user);
if (!user) {
token.valid = false;
await RefreshToken.save(token);
throw new RequestError(
req.__("Invalid token"),
HttpStatusCode.BAD_REQUEST
);
}
let client = await Client.findById(token.client);
let jwt = await getAccessTokenJWT({
user,
permissions: token.permissions,
client,
});
res.json({ token: jwt });
});
export default JWTRoute;
import { Request, Response } from "express";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import RefreshToken from "../../models/refresh_token.js";
import User from "../../models/user.js";
import Client from "../../models/client.js";
import { getAccessTokenJWT } from "../../helper/jwt.js";
const JWTRoute = promiseMiddleware(async (req: Request, res: Response) => {
let { refreshtoken } = req.query as { [key: string]: string };
if (!refreshtoken)
throw new RequestError(
req.__("Refresh token not set"),
HttpStatusCode.BAD_REQUEST
);
let token = await RefreshToken.findOne({ token: refreshtoken });
if (!token)
throw new RequestError(
req.__("Invalid token"),
HttpStatusCode.BAD_REQUEST
);
let user = await User.findById(token.user);
if (!user) {
token.valid = false;
await RefreshToken.save(token);
throw new RequestError(
req.__("Invalid token"),
HttpStatusCode.BAD_REQUEST
);
}
let client = await Client.findById(token.client);
let jwt = await getAccessTokenJWT({
user,
permissions: token.permissions,
client,
});
res.json({ token: jwt });
});
export default JWTRoute;

View File

@ -1,6 +1,6 @@
import Mail from "../../models/mail";
import { GetClientApiAuthMiddleware } from "../middlewares/client";
import Stacker from "../middlewares/stacker";
import Mail from "../../models/mail.js";
import { GetClientApiAuthMiddleware } from "../middlewares/client.js";
import Stacker from "../middlewares/stacker.js";
import { Request, Response } from "express";
import Logging from "@hibas123/nodelogging";

View File

@ -1,6 +1,6 @@
import { Request, Response } from "express";
import { public_key } from "../../keys";
export default function Public(req: Request, res: Response) {
res.json({ public_key: public_key });
}
import { Request, Response } from "express";
import { public_key } from "../../keys.js";
export default function Public(req: Request, res: Response) {
res.json({ public_key: public_key });
}

View File

@ -1,122 +1,122 @@
import { Request, Response } from "express";
import RequestError, { HttpStatusCode } from "../../helper/request_error";
import User from "../../models/user";
import Client from "../../models/client";
import {
getAccessTokenJWT,
getIDToken,
AccessTokenJWTExp,
} from "../../helper/jwt";
import Stacker from "../middlewares/stacker";
import { GetClientAuthMiddleware } from "../middlewares/client";
import ClientCode from "../../models/client_code";
import Mail from "../../models/mail";
import { randomBytes } from "crypto";
import moment = require("moment");
// import { JWTExpDur } from "../../keys";
import RefreshToken from "../../models/refresh_token";
import { getEncryptionKey } from "../../helper/user_key";
import { refreshTokenValidTime } from "../../config";
// TODO:
/*
For example, the authorization server could employ refresh token
rotation in which a new refresh token is issued with every access
token refresh response. The previous refresh token is invalidated but retained by the authorization server. If a refresh token is
compromised and subsequently used by both the attacker and the
legitimate client, one of them will present an invalidated refresh
token, which will inform the authorization server of the breach.
*/
const RefreshTokenRoute = Stacker(
GetClientAuthMiddleware(false, false, true),
async (req: Request, res: Response) => {
let grant_type = req.query.grant_type || req.body.grant_type;
if (!grant_type || grant_type === "authorization_code") {
let code = req.query.code || req.body.code;
let nonce = req.query.nonce || req.body.nonce;
let c = await ClientCode.findOne({ code: code });
if (!c || moment(c.validTill).isBefore()) {
throw new RequestError(
req.__("Invalid code"),
HttpStatusCode.BAD_REQUEST
);
}
let client = await Client.findById(c.client);
let user = await User.findById(c.user);
let mails = await Promise.all(user.mails.map((m) => Mail.findOne(m)));
let token = RefreshToken.new({
user: c.user,
client: c.client,
permissions: c.permissions,
token: randomBytes(16).toString("hex"),
valid: true,
validTill: moment().add(refreshTokenValidTime).toDate(),
});
await RefreshToken.save(token);
await ClientCode.delete(c);
let mail = mails.find((e) => e.primary);
if (!mail) mail = mails[0];
res.json({
refresh_token: token.token,
token: token.token,
access_token: await getAccessTokenJWT({
client: client,
user: user,
permissions: c.permissions,
}),
token_type: "bearer",
expires_in: AccessTokenJWTExp.asSeconds(),
profile: {
uid: user.uid,
email: mail ? mail.mail : "",
name: user.name,
enc_key: getEncryptionKey(user, client),
},
id_token: getIDToken(user, client.client_id, nonce),
});
} else if (grant_type === "refresh_token") {
let refresh_token = req.query.refresh_token || req.body.refresh_token;
if (!refresh_token)
throw new RequestError(
req.__("refresh_token not set"),
HttpStatusCode.BAD_REQUEST
);
let token = await RefreshToken.findOne({ token: refresh_token });
if (!token || !token.valid || moment(token.validTill).isBefore())
throw new RequestError(
req.__("Invalid token"),
HttpStatusCode.BAD_REQUEST
);
token.validTill = moment().add(refreshTokenValidTime).toDate();
await RefreshToken.save(token);
let user = await User.findById(token.user);
let client = await Client.findById(token.client);
let jwt = await getAccessTokenJWT({
user,
client,
permissions: token.permissions,
});
res.json({
access_token: jwt,
expires_in: AccessTokenJWTExp.asSeconds(),
});
} else {
throw new RequestError(
"invalid grant_type",
HttpStatusCode.BAD_REQUEST
);
}
}
);
export default RefreshTokenRoute;
import { Request, Response } from "express";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import User from "../../models/user.js";
import Client from "../../models/client.js";
import {
getAccessTokenJWT,
getIDToken,
AccessTokenJWTExp,
} from "../../helper/jwt.js";
import Stacker from "../middlewares/stacker.js";
import { GetClientAuthMiddleware } from "../middlewares/client.js";
import ClientCode from "../../models/client_code.js";
import Mail from "../../models/mail.js";
import { randomBytes } from "crypto";
import moment = require("moment");
// import { JWTExpDur } from "../../keys";
import RefreshToken from "../../models/refresh_token.js";
import { getEncryptionKey } from "../../helper/user_key.js";
import { refreshTokenValidTime } from "../../config.js";
// TODO:
/*
For example, the authorization server could employ refresh token
rotation in which a new refresh token is issued with every access
token refresh response. The previous refresh token is invalidated but retained by the authorization server. If a refresh token is
compromised and subsequently used by both the attacker and the
legitimate client, one of them will present an invalidated refresh
token, which will inform the authorization server of the breach.
*/
const RefreshTokenRoute = Stacker(
GetClientAuthMiddleware(false, false, true),
async (req: Request, res: Response) => {
let grant_type = req.query.grant_type || req.body.grant_type;
if (!grant_type || grant_type === "authorization_code") {
let code = req.query.code || req.body.code;
let nonce = req.query.nonce || req.body.nonce;
let c = await ClientCode.findOne({ code: code });
if (!c || moment(c.validTill).isBefore()) {
throw new RequestError(
req.__("Invalid code"),
HttpStatusCode.BAD_REQUEST
);
}
let client = await Client.findById(c.client);
let user = await User.findById(c.user);
let mails = await Promise.all(user.mails.map((m) => Mail.findOne(m)));
let token = RefreshToken.new({
user: c.user,
client: c.client,
permissions: c.permissions,
token: randomBytes(16).toString("hex"),
valid: true,
validTill: moment().add(refreshTokenValidTime).toDate(),
});
await RefreshToken.save(token);
await ClientCode.delete(c);
let mail = mails.find((e) => e.primary);
if (!mail) mail = mails[0];
res.json({
refresh_token: token.token,
token: token.token,
access_token: await getAccessTokenJWT({
client: client,
user: user,
permissions: c.permissions,
}),
token_type: "bearer",
expires_in: AccessTokenJWTExp.asSeconds(),
profile: {
uid: user.uid,
email: mail ? mail.mail : "",
name: user.name,
enc_key: getEncryptionKey(user, client),
},
id_token: getIDToken(user, client.client_id, nonce),
});
} else if (grant_type === "refresh_token") {
let refresh_token = req.query.refresh_token || req.body.refresh_token;
if (!refresh_token)
throw new RequestError(
req.__("refresh_token not set"),
HttpStatusCode.BAD_REQUEST
);
let token = await RefreshToken.findOne({ token: refresh_token });
if (!token || !token.valid || moment(token.validTill).isBefore())
throw new RequestError(
req.__("Invalid token"),
HttpStatusCode.BAD_REQUEST
);
token.validTill = moment().add(refreshTokenValidTime).toDate();
await RefreshToken.save(token);
let user = await User.findById(token.user);
let client = await Client.findById(token.client);
let jwt = await getAccessTokenJWT({
user,
client,
permissions: token.permissions,
});
res.json({
access_token: jwt,
expires_in: AccessTokenJWTExp.asSeconds(),
});
} else {
throw new RequestError(
"invalid grant_type",
HttpStatusCode.BAD_REQUEST
);
}
}
);
export default RefreshTokenRoute;

View File

@ -1,6 +1,6 @@
import { Router } from "express";
import Register from "./register";
import OAuthRoute from "./oauth";
import Register from "./register.js";
import OAuthRoute from "./oauth/index.js";
const UserRoute: Router = Router();

View File

@ -1,21 +1,21 @@
import RequestError, { HttpStatusCode } from "../../../helper/request_error";
import Client, { IClient } from "../../../models/client";
export async function getClientWithOrigin(client_id: string, origin: string) {
const client = await Client.findOne({
client_id,
});
const clientNotFoundError = new RequestError(
"Client not found!",
HttpStatusCode.BAD_REQUEST
);
if (!client) throw clientNotFoundError;
const clientUrl = new URL(client.redirect_url);
if (clientUrl.hostname !== origin) throw clientNotFoundError;
return client;
}
import RequestError, { HttpStatusCode } from "../../../helper/request_error.js";
import Client, { IClient } from "../../../models/client.js";
export async function getClientWithOrigin(client_id: string, origin: string) {
const client = await Client.findOne({
client_id,
});
const clientNotFoundError = new RequestError(
"Client not found!",
HttpStatusCode.BAD_REQUEST
);
if (!client) throw clientNotFoundError;
const clientUrl = new URL(client.redirect_url);
if (clientUrl.hostname !== origin) throw clientNotFoundError;
return client;
}

View File

@ -1,12 +1,12 @@
import { Router } from "express";
import { GetJWTByUser } from "./jwt";
import { GetPermissionsForAuthRequest } from "./permissions";
import { GetTokenByUser } from "./refresh_token";
const router = Router();
router.get("/jwt", GetJWTByUser);
router.get("/permissions", GetPermissionsForAuthRequest);
router.get("/refresh_token", GetTokenByUser);
export default router;
import { Router } from "express";
import { GetJWTByUser } from "./jwt.js";
import { GetPermissionsForAuthRequest } from "./permissions.js";
import { GetTokenByUser } from "./refresh_token.js";
const router = Router();
router.get("/jwt", GetJWTByUser);
router.get("/permissions", GetPermissionsForAuthRequest);
router.get("/refresh_token", GetTokenByUser);
export default router;

View File

@ -1,25 +1,23 @@
import { Request, Response } from "express";
import Stacker from "../../middlewares/stacker";
import { GetUserMiddleware } from "../../middlewares/user";
import { URL } from "url";
import Client from "../../../models/client";
import RequestError, { HttpStatusCode } from "../../../helper/request_error";
import { getAccessTokenJWT } from "../../../helper/jwt";
import { getClientWithOrigin } from "./_helper";
export const GetJWTByUser = Stacker(
GetUserMiddleware(true, false),
async (req: Request, res: Response) => {
const { client_id, origin } = req.query as { [key: string]: string };
const client = await getClientWithOrigin(client_id, origin);
const jwt = await getAccessTokenJWT({
user: req.user,
client: client,
permissions: [],
});
res.json({ jwt });
}
);
import { Request, Response } from "express";
import Stacker from "../../middlewares/stacker.js";
import { GetUserMiddleware } from "../../middlewares/user.js";
import { getAccessTokenJWT } from "../../../helper/jwt.js";
import { getClientWithOrigin } from "./_helper.js";
export const GetJWTByUser = Stacker(
GetUserMiddleware(true, false),
async (req: Request, res: Response) => {
const { client_id, origin } = req.query as { [key: string]: string };
const client = await getClientWithOrigin(client_id, origin);
const jwt = await getAccessTokenJWT({
user: req.user,
client: client,
permissions: [],
});
res.json({ jwt });
}
);

View File

@ -1,15 +1,9 @@
import { Request, Response } from "express";
import Stacker from "../../middlewares/stacker";
import { GetUserMiddleware } from "../../middlewares/user";
import { URL } from "url";
import Client from "../../../models/client";
import RequestError, { HttpStatusCode } from "../../../helper/request_error";
import { randomBytes } from "crypto";
import moment = require("moment");
import RefreshToken from "../../../models/refresh_token";
import { refreshTokenValidTime } from "../../../config";
import { getClientWithOrigin } from "./_helper";
import Permission from "../../../models/permissions";
import Stacker from "../../middlewares/stacker.js";
import { GetUserMiddleware } from "../../middlewares/user.js";
import RequestError, { HttpStatusCode } from "../../../helper/request_error.js";
import { getClientWithOrigin } from "./_helper.js";
import Permission from "../../../models/permissions.js";
export const GetPermissionsForAuthRequest = Stacker(
GetUserMiddleware(true, false),

View File

@ -1,49 +1,47 @@
import { Request, Response } from "express";
import Stacker from "../../middlewares/stacker";
import { GetUserMiddleware } from "../../middlewares/user";
import { URL } from "url";
import Client from "../../../models/client";
import RequestError, { HttpStatusCode } from "../../../helper/request_error";
import { randomBytes } from "crypto";
import moment = require("moment");
import RefreshToken from "../../../models/refresh_token";
import { refreshTokenValidTime } from "../../../config";
import { getClientWithOrigin } from "./_helper";
import Permission from "../../../models/permissions";
export const GetTokenByUser = Stacker(
GetUserMiddleware(true, false),
async (req: Request, res: Response) => {
const { client_id, origin, permissions } = req.query as {
[key: string]: string;
};
const client = await getClientWithOrigin(client_id, origin);
const perm = permissions.split(",").filter((e) => !!e);
const resolved = await Promise.all(
perm.map((p) => Permission.findById(p))
);
if (resolved.some((e) => e.grant_type !== "user")) {
throw new RequestError(
"Invalid Permission requested",
HttpStatusCode.BAD_REQUEST
);
}
let token = RefreshToken.new({
user: req.user._id,
client: client._id,
permissions: resolved.map((e) => e._id),
token: randomBytes(16).toString("hex"),
valid: true,
validTill: moment().add(refreshTokenValidTime).toDate(),
});
await RefreshToken.save(token);
res.json({ token });
}
);
import { Request, Response } from "express";
import Stacker from "../../middlewares/stacker.js";
import { GetUserMiddleware } from "../../middlewares/user.js";
import RequestError, { HttpStatusCode } from "../../../helper/request_error.js";
import { randomBytes } from "crypto";
import moment = require("moment");
import RefreshToken from "../../../models/refresh_token.js";
import { refreshTokenValidTime } from "../../../config.js";
import { getClientWithOrigin } from "./_helper.js";
import Permission from "../../../models/permissions.js";
export const GetTokenByUser = Stacker(
GetUserMiddleware(true, false),
async (req: Request, res: Response) => {
const { client_id, origin, permissions } = req.query as {
[key: string]: string;
};
const client = await getClientWithOrigin(client_id, origin);
const perm = permissions.split(",").filter((e) => !!e);
const resolved = await Promise.all(
perm.map((p) => Permission.findById(p))
);
if (resolved.some((e) => e.grant_type !== "user")) {
throw new RequestError(
"Invalid Permission requested",
HttpStatusCode.BAD_REQUEST
);
}
let token = RefreshToken.new({
user: req.user._id,
client: client._id,
permissions: resolved.map((e) => e._id),
token: randomBytes(16).toString("hex"),
valid: true,
validTill: moment().add(refreshTokenValidTime).toDate(),
});
await RefreshToken.save(token);
res.json({ token });
}
);

View File

@ -1,155 +1,155 @@
import { Request, Response, Router } from "express";
import Stacker from "../middlewares/stacker";
import verify, { Types } from "../middlewares/verify";
import promiseMiddleware from "../../helper/promiseMiddleware";
import User, { Gender } from "../../models/user";
import { HttpStatusCode } from "../../helper/request_error";
import Mail from "../../models/mail";
import RegCode from "../../models/regcodes";
const Register = Stacker(
verify({
mail: {
type: Types.EMAIL,
notempty: true,
},
username: {
type: Types.STRING,
notempty: true,
},
password: {
type: Types.STRING,
notempty: true,
},
salt: {
type: Types.STRING,
notempty: true,
},
regcode: {
type: Types.STRING,
notempty: true,
},
gender: {
type: Types.STRING,
notempty: true,
},
name: {
type: Types.STRING,
notempty: true,
},
// birthday: {
// type: Types.DATE
// }
}),
promiseMiddleware(async (req: Request, res: Response) => {
let {
username,
password,
salt,
mail,
gender,
name,
birthday,
regcode,
} = req.body;
let u = await User.findOne({ username: username.toLowerCase() });
if (u) {
let err = {
message: [
{
message: req.__("Username taken"),
field: "username",
},
],
status: HttpStatusCode.BAD_REQUEST,
nolog: true,
};
throw err;
}
let m = await Mail.findOne({ mail: mail });
if (m) {
let err = {
message: [
{
message: req.__("Mail linked with other account"),
field: "mail",
},
],
status: HttpStatusCode.BAD_REQUEST,
nolog: true,
};
throw err;
}
let regc = await RegCode.findOne({ token: regcode });
if (!regc) {
let err = {
message: [
{
message: req.__("Invalid registration code"),
field: "regcode",
},
],
status: HttpStatusCode.BAD_REQUEST,
nolog: true,
};
throw err;
}
if (!regc.valid) {
let err = {
message: [
{
message: req.__("Registration code already used"),
field: "regcode",
},
],
status: HttpStatusCode.BAD_REQUEST,
nolog: true,
};
throw err;
}
let g = -1;
switch (gender) {
case "male":
g = Gender.male;
break;
case "female":
g = Gender.female;
break;
case "other":
g = Gender.other;
break;
default:
g = Gender.none;
break;
}
let user = User.new({
username: username.toLowerCase(),
password: password,
salt: salt,
gender: g,
name: name,
// birthday: birthday,
admin: false,
});
regc.valid = false;
await RegCode.save(regc);
let ml = Mail.new({
mail: mail,
primary: true,
});
await Mail.save(ml);
user.mails.push(ml._id);
await User.save(user);
res.json({ success: true });
})
);
export default Register;
import { Request, Response, Router } from "express";
import Stacker from "../middlewares/stacker.js";
import verify, { Types } from "../middlewares/verify.js";
import promiseMiddleware from "../../helper/promiseMiddleware.js";
import User, { Gender } from "../../models/user.js";
import { HttpStatusCode } from "../../helper/request_error.js";
import Mail from "../../models/mail.js";
import RegCode from "../../models/regcodes.js";
const Register = Stacker(
verify({
mail: {
type: Types.EMAIL,
notempty: true,
},
username: {
type: Types.STRING,
notempty: true,
},
password: {
type: Types.STRING,
notempty: true,
},
salt: {
type: Types.STRING,
notempty: true,
},
regcode: {
type: Types.STRING,
notempty: true,
},
gender: {
type: Types.STRING,
notempty: true,
},
name: {
type: Types.STRING,
notempty: true,
},
// birthday: {
// type: Types.DATE
// }
}),
promiseMiddleware(async (req: Request, res: Response) => {
let {
username,
password,
salt,
mail,
gender,
name,
birthday,
regcode,
} = req.body;
let u = await User.findOne({ username: username.toLowerCase() });
if (u) {
let err = {
message: [
{
message: req.__("Username taken"),
field: "username",
},
],
status: HttpStatusCode.BAD_REQUEST,
nolog: true,
};
throw err;
}
let m = await Mail.findOne({ mail: mail });
if (m) {
let err = {
message: [
{
message: req.__("Mail linked with other account"),
field: "mail",
},
],
status: HttpStatusCode.BAD_REQUEST,
nolog: true,
};
throw err;
}
let regc = await RegCode.findOne({ token: regcode });
if (!regc) {
let err = {
message: [
{
message: req.__("Invalid registration code"),
field: "regcode",
},
],
status: HttpStatusCode.BAD_REQUEST,
nolog: true,
};
throw err;
}
if (!regc.valid) {
let err = {
message: [
{
message: req.__("Registration code already used"),
field: "regcode",
},
],
status: HttpStatusCode.BAD_REQUEST,
nolog: true,
};
throw err;
}
let g = -1;
switch (gender) {
case "male":
g = Gender.male;
break;
case "female":
g = Gender.female;
break;
case "other":
g = Gender.other;
break;
default:
g = Gender.none;
break;
}
let user = User.new({
username: username.toLowerCase(),
password: password,
salt: salt,
gender: g,
name: name,
// birthday: birthday,
admin: false,
});
regc.valid = false;
await RegCode.save(regc);
let ml = Mail.new({
mail: mail,
primary: true,
});
await Mail.save(ml);
user.mails.push(ml._id);
await User.save(user);
res.json({ success: true });
})
);
export default Register;

View File

@ -1,5 +1,5 @@
import SafeMongo from "@hibas123/safe_mongo";
import Config from "./config";
import Config from "./config.js";
const host = Config.database.host || "localhost";

View File

@ -1,5 +1,5 @@
import { IUser } from "./models/user";
import { IClient } from "./models/client";
import { IUser } from "./models/user.js";
import { IClient } from "./models/client.js";
declare module "express" {
interface Request {

View File

@ -1,8 +1,8 @@
import { IUser, Gender } from "../models/user";
import { IUser, Gender } from "../models/user.js";
import { ObjectId } from "bson";
import { createJWT } from "../keys";
import { IClient } from "../models/client";
import config from "../config";
import { createJWT } from "../keys.js";
import { IClient } from "../models/client.js";
import config from "../config.js";
import moment = require("moment");
export interface OAuthJWT {

View File

@ -1,4 +1,4 @@
import { SessionContext } from "../api/jrpc";
import { SessionContext } from "../api/jrpc/index.js";
export function requireLoginState(ctx: SessionContext, validated: boolean = true, special: boolean = false): boolean {
if (!ctx.user) return false;

View File

@ -1,18 +1,18 @@
// import * as crypto from "crypto-js"
import { IUser } from "../models/user";
import { IClient } from "../models/client";
import * as crypto from "crypto";
function sha512(text: string) {
let hash = crypto.createHash("sha512");
hash.update(text);
return hash.digest("base64");
}
export function getEncryptionKey(user: IUser, client: IClient) {
return sha512(
sha512(user.encryption_key) +
sha512(client._id.toHexString()) +
sha512(client.client_id)
);
}
// import * as crypto from "crypto-js"
import { IUser } from "../models/user.js";
import { IClient } from "../models/client.js";
import * as crypto from "crypto";
function sha512(text: string) {
let hash = crypto.createHash("sha512");
hash.update(text);
return hash.digest("base64");
}
export function getEncryptionKey(user: IUser, client: IClient) {
return sha512(
sha512(user.encryption_key) +
sha512(client._id.toHexString()) +
sha512(client.client_id)
);
}

View File

@ -1,90 +1,90 @@
import Logging from "@hibas123/nodelogging";
import config from "./config";
// import NLS from "@hibas123/nodeloggingserver_client";
// if (config.logging) {
// let s = NLS(Logging, config.logging.server, config.logging.appid, config.logging.token);
// s.send(`[${new Date().toLocaleTimeString()}] Starting application`);
// }
// if (!config.database) {
// Logging.error("No database config set. Terminating.")
// process.exit();
// }
if (!config.web) {
Logging.error("No web config set. Terminating.");
process.exit();
}
import * as i18n from "i18n";
i18n.configure({
locales: ["en", "de"],
directory: "./locales",
});
import Web from "./web";
import TestData from "./testdata";
import DB from "./database";
Logging.log("Connecting to Database");
if (config.core.dev) {
Logging.warning("Running in dev mode! Database will be cleared!");
}
DB.connect()
.then(async () => {
Logging.log("Database connected", config);
if (config.core.dev) await TestData();
let web = new Web(config.web);
web.listen();
let already = new Set();
function print(path, layer) {
if (layer.route) {
layer.route.stack.forEach(
print.bind(null, path.concat(split(layer.route.path)))
);
} else if (layer.name === "router" && layer.handle.stack) {
layer.handle.stack.forEach(
print.bind(null, path.concat(split(layer.regexp)))
);
} else if (layer.method) {
let me: string = layer.method.toUpperCase();
me += " ".repeat(6 - me.length);
let msg = `${me} /${path
.concat(split(layer.regexp))
.filter(Boolean)
.join("/")}`;
if (!already.has(msg)) {
already.add(msg);
Logging.log(msg);
}
}
}
function split(thing) {
if (typeof thing === "string") {
return thing.split("/");
} else if (thing.fast_slash) {
return "";
} else {
var match = thing
.toString()
.replace("\\/?", "")
.replace("(?=\\/|$)", "$")
.match(
/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//
);
return match
? match[1].replace(/\\(.)/g, "$1").split("/")
: "<complex:" + thing.toString() + ">";
}
}
// Logging.log("--- Endpoints: ---");
// web.server._router.stack.forEach(print.bind(null, []))
// Logging.log("--- Endpoints end ---")
})
.catch((e) => {
Logging.error(e);
process.exit();
});
import Logging from "@hibas123/nodelogging";
import config from "./config.js";
// import NLS from "@hibas123/nodeloggingserver_client";
// if (config.logging) {
// let s = NLS(Logging, config.logging.server, config.logging.appid, config.logging.token);
// s.send(`[${new Date().toLocaleTimeString()}] Starting application`);
// }
// if (!config.database) {
// Logging.error("No database config set. Terminating.")
// process.exit();
// }
if (!config.web) {
Logging.error("No web config set. Terminating.");
process.exit();
}
import * as i18n from "i18n";
i18n.configure({
locales: ["en", "de"],
directory: "./locales",
});
import Web from "./web.js";
import TestData from "./testdata.js";
import DB from "./database.js";
Logging.log("Connecting to Database");
if (config.core.dev) {
Logging.warning("Running in dev mode! Database will be cleared!");
}
DB.connect()
.then(async () => {
Logging.log("Database connected", config);
if (config.core.dev) await TestData();
let web = new Web(config.web);
web.listen();
let already = new Set();
function print(path, layer) {
if (layer.route) {
layer.route.stack.forEach(
print.bind(null, path.concat(split(layer.route.path)))
);
} else if (layer.name === "router" && layer.handle.stack) {
layer.handle.stack.forEach(
print.bind(null, path.concat(split(layer.regexp)))
);
} else if (layer.method) {
let me: string = layer.method.toUpperCase();
me += " ".repeat(6 - me.length);
let msg = `${me} /${path
.concat(split(layer.regexp))
.filter(Boolean)
.join("/")}`;
if (!already.has(msg)) {
already.add(msg);
Logging.log(msg);
}
}
}
function split(thing) {
if (typeof thing === "string") {
return thing.split("/");
} else if (thing.fast_slash) {
return "";
} else {
var match = thing
.toString()
.replace("\\/?", "")
.replace("(?=\\/|$)", "$")
.match(
/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//
);
return match
? match[1].replace(/\\(.)/g, "$1").split("/")
: "<complex:" + thing.toString() + ">";
}
}
// Logging.log("--- Endpoints: ---");
// web.server._router.stack.forEach(print.bind(null, []))
// Logging.log("--- Endpoints end ---")
})
.catch((e) => {
Logging.error(e);
process.exit();
});

View File

@ -14,7 +14,6 @@ export function verify(message: Buffer, signature: Buffer): boolean {
export let public_key: string;
import * as jwt from "jsonwebtoken";
import config from "./config";
export function createJWT(payload: any, options: jwt.SignOptions) {
return new Promise<string>((resolve, reject) => {

View File

@ -1,5 +1,5 @@
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model";
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model.js";
import { ObjectId } from "mongodb";
import { v4 } from "uuid";

View File

@ -1,7 +1,6 @@
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model";
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model.js";
import { ObjectId } from "mongodb";
import { v4 } from "uuid";
export interface IClientCode extends ModelDataBase {
user: ObjectId;

View File

@ -1,5 +1,5 @@
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model";
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model.js";
import { ObjectId } from "mongodb";
export interface IGrant extends ModelDataBase {

View File

@ -1,5 +1,5 @@
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model";
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model.js";
import { ObjectId } from "mongodb";
import moment = require("moment");

View File

@ -1,24 +1,24 @@
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo";
export interface IMail extends ModelDataBase {
mail: string;
verified: boolean;
primary: boolean;
}
const Mail = DB.addModel<IMail>({
name: "mail",
versions: [
{
migration: () => {},
schema: {
mail: { type: String },
verified: { type: Boolean, default: false },
primary: { type: Boolean },
},
},
],
});
export default Mail;
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo";
export interface IMail extends ModelDataBase {
mail: string;
verified: boolean;
primary: boolean;
}
const Mail = DB.addModel<IMail>({
name: "mail",
versions: [
{
migration: () => { },
schema: {
mail: { type: String },
verified: { type: Boolean, default: false },
primary: { type: Boolean },
},
},
],
});
export default Mail;

View File

@ -1,5 +1,5 @@
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model";
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model.js";
import { ObjectId } from "mongodb";
export interface IPermission extends ModelDataBase {

View File

@ -1,5 +1,5 @@
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model";
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model.js";
import { ObjectId } from "mongodb";
import { v4 } from "uuid";

View File

@ -1,5 +1,5 @@
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model";
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model.js";
import { ObjectId } from "mongodb";
import { v4 } from "uuid";

View File

@ -1,6 +1,6 @@
import { TFAType } from "@hibas123/openauth-internalapi";
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model";
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model.js";
import { ObjectId } from "bson";
import { Binary } from "mongodb";
@ -30,7 +30,7 @@ export interface IWebAuthn extends ITwoFactor {
data: {
challenge?: any;
device?: {
credentialID: Binary;
credentialID: Binary | string;
credentialPublicKey: Binary;
counter: number;
transports: AuthenticatorTransport[]

View File

@ -1,8 +1,9 @@
import DB from "../database";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model";
import DB from "../database.js";
import { ModelDataBase } from "@hibas123/safe_mongo/lib/model.js";
import { ObjectId } from "mongodb";
import { v4 } from "uuid";
import { randomString } from "../helper/random";
import { randomString } from "../helper/random.js";
export enum Gender {
none,

View File

@ -1,15 +1,15 @@
import User, { Gender } from "./models/user";
import Client from "./models/client";
import User, { Gender } from "./models/user.js";
import Client from "./models/client.js";
import Logging from "@hibas123/nodelogging";
import RegCode from "./models/regcodes";
import RegCode from "./models/regcodes.js";
import moment from "moment";
import Permission from "./models/permissions";
import Permission from "./models/permissions.js";
import { ObjectId } from "mongodb";
import DB from "./database";
import TwoFactor from "./models/twofactor";
import DB from "./database.js";
import TwoFactor from "./models/twofactor.js";
import LoginToken from "./models/login_token";
import Mail from "./models/mail";
import LoginToken from "./models/login_token.js";
import Mail from "./models/mail.js";
export default async function TestData() {
Logging.warn("Running in dev mode! Database will be cleared!");

View File

@ -1,8 +1,8 @@
import { __ as i__ } from "i18n";
import config from "../config";
import * as viewsv1 from "@hibas123/openauth-views-v1";
export default function GetAdminPage(__: typeof i__): string {
let data = {};
return viewsv1.admin(config.core.dev)(data, { helpers: { i18n: __ } });
}
import { __ as i__ } from "i18n";
import config from "../config.js";
import * as viewsv1 from "@hibas123/openauth-views-v1";
export default function GetAdminPage(__: typeof i__): string {
let data = {};
return viewsv1.admin(config.core.dev)(data, { helpers: { i18n: __ } });
}

View File

@ -1,22 +1,22 @@
import { __ as i__ } from "i18n";
import config from "../config";
import * as viewsv1 from "@hibas123/openauth-views-v1";
export default function GetAuthPage(
__: typeof i__,
appname: string,
scopes: { name: string; description: string; logo: string }[]
): string {
return viewsv1.authorize(config.core.dev)(
{
title: __("Authorize %s", appname),
information: __(
"By clicking on ALLOW, you allow this app to access the requested recources."
),
scopes: scopes,
// request: request
},
{ helpers: { i18n: __ } }
);
}
import { __ as i__ } from "i18n";
import config from "../config.js";
import * as viewsv1 from "@hibas123/openauth-views-v1";
export default function GetAuthPage(
__: typeof i__,
appname: string,
scopes: { name: string; description: string; logo: string }[]
): string {
return viewsv1.authorize(config.core.dev)(
{
title: __("Authorize %s", appname),
information: __(
"By clicking on ALLOW, you allow this app to access the requested recources."
),
scopes: scopes,
// request: request
},
{ helpers: { i18n: __ } }
);
}

View File

@ -7,12 +7,12 @@ import {
} from "express";
import * as Handlebars from "handlebars";
import moment = require("moment");
import { GetUserMiddleware, UserMiddleware } from "../api/middlewares/user";
import GetAuthRoute from "../api/oauth/auth";
import config from "../config";
import { HttpStatusCode } from "../helper/request_error";
import GetAdminPage from "./admin";
import GetRegistrationPage from "./register";
import { GetUserMiddleware, UserMiddleware } from "../api/middlewares/user.js";
import GetAuthRoute from "../api/oauth/auth.js";
import config from "../config.js";
import { HttpStatusCode } from "../helper/request_error.js";
import GetAdminPage from "./admin.js";
import GetRegistrationPage from "./register.js";
import * as path from "path";
const viewsv2_location = path.join(path.dirname(require.resolve("@hibas123/openauth-views-v2")), "build");

View File

@ -1,9 +1,9 @@
import { __ as i__ } from "i18n";
import config from "../config";
import * as viewsv1 from "@hibas123/openauth-views-v1";
export default function GetRegistrationPage(__: typeof i__): string {
let data = {};
return viewsv1.register(config.core.dev)(data, { helpers: { i18n: __ } });
}
import { __ as i__ } from "i18n";
import config from "../config.js";
import * as viewsv1 from "@hibas123/openauth-views-v1";
export default function GetRegistrationPage(__: typeof i__): string {
let data = {};
return viewsv1.register(config.core.dev)(data, { helpers: { i18n: __ } });
}

View File

@ -1,4 +1,4 @@
import config, { WebConfig } from "./config";
import config, { WebConfig } from "./config.js";
import express from "express";
import { Express } from "express";
@ -11,14 +11,12 @@ import session from "express-session";
import MongoStore from "connect-mongo";
import i18n from "i18n";
import compression from "compression";
import ApiRouter from "./api";
import ViewRouter from "./views";
import RequestError, { HttpStatusCode } from "./helper/request_error";
import DB from "./database";
import promiseMiddleware from "./helper/promiseMiddleware";
import User from "./models/user";
import LoginToken, { CheckToken } from "./models/login_token";
import ApiRouter from "./api/index.js";
import ViewRouter from "./views/index.js";
import RequestError, { HttpStatusCode } from "./helper/request_error.js";
import DB from "./database.js";
import promiseMiddleware from "./helper/promiseMiddleware.js";
import User from "./models/user.js";
export default class Web {
server: Express;
@ -41,7 +39,7 @@ export default class Web {
}
private registerMiddleware() {
this.server.use(session({
const sess = session({
secret: config.core.secret,
resave: false,
saveUninitialized: false,
@ -57,7 +55,9 @@ export default class Web {
secure: !config.core.dev,
sameSite: "strict",
}
}))
});
this.server.use(sess as any) // FIXME: These types seem to be brokenb, but they shouldn't
this.server.use(cookieparser());
this.server.use(
bodyparser.json(),
@ -103,16 +103,17 @@ export default class Web {
next();
});
this.server.use(
compression({
filter: (req, res) => {
if (req.headers["x-no-compression"]) {
return false;
}
return compression.filter(req, res);
},
})
);
// Compression will be handled by the reverse proxy!
// this.server.use(
// compression({
// filter: (req, res) => {
// if (req.headers["x-no-compression"]) {
// return false;
// }
// return compression.filter(req, res);
// },
// })
// );
}
private registerEndpoints() {

View File

@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "commonjs",
"target": "esnext",
"isolatedModules": true,
"noEmit": false,
"allowImportingTsExtensions": false,
"module": "nodenext",
"moduleResolution": "nodenext",
"declaration": true,
"sourceMap": true,
"outDir": "./lib",
@ -11,7 +15,14 @@
"emitDecoratorMetadata": true,
"esModuleInterop": true
},
"exclude": ["node_modules/"],
"files": ["src/express.d.ts"],
"include": ["./src"]
"exclude": [
"node_modules/",
"../node_modules/",
],
"files": [
"src/express.d.ts"
],
"include": [
"./src"
]
}

View File

@ -6,36 +6,36 @@
"@hibas123/theme": "^2.0.7",
"@hibas123/utils": "^2.2.18",
"@popperjs/core": "^2.11.8",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-html": "^1.0.3",
"@rollup/plugin-commonjs": "^28.0.6",
"@rollup/plugin-html": "^2.0.0",
"@rollup/plugin-image": "^3.0.3",
"@rollup/plugin-node-resolve": "^15.0.2",
"@simplewebauthn/browser": "^7.2.0",
"@tsconfig/svelte": "^4.0.1",
"@types/cleave.js": "^1.4.7",
"autoprefixer": "^10.4.14",
"classnames": "^2.3.2",
"@rollup/plugin-node-resolve": "^16.0.1",
"@simplewebauthn/browser": "^13.2.0",
"@tsconfig/svelte": "^5.0.5",
"@types/cleave.js": "^1.4.12",
"autoprefixer": "^10.4.21",
"classnames": "^2.5.1",
"cleave.js": "^1.6.0",
"cssnano": "^6.0.1",
"esbuild": "^0.17.16",
"flowbite": "^1.6.5",
"flowbite-svelte": "^0.34.9",
"joi": "^17.11.0",
"postcss": "^8.4.31",
"postcss-import": "^15.1.0",
"cssnano": "^7.1.1",
"esbuild": "^0.25.9",
"flowbite": "^3.1.2",
"flowbite-svelte": "^1.13.8",
"joi": "^18.0.1",
"postcss": "^8.5.6",
"postcss-import": "^16.1.1",
"postcss-url": "^10.1.3",
"rollup": "^3.20.2",
"rollup-plugin-esbuild": "^5.0.0",
"rollup": "^4.50.2",
"rollup-plugin-esbuild": "^6.2.1",
"rollup-plugin-hash": "^1.3.0",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-sizes": "^1.0.6",
"rollup-plugin-svelte": "^7.1.4",
"rollup-plugin-visualizer": "^5.9.0",
"svelte": "^3.58.0",
"svelte-preprocess": "^5.0.3",
"tailwindcss": "^3.3.1",
"typescript": "^5.0.4",
"rollup-plugin-sizes": "^1.1.0",
"rollup-plugin-svelte": "^7.2.3",
"rollup-plugin-visualizer": "^6.0.3",
"svelte": "^5.38.10",
"svelte-preprocess": "^6.0.3",
"tailwindcss": "^4.1.13",
"typescript": "^5.9.2",
"what-the-pack": "^2.0.3"
},
"scripts": {

View File

@ -8,21 +8,21 @@
"watch": "node build.js watch"
},
"dependencies": {
"handlebars": "^4.7.7"
"handlebars": "^4.7.8"
},
"devDependencies": {
"@material/button": "^5.1.0",
"@material/form-field": "^5.1.0",
"@material/radio": "^5.1.0",
"chokidar": "^3.5.3",
"gzip-size": "^6.0.0",
"@material/button": "^14.0.0",
"@material/form-field": "^14.0.0",
"@material/radio": "^14.0.0",
"chokidar": "^4.0.3",
"gzip-size": "^7.0.0",
"html-minifier": "^4.0.0",
"preact": "^10.13.2",
"rollup": "^3.20.2",
"preact": "^10.27.2",
"rollup": "^4.50.2",
"rollup-plugin-includepaths": "^0.2.4",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-typescript2": "^0.34.1",
"sass": "^1.61.0",
"typescript": "^5.0.4"
"rollup-plugin-typescript2": "^0.36.0",
"sass": "^1.92.1",
"typescript": "^5.9.2"
}
}

View File

@ -14,6 +14,6 @@
"author": "Fabian Stamm <Fabian.Stamm@polizei.hessen.de>",
"license": "ISC",
"devDependencies": {
"typescript": "^5.0.4"
"typescript": "^5.9.2"
}
}

View File

@ -1,21 +1,22 @@
{
"compilerOptions": {
"module": "ESNext",
"target": "esnext",
"moduleResolution": "node",
"outDir": "esm/",
"sourceMap": true,
"declaration": true,
"noImplicitAny": false,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"preserveWatchOutput": true
},
"exclude": [
"node_modules"
],
"include": [
"src"
]
}
{
"compilerOptions": {
"module": "ESNext",
"target": "esnext",
"moduleResolution": "node",
"outDir": "esm/",
"sourceMap": true,
"declaration": true,
"noImplicitAny": false,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"preserveWatchOutput": true
},
"exclude": [
"node_modules",
"../node_modules"
],
"include": [
"src"
]
}

View File

@ -1,21 +1,22 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "esnext",
"moduleResolution": "node",
"outDir": "lib/",
"sourceMap": true,
"declaration": true,
"noImplicitAny": false,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"preserveWatchOutput": true
},
"exclude": [
"node_modules"
],
"include": [
"src"
]
}
{
"compilerOptions": {
"module": "commonjs",
"target": "esnext",
"moduleResolution": "node",
"outDir": "lib/",
"sourceMap": true,
"declaration": true,
"noImplicitAny": false,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"preserveWatchOutput": true
},
"exclude": [
"node_modules",
"../node_modules"
],
"include": [
"src"
]
}

View File

@ -18,6 +18,6 @@
"_API"
],
"dependencies": {
"@hibas123/jrpcgen": "^1.2.14"
"@hibas123/jrpcgen": "^1.2.20"
}
}

4667
yarn.lock

File diff suppressed because it is too large Load Diff