softwarebrothers / adminjs-expressjs Goto Github PK
View Code? Open in Web Editor NEWThis is an official AdminJS plugin which integrates it to expressjs framework.
License: MIT License
This is an official AdminJS plugin which integrates it to expressjs framework.
License: MIT License
Just noticed that there is no try-catch blocks around await auth.authenticate(email, password, context)
so due to the nature of express if that function throws - whole node process dies.
Question is - is this intentional decision (cannot find any mention in AdminJS documentation) or misunderstanding?
Using this function to check the authenticated access to the admin panel:
const router = AdminBroExpress.buildAuthenticatedRouter(adminBro, {
authenticate: async (email, password) => {
if (ADMIN.password === password && ADMIN.email === email) {
return ADMIN
}
return null
},
cookieName: 'somename',
cookiePassword: 'somepassword',
})
How can I use custom login page?
Lukáš Novotný wrote on slack:
Adminbro end in endless redirect loop when I set rootPath: "/"? It does a redirect to /login but it end in redirect loop.
const server = express()
const adminBro = new AdminBro({
rootPath: "/",
loginPath: "/login",
logoutPath: "/logout",
...
})
const adminRouter = buildAdminRouter(adminBro)
server.use("/", adminRouter)
server.listen(3000, () => {
console.log(`Server running at 3000`)
})
Hi 👋
First of all thanks for AdminJS, I am very happy to use it and it saves us a lot of time in our Company.
We did some tests recently about authentication, and it seems that there is a hole in the admin route management.
Here is what we observed:
/admin
rootPathauthenticate
handler/admin
prompt the expected login screen/admin/
shows to the dashboard and bypass the login screen 🚨We are using AdminJS in a NestJS 8.4.2 context, with AdminJS 5.7.4 and authentication enabled:
auth: {
authenticate: authenticateAdmin,
cookieName: adminCookieInfo.name,
cookiePassword: adminCookieInfo.password,
},
sessionOptions: {
secret: adminCookieInfo.password,
store: new PGStore({
pool: pgSessionPool,
tableName: 'user_session',
}),
},
adminJsOptions: {
rootPath: '/admin',
}
After some investigations, it seems that the problem is located in the isAdminRoute
function from the src/authentication/protected-routes.handler.ts
file when the isAdminRootUrl
is set.
At this point, url
and adminRootPath
are different: url
is /admin/
(the URL from my test), and adminRootPath
is /admin
.
As I do not fully understand the consequences of changing the code in this file, I prefer not to suggest a PR and only report an issue.
Edit: updating to the latest AdminJS / @adminjs/express (6.2.3 / 5.0.0) does not fix the issue unfortunately.
Actual behaviour:
<admin_url>/resources/MyResource/records/SomeId
<admin_url>/resources/MyResource/records/SomeId
Expected:
<admin_url>/resources/MyResource/records/SomeId
<admin_url>/resources/MyResource/records/SomeId
It would be nice to have feature, good to be picked up as first issue I think
After changing the root path to "" the login redirect breaks and is stuck with the message "Found. Redirecting to"
Hello there,
I am using this package in one of my project and I noticed that after updating to 4.0.2 the authentication is not working as intended.
When i visit the url http://localhost:8080/admin
everything works fine, it redirects me to the login route. When i visit http://localhost:8080/admin/
(with the / in the end) it just lets me in and i can see the dashboard.
I can also see the the resource urls for example http://localhost:8080/admin/resources/User
I made a small test project so you can test it out.
package.json
{
"dependencies": {
"@adminjs/express": "^4.0.2",
"@adminjs/mongoose": "^2.0.0",
"adminjs": "^5.6.1",
"express": "^4.17.2",
"express-formidable": "^1.2.0",
"mongoose": "^6.2.1"
}
}
app.js
const AdminJS = require('adminjs');
const AdminJSExpress = require('@adminjs/express');
const AdminJSMongoose = require('@adminjs/mongoose');
const mongoose = require('mongoose');
const express = require('express');
const app = express();
AdminJS.registerAdapter(AdminJSMongoose);
const run = async () => {
const User = mongoose.model('User', { name: String, email: String, surname: String });
const mongooseDb = await mongoose.connect('mongodb://localhost:27017/adminjsissue', { useNewUrlParser: true })
const adminJs = new AdminJS({
resources: [User],
rootPath: '/admin',
});
const Admin = {
email: "[email protected]",
password: "password"
}
const router = AdminJSExpress.buildAuthenticatedRouter(adminJs, {
authenticate: async (email, password) => {
if (Admin.email === email && Admin.password === password) {
return Admin;
}
return false;
},
cookiePassword: 'some-secret-password-used-to-secure-cookie',
}, null, {
resave: false,
saveUninitialized: true
});
const port = 8080;
const adminPath = adminJs.options.rootPath;
app.use(adminPath, router);
app.listen(port, () => console.log(`AdminJS is under localhost:${port}${adminPath}`));
}
run();
If you try it with "@adminjs/express": "4.0.1"
it will work just fine.
I've tried implementing the example of an authenticated router provided here but I am running into an error message.
I have changed to use my DB_URI and I am able to access DB_URI both locally and remotely on my fly.io app, but in both cases I run into this error:
const adminRouter = AdminJSExpress.buildAuthenticatedRouter(
^
TypeError: AdminJSExpress.buildAuthenticatedRouter is not a function
at start (etc)
Here is the code I am running, based off that adminjsexpress example:
import AdminJS from 'adminjs'
import * as AdminJSExpress from '@adminjs/express'
import express from 'express'
import Connect from 'connect-pg-simple'
import session from 'express-session'
// import from 'express-session'
import * as dotenv from 'dotenv' // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
dotenv.config()
const PORT = 3000
const DEFAULT_ADMIN = {
email: '[email protected]',
password: 'password',
}
const authenticate = async (email, password) => {
if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {
return Promise.resolve(DEFAULT_ADMIN)
}
return null
}
console.log("hi");
console.log(process.env.DB_URI);
const start = async () => {
const app = express()
const admin = new AdminJS({})
const ConnectSession = Connect(session)
const sessionStore = new ConnectSession({
conObject: {
connectionString: process.env.DB_URI,
ssl: process.env.NODE_ENV === 'production',
},
tableName: 'session',
createTableIfMissing: true,
})
const adminRouter = AdminJSExpress.buildAuthenticatedRouter(
admin,
{
authenticate,
cookieName: 'adminjs',
cookiePassword: 'sessionsecret',
},
null,
{
store: sessionStore,
resave: true,
saveUninitialized: true,
secret: 'sessionsecret',
cookie: {
httpOnly: process.env.NODE_ENV === 'production',
secure: process.env.NODE_ENV === 'production',
},
name: 'adminjs',
}
)
app.use(admin.options.rootPath, adminRouter)
app.listen(PORT, () => {
console.log(`AdminJS started on http://localhost:${PORT}${admin.options.rootPath}`)
})
}
start()
PR: #35
Currently after POST
on loginPath
you're being redirected to either rootPath
or redirectTo
from session. The problem is, it uses 307 redirect which keeps the request method and form data.
In some circumstances (eg. using with nestjs) you might end up with redirecting to the rootPath
AS a POST
request, so you'll see the message that route POST ${rootPath}
does not exist.
Redirect from POST
loginPath
using 302 status
node_modules/@admin-bro/express/lib/buildAuthenticatedRouter.js:1
Error: Cannot find module 'tslib'
Warnings
express-session deprecated undefined resave option; provide resave option node_modules/admin-bro-expressjs/plugin.js:131:14
express-session deprecated undefined saveUninitialized option; provide saveUninitialized option node_modules/admin-bro-expressjs/plugin.js:131:14
Versions
"admin-bro": "^1.3.5",
"admin-bro-expressjs": "^0.2.0",
"admin-bro-sequelizejs": "^0.2.6",
"admin-bro-theme-dark": "^1.0.0",
"cookie-parser": "~1.4.4",
"express": "~4.16.1",
"express-session": "^1.17.0",
We are looking into using Admin Bro and would like the option to be able to use cookie-session over express-session, as client side cookies would be more convenient in our use case.
We are currently implementing our own buildAuthenticatedRouter
style function to hook into our auth system.
Backend api built with nestjs serves call at /
, endpoints looks like /users/me
or /auth/login
etc.
Before adding AdminBro, call to /auth/login
with post body of {"email": "<some-email>", "password": "<some-password>" }
works fine.
But when adding a module named Backoffice to my app.module.ts post body is not found when calling /auth/login and throws an exception in the controller.
All calls to AdminBro works fine under /admin
@Module({
imports: [
AdminModule.createAdminAsync({
imports: [
TypeOrmModule.forRootAsync({
imports: [SharedModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => configService.typeOrmConfig,
}),
UsersModule,
],
inject: [UsersService, ConfigService],
useFactory: (usersService: UsersService, configService: ConfigService) => {
return {
adminBroOptions: {
rootPath: '/admin',
resources: AdminBroResources,
},
auth: {
authenticate: async (email, password) => {
const user = await usersService.findOne({
email: email,
password: ConfigService.getPasswordsHash(password),
});
if (user) {
return { email: `${user.firstName}`, title: user.email.split('@')[0] };
}
return false;
},
cookiePassword: configService.get('COOKIE_SECRET'),
cookieName: 'backoffice',
},
} as AdminModuleOptions;
},
}),
],
// providers: [UsersService],
})
export class BackofficeModule {}
package.json dependencies used:
"@admin-bro/express": "^3.0.1", "@admin-bro/nestjs": "^1.0.0", "@admin-bro/typeorm": "^1.3.0", "express-formidable": "^1.2.0",
I've narrowed it down to ExpressLoader
in this repo which moves jsonParser and urlencodeparser in layer stack below AdminBro router. Commenting this out breaks AdminBro but nestjs api works again.
Describe the bug
Providing any valid query param combination, allows users to bypass the cookie check
Installed libraries and their versions
To Reproduce
Steps to reproduce the behavior:
Expected behavior
You should be redirected to the login page
express-session deprecated undefined resave option; provide resave option at node_modules/@adminjs/express/lib/buildAuthenticatedRouter
express-session deprecated undefined saveUninitialized option; provide saveUninitialized option at node_modules/@adminjs/express/lib/buildAuthenticatedRouter.js
I get that deprecation warning above, which needs to specify the resave and the saveUninitialized options to the value you want to use. See https://github.com/expressjs/session#options for their values and meanings.
Cannot find module '@adminjs/mongoose' or its corresponding type declarations.
There are types at 'c:/Users/Aman/Desktop/Project/mlcoe/quiz_backend/node_modules/@adminjs/mongoose/lib/index.d.ts', but this result could not be resolved under your current 'moduleResolution' setting. Consider updating to 'node16', 'nodenext', or 'bundler'.ts(2307)
Here my ts config file
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"strict": true,
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"baseUrl": ".",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strictPropertyInitialization": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"paths": {
"": [
"node_modules/"
]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
When visiting http://localhost:8000/admin it shows following error:
TypeError: path_to_regexp_1.pathToRegexp is not a function at /Users/yenliksharipkhanova/Documents/projects/yenbek/rest/node_modules/@adminjs/express/src/authentication/protected-routes.handler.ts:59:7 at Array.find (<anonymous>) at Object.isAdminRoute (/Users/yenliksharipkhanova/Documents/projects/yenbek/rest/node_modules/@adminjs/express/src/authentication/protected-routes.handler.ts:58:19) at /Users/yenliksharipkhanova/Documents/projects/yenbek/rest/node_modules/@adminjs/express/src/authentication/protected-routes.handler.ts:22:16 at Layer.handle [as handle_request] (/Users/yenliksharipkhanova/Documents/projects/yenbek/rest/node_modules/express/lib/router/layer.js:95:5) at trim_prefix (/Users/yenliksharipkhanova/Documents/projects/yenbek/rest/node_modules/express/lib/router/index.js:323:13) at /Users/yenliksharipkhanova/Documents/projects/yenbek/rest/node_modules/express/lib/router/index.js:284:7 at Function.process_params (/Users/yenliksharipkhanova/Documents/projects/yenbek/rest/node_modules/express/lib/router/index.js:341:12) at next (/Users/yenliksharipkhanova/Documents/projects/yenbek/rest/node_modules/express/lib/router/index.js:275:10) at /Users/yenliksharipkhanova/Documents/projects/yenbek/rest/node_modules/express-formidable/lib/middleware.js:36:7
The package.json:
{
"dependencies": {
"@adminjs/express": "^4.1.0",
"@adminjs/sequelize": "^2.1.5",
"@sentry/node": "^6.19.2",
"@sentry/tracing": "^6.19.2",
"@types/express-session": "^1.17.4",
"adminjs": "^5.9.9",
"aws-sdk": "^2.1111.0",
"bcrypt": "^5.0.1",
"body-parser": "^1.20.0",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"detect-libc": "^2.0.1",
"express": "^4.17.2",
"express-formidable": "^1.2.0",
"express-session": "^1.17.2",
"express-validator": "^6.14.0",
"file-type": "^17.1.1",
"firebase": "^9.6.5",
"firebase-admin": "^10.0.2",
"formidable": "^2.0.1",
"multiparty": "^4.2.3",
"node-gyp": "^9.0.0",
"pg": "^8.7.1",
"pg-hstore": "^2.3.4",
"reflect-metadata": "^0.1.13",
"sequelize": "^6.17.0",
"sequelize-typescript": "^2.1.3",
"sqlite3": "^5.0.2",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/bcrypt": "^5.0.0",
"@types/compression": "^1.7.2",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"@types/multiparty": "^0.0.33",
"@types/node": "^14.18.12",
"@types/uuid": "^8.3.4",
"@types/validator": "^13.7.1",
"@typescript-eslint/eslint-plugin": "^5.10.1",
"@typescript-eslint/parser": "^5.10.1",
"eslint": "^8.7.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-prettier": "^4.0.0",
"nodemon": "^2.0.15",
"prettier": "2.5.1",
"ts-node": "^10.4.0",
"tsconfig-paths": "^3.12.0",
"tslint": "^6.1.3",
"typescript": "^4.5.5"
}
}
After downgrading @adminjs/express to 4.0.1 it starts to work fine. Both 4.0.2, 4.0.3 not working.
Can you please help with this issue?
Authentication works fine on the development environment. But if I set the NODE_ENV to production, it won't.
I see the server is still responding with 200 OK, but it's getting redirected to the login page.
Also, I noticed a warning,
express-session deprecated undefined resave option;
express-session deprecated undefined saveUninitialized option
Warning: connect.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and will not scale past a single process.
It would be nice if the default cookie parser and session is configured only no predefined router is passed. Or at least an option to skip that, so that I can pass a predefined router with session configuration. I'm not using the buildRouter
since I want the authentication methods.
Typescript failing to execute due to error TS2339
AdminJSExpress
is failing to be identified proper in typing.
My versions:
Code example:
import * as express from 'express'
import AdminJS from 'adminjs'
import * as AdminJSExpress from '@adminjs/express'
const adminJs = new AdminJS({
databases: [],
rootPath: '/admin',
})
const app = express()
// This line fails with:
// TS2339: Property 'buildRouter' does not exist on type 'typeof import("/project/path/node_modules/@adminjs/express/lib/index")'
const router = AdminJSExpress.buildRouter(adminJs)
app.use(adminJs.options.rootPath, router)
app.listen(8080, () => console.log('AdminJS is running under localhost:8080/admin'))
Workaround is to add a comment // @ts-ignore TS2339
:
// @ts-ignore TS2339
const router = AdminJSExpress.buildRouter(adminJs)
This is necessary when making any calls to AdminJSExpress
Also if helpful for debugging here is a copy of my .tsconfig
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"strict": true,
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist",
"baseUrl": ".",
"forceConsistentCasingInFileNames": true,
"strictPropertyInitialization": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"paths": {
"*": [
"node_modules/*"
]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
I noticed some errors when I run that inside a NODE_ENV=production env.
express-session deprecated undefined resave option; provide resave option at node_modules/admin-bro-expressjs/plugin.js:131:14
express-session deprecated undefined saveUninitialized option; provide saveUninitialized option at node_modules/admin-bro-expressjs/plugin.js:131:14
Warning: connect.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and will not scale past a single process.
We should have a options to set express.js values directly.
Something like that in plugin.js :
const buildAuthenticatedRouter = (admin, auth, predefinedRouter, expressOptions = {}) => {
if (!cookieParser || !session) {
throw new Error(['In order to use authentication, you have to install',
'cookie-parser and express-session packages'].join(' '))
}
const router = predefinedRouter || express.Router()
router.use(cookieParser())
router.use(session({
secret: auth.cookiePassword,
name: auth.cookieName || 'adminbro',
...expressOptions
}))
When logging in the handler redirects to req.session.redirectTo
, but this is no longer happening because it's not being set by the protected routes handler anymore.
Related discussion #81
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.