@mostajs/auth 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/PermissionGuard.js +7 -10
- package/dist/components/SessionProvider.js +4 -7
- package/dist/hooks/usePermissions.d.ts +1 -0
- package/dist/hooks/usePermissions.js +7 -8
- package/dist/index.d.ts +2 -1
- package/dist/index.js +18 -44
- package/dist/lib/auth-check.js +16 -13
- package/dist/lib/auth.d.ts +1 -1
- package/dist/lib/auth.js +29 -21
- package/dist/lib/password.js +5 -12
- package/dist/lib/permissions-server.js +4 -7
- package/dist/lib/permissions.js +2 -6
- package/dist/lib/rbac-seed.d.ts +1 -1
- package/dist/lib/rbac-seed.js +9 -12
- package/dist/middleware/auth-middleware.js +10 -11
- package/dist/repositories/permission-category.repository.d.ts +1 -1
- package/dist/repositories/permission-category.repository.js +4 -8
- package/dist/repositories/permission.repository.d.ts +1 -1
- package/dist/repositories/permission.repository.js +4 -8
- package/dist/repositories/role.repository.d.ts +1 -1
- package/dist/repositories/role.repository.js +4 -8
- package/dist/repositories/user.repository.d.ts +1 -1
- package/dist/repositories/user.repository.js +4 -8
- package/dist/schemas/permission-category.schema.js +1 -4
- package/dist/schemas/permission.schema.js +1 -4
- package/dist/schemas/role.schema.js +1 -4
- package/dist/schemas/user.schema.js +1 -4
- package/dist/types/index.d.ts +0 -2
- package/dist/types/index.js +1 -2
- package/package.json +39 -7
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// @mosta/auth — PermissionGuard component
|
|
3
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
4
3
|
'use client';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
-
const usePermissions_1 = require("../hooks/usePermissions");
|
|
4
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
5
|
+
import { usePermissions } from '../hooks/usePermissions';
|
|
9
6
|
/**
|
|
10
7
|
* Conditionally renders children based on user permissions.
|
|
11
8
|
* Admin users bypass all permission checks.
|
|
12
9
|
*/
|
|
13
|
-
function PermissionGuard({ permissions, requireAll = false, children, fallback = null, adminRole = 'admin', }) {
|
|
14
|
-
const { hasPermission, hasAnyPermission, isAdmin } =
|
|
10
|
+
export default function PermissionGuard({ permissions, requireAll = false, children, fallback = null, adminRole = 'admin', }) {
|
|
11
|
+
const { hasPermission, hasAnyPermission, isAdmin } = usePermissions(adminRole);
|
|
15
12
|
if (isAdmin())
|
|
16
|
-
return (
|
|
13
|
+
return _jsx(_Fragment, { children: children });
|
|
17
14
|
const hasAccess = requireAll
|
|
18
15
|
? permissions.every((p) => hasPermission(p))
|
|
19
16
|
: hasAnyPermission(permissions);
|
|
20
17
|
if (!hasAccess)
|
|
21
|
-
return (
|
|
22
|
-
return (
|
|
18
|
+
return _jsx(_Fragment, { children: fallback });
|
|
19
|
+
return _jsx(_Fragment, { children: children });
|
|
23
20
|
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// @mosta/auth — NextAuth SessionProvider wrapper
|
|
3
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
4
3
|
'use client';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function SessionProvider({ children }) {
|
|
10
|
-
return (0, jsx_runtime_1.jsx)(react_1.SessionProvider, { children: children });
|
|
4
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
|
+
import { SessionProvider as NextAuthSessionProvider } from 'next-auth/react';
|
|
6
|
+
export default function SessionProvider({ children }) {
|
|
7
|
+
return _jsx(NextAuthSessionProvider, { children: children });
|
|
11
8
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
export declare function usePermissions(adminRole?: string): {
|
|
8
8
|
permissions: string[];
|
|
9
9
|
role: string;
|
|
10
|
+
roles: string[];
|
|
10
11
|
hasPermission: (permission: string) => boolean;
|
|
11
12
|
hasAnyPermission: (permissions: string[]) => boolean;
|
|
12
13
|
hasRole: (role: string) => boolean;
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// @mosta/auth — usePermissions hook (client-side)
|
|
3
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
4
3
|
'use client';
|
|
5
|
-
|
|
6
|
-
exports.usePermissions = usePermissions;
|
|
7
|
-
const react_1 = require("next-auth/react");
|
|
4
|
+
import { useSession } from 'next-auth/react';
|
|
8
5
|
/**
|
|
9
6
|
* Client-side hook for permission checking.
|
|
10
7
|
* Reads permissions from the NextAuth session.
|
|
11
8
|
*
|
|
12
9
|
* @param adminRole - The role name considered as admin (default: 'admin')
|
|
13
10
|
*/
|
|
14
|
-
function usePermissions(adminRole = 'admin') {
|
|
15
|
-
const { data: session } =
|
|
11
|
+
export function usePermissions(adminRole = 'admin') {
|
|
12
|
+
const { data: session } = useSession();
|
|
16
13
|
const userPermissions = session?.user?.permissions || [];
|
|
17
14
|
const userRole = session?.user?.role || '';
|
|
15
|
+
const userRoles = session?.user?.roles || [];
|
|
18
16
|
function hasPermission(permission) {
|
|
19
17
|
if (!userPermissions || userPermissions.length === 0)
|
|
20
18
|
return false;
|
|
@@ -26,10 +24,10 @@ function usePermissions(adminRole = 'admin') {
|
|
|
26
24
|
return permissions.some((p) => hasPermission(p));
|
|
27
25
|
}
|
|
28
26
|
function hasRole(role) {
|
|
29
|
-
return userRole === role;
|
|
27
|
+
return userRoles.includes(role) || userRole === role;
|
|
30
28
|
}
|
|
31
29
|
function isAdmin() {
|
|
32
|
-
return userRole === adminRole;
|
|
30
|
+
return userRoles.includes(adminRole) || userRole === adminRole;
|
|
33
31
|
}
|
|
34
32
|
function canAccess(permission) {
|
|
35
33
|
if (isAdmin())
|
|
@@ -39,6 +37,7 @@ function usePermissions(adminRole = 'admin') {
|
|
|
39
37
|
return {
|
|
40
38
|
permissions: userPermissions,
|
|
41
39
|
role: userRole,
|
|
40
|
+
roles: userRoles,
|
|
42
41
|
hasPermission,
|
|
43
42
|
hasAnyPermission,
|
|
44
43
|
hasRole,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createAuthHandlers } from './lib/auth';
|
|
2
2
|
export { createAuthChecks } from './lib/auth-check';
|
|
3
3
|
export { createAuthMiddleware } from './middleware/auth-middleware';
|
|
4
|
+
export type { AuthMiddlewareOptions } from './middleware/auth-middleware';
|
|
4
5
|
export { hashPassword, comparePassword } from './lib/password';
|
|
5
6
|
export { hasPermission, getPermissionsForRole } from './lib/permissions';
|
|
6
7
|
export { getPermissionsForRoleFromDB } from './lib/permissions-server';
|
|
@@ -17,4 +18,4 @@ export { PermissionCategoryRepository } from './repositories/permission-category
|
|
|
17
18
|
export { usePermissions } from './hooks/usePermissions';
|
|
18
19
|
export { default as PermissionGuard } from './components/PermissionGuard';
|
|
19
20
|
export { default as SessionProvider } from './components/SessionProvider';
|
|
20
|
-
export type { MostaAuthConfig, PermissionDefinition, RoleDefinition, CategoryDefinition, UserDTO, RoleDTO, PermissionDTO, PermissionCategoryDTO, } from './types';
|
|
21
|
+
export type { MostaAuthConfig, PermissionDefinition, RoleDefinition, CategoryDefinition, UserDTO, RoleDTO, PermissionDTO, PermissionCategoryDTO, } from './types/index';
|
package/dist/index.js
CHANGED
|
@@ -1,55 +1,29 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// @mosta/auth — Barrel exports
|
|
3
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
4
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
|
-
};
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.SessionProvider = exports.PermissionGuard = exports.usePermissions = exports.PermissionCategoryRepository = exports.PermissionRepository = exports.RoleRepository = exports.UserRepository = exports.PermissionCategorySchema = exports.PermissionSchema = exports.RoleSchema = exports.UserSchema = exports.seedRBAC = exports.getPermissionsForRoleFromDB = exports.getPermissionsForRole = exports.hasPermission = exports.comparePassword = exports.hashPassword = exports.createAuthMiddleware = exports.createAuthChecks = exports.createAuthHandlers = void 0;
|
|
9
3
|
// Auth factory
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Object.defineProperty(exports, "createAuthChecks", { enumerable: true, get: function () { return auth_check_1.createAuthChecks; } });
|
|
14
|
-
var auth_middleware_1 = require("./middleware/auth-middleware");
|
|
15
|
-
Object.defineProperty(exports, "createAuthMiddleware", { enumerable: true, get: function () { return auth_middleware_1.createAuthMiddleware; } });
|
|
4
|
+
export { createAuthHandlers } from './lib/auth';
|
|
5
|
+
export { createAuthChecks } from './lib/auth-check';
|
|
6
|
+
export { createAuthMiddleware } from './middleware/auth-middleware';
|
|
16
7
|
// Password utils
|
|
17
|
-
|
|
18
|
-
Object.defineProperty(exports, "hashPassword", { enumerable: true, get: function () { return password_1.hashPassword; } });
|
|
19
|
-
Object.defineProperty(exports, "comparePassword", { enumerable: true, get: function () { return password_1.comparePassword; } });
|
|
8
|
+
export { hashPassword, comparePassword } from './lib/password';
|
|
20
9
|
// Permission helpers (client-safe)
|
|
21
|
-
|
|
22
|
-
Object.defineProperty(exports, "hasPermission", { enumerable: true, get: function () { return permissions_1.hasPermission; } });
|
|
23
|
-
Object.defineProperty(exports, "getPermissionsForRole", { enumerable: true, get: function () { return permissions_1.getPermissionsForRole; } });
|
|
10
|
+
export { hasPermission, getPermissionsForRole } from './lib/permissions';
|
|
24
11
|
// Server-side permission DB lookup
|
|
25
|
-
|
|
26
|
-
Object.defineProperty(exports, "getPermissionsForRoleFromDB", { enumerable: true, get: function () { return permissions_server_1.getPermissionsForRoleFromDB; } });
|
|
12
|
+
export { getPermissionsForRoleFromDB } from './lib/permissions-server';
|
|
27
13
|
// RBAC seed
|
|
28
|
-
|
|
29
|
-
Object.defineProperty(exports, "seedRBAC", { enumerable: true, get: function () { return rbac_seed_1.seedRBAC; } });
|
|
14
|
+
export { seedRBAC } from './lib/rbac-seed';
|
|
30
15
|
// Schemas
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
var permission_schema_1 = require("./schemas/permission.schema");
|
|
36
|
-
Object.defineProperty(exports, "PermissionSchema", { enumerable: true, get: function () { return permission_schema_1.PermissionSchema; } });
|
|
37
|
-
var permission_category_schema_1 = require("./schemas/permission-category.schema");
|
|
38
|
-
Object.defineProperty(exports, "PermissionCategorySchema", { enumerable: true, get: function () { return permission_category_schema_1.PermissionCategorySchema; } });
|
|
16
|
+
export { UserSchema } from './schemas/user.schema';
|
|
17
|
+
export { RoleSchema } from './schemas/role.schema';
|
|
18
|
+
export { PermissionSchema } from './schemas/permission.schema';
|
|
19
|
+
export { PermissionCategorySchema } from './schemas/permission-category.schema';
|
|
39
20
|
// Repositories
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
var permission_repository_1 = require("./repositories/permission.repository");
|
|
45
|
-
Object.defineProperty(exports, "PermissionRepository", { enumerable: true, get: function () { return permission_repository_1.PermissionRepository; } });
|
|
46
|
-
var permission_category_repository_1 = require("./repositories/permission-category.repository");
|
|
47
|
-
Object.defineProperty(exports, "PermissionCategoryRepository", { enumerable: true, get: function () { return permission_category_repository_1.PermissionCategoryRepository; } });
|
|
21
|
+
export { UserRepository } from './repositories/user.repository';
|
|
22
|
+
export { RoleRepository } from './repositories/role.repository';
|
|
23
|
+
export { PermissionRepository } from './repositories/permission.repository';
|
|
24
|
+
export { PermissionCategoryRepository } from './repositories/permission-category.repository';
|
|
48
25
|
// Hooks
|
|
49
|
-
|
|
50
|
-
Object.defineProperty(exports, "usePermissions", { enumerable: true, get: function () { return usePermissions_1.usePermissions; } });
|
|
26
|
+
export { usePermissions } from './hooks/usePermissions';
|
|
51
27
|
// Components
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
var SessionProvider_1 = require("./components/SessionProvider");
|
|
55
|
-
Object.defineProperty(exports, "SessionProvider", { enumerable: true, get: function () { return __importDefault(SessionProvider_1).default; } });
|
|
28
|
+
export { default as PermissionGuard } from './components/PermissionGuard';
|
|
29
|
+
export { default as SessionProvider } from './components/SessionProvider';
|
package/dist/lib/auth-check.js
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createAuthChecks = createAuthChecks;
|
|
4
1
|
// @mosta/auth — Server-side auth guards
|
|
5
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import { NextResponse } from 'next/server';
|
|
4
|
+
import { hasPermission } from './permissions';
|
|
5
|
+
import { getPermissionsForRoleFromDB } from './permissions-server';
|
|
9
6
|
/**
|
|
10
7
|
* Create server-side guard functions bound to your auth() instance.
|
|
11
8
|
*
|
|
12
9
|
* Usage:
|
|
13
10
|
* const { checkAuth, checkPermission, getUserFromSession } = createAuthChecks(auth)
|
|
14
11
|
*/
|
|
15
|
-
function createAuthChecks(auth, fallbackMap) {
|
|
12
|
+
export function createAuthChecks(auth, fallbackMap) {
|
|
16
13
|
async function checkAuth() {
|
|
17
14
|
const session = await auth();
|
|
18
15
|
if (!session?.user) {
|
|
19
16
|
return {
|
|
20
|
-
error:
|
|
17
|
+
error: NextResponse.json({ error: { code: 'UNAUTHORIZED', message: 'Non authentifié' } }, { status: 401 }),
|
|
21
18
|
session: null,
|
|
22
19
|
};
|
|
23
20
|
}
|
|
@@ -29,14 +26,20 @@ function createAuthChecks(auth, fallbackMap) {
|
|
|
29
26
|
return { error, session: null };
|
|
30
27
|
let userPermissions = session.user.permissions || [];
|
|
31
28
|
if (userPermissions.length === 0) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
const roles = session.user.roles || [];
|
|
30
|
+
const singleRole = session.user.role;
|
|
31
|
+
const rolesToCheck = roles.length > 0 ? roles : singleRole ? [singleRole] : [];
|
|
32
|
+
for (const role of rolesToCheck) {
|
|
33
|
+
const perms = await getPermissionsForRoleFromDB(role, fallbackMap);
|
|
34
|
+
for (const p of perms) {
|
|
35
|
+
if (!userPermissions.includes(p))
|
|
36
|
+
userPermissions.push(p);
|
|
37
|
+
}
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
|
-
if (!
|
|
40
|
+
if (!hasPermission(userPermissions, requiredPermission)) {
|
|
38
41
|
return {
|
|
39
|
-
error:
|
|
42
|
+
error: NextResponse.json({ error: { code: 'FORBIDDEN', message: 'Permission insuffisante' } }, { status: 403 }),
|
|
40
43
|
session: null,
|
|
41
44
|
};
|
|
42
45
|
}
|
package/dist/lib/auth.d.ts
CHANGED
package/dist/lib/auth.js
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createAuthHandlers = createAuthHandlers;
|
|
7
1
|
// @mosta/auth — NextAuth configuration factory
|
|
8
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
import NextAuth from 'next-auth';
|
|
4
|
+
import CredentialsProvider from 'next-auth/providers/credentials';
|
|
5
|
+
import { getDialect, registerSchemas } from '@mostajs/orm';
|
|
6
|
+
import { UserRepository } from '../repositories/user.repository';
|
|
7
|
+
import { RoleRepository } from '../repositories/role.repository';
|
|
8
|
+
import { comparePassword } from './password';
|
|
9
|
+
import { UserSchema } from '../schemas/user.schema';
|
|
10
|
+
import { RoleSchema } from '../schemas/role.schema';
|
|
11
|
+
import { PermissionSchema } from '../schemas/permission.schema';
|
|
12
|
+
import { PermissionCategorySchema } from '../schemas/permission-category.schema';
|
|
13
|
+
// Auto-register auth schemas into ORM registry (idempotent)
|
|
14
|
+
// Must run before getDialect() to ensure relations are resolvable
|
|
15
|
+
registerSchemas([UserSchema, RoleSchema, PermissionSchema, PermissionCategorySchema]);
|
|
15
16
|
/**
|
|
16
17
|
* Create NextAuth handlers configured for MostaAuth RBAC.
|
|
17
18
|
*
|
|
18
19
|
* @param rolePermissions - Static role→permissions map (used as fallback)
|
|
19
20
|
* @param config - Optional MostaAuthConfig overrides
|
|
20
21
|
*/
|
|
21
|
-
function createAuthHandlers(rolePermissions, config) {
|
|
22
|
-
const { handlers, auth, signIn, signOut } = (
|
|
22
|
+
export function createAuthHandlers(rolePermissions, config) {
|
|
23
|
+
const { handlers, auth, signIn, signOut } = NextAuth({
|
|
23
24
|
secret: process.env.AUTH_SECRET || process.env.NEXTAUTH_SECRET,
|
|
24
25
|
trustHost: true,
|
|
25
26
|
debug: false,
|
|
@@ -31,7 +32,7 @@ function createAuthHandlers(rolePermissions, config) {
|
|
|
31
32
|
},
|
|
32
33
|
},
|
|
33
34
|
providers: [
|
|
34
|
-
(
|
|
35
|
+
CredentialsProvider({
|
|
35
36
|
name: 'credentials',
|
|
36
37
|
credentials: {
|
|
37
38
|
email: { label: 'Email', type: 'email' },
|
|
@@ -40,13 +41,13 @@ function createAuthHandlers(rolePermissions, config) {
|
|
|
40
41
|
async authorize(credentials) {
|
|
41
42
|
if (!credentials?.email || !credentials?.password)
|
|
42
43
|
return null;
|
|
43
|
-
const uRepo = new
|
|
44
|
+
const uRepo = new UserRepository(await getDialect());
|
|
44
45
|
const user = await uRepo.findByEmail(credentials.email);
|
|
45
46
|
if (!user)
|
|
46
47
|
return null;
|
|
47
48
|
if (user.status !== 'active')
|
|
48
49
|
return null;
|
|
49
|
-
const valid = await
|
|
50
|
+
const valid = await comparePassword(credentials.password, user.password);
|
|
50
51
|
if (!valid)
|
|
51
52
|
return null;
|
|
52
53
|
await uRepo.updateLastLogin(user.id);
|
|
@@ -70,8 +71,15 @@ function createAuthHandlers(rolePermissions, config) {
|
|
|
70
71
|
token.roles = user.roles;
|
|
71
72
|
token.permissions = user.permissions;
|
|
72
73
|
}
|
|
73
|
-
if (
|
|
74
|
-
|
|
74
|
+
if ((!token.permissions || token.permissions.length === 0) && token.roles) {
|
|
75
|
+
const allPerms = [];
|
|
76
|
+
for (const r of token.roles) {
|
|
77
|
+
for (const p of rolePermissions[r] || []) {
|
|
78
|
+
if (!allPerms.includes(p))
|
|
79
|
+
allPerms.push(p);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
token.permissions = allPerms;
|
|
75
83
|
}
|
|
76
84
|
return token;
|
|
77
85
|
},
|
|
@@ -95,8 +103,8 @@ function createAuthHandlers(rolePermissions, config) {
|
|
|
95
103
|
// ─── Internal: resolve user→roles→permissions ─────────────────
|
|
96
104
|
async function resolveUserPermissions(userId, fallbackMap) {
|
|
97
105
|
try {
|
|
98
|
-
const uRepo = new
|
|
99
|
-
const rRepo = new
|
|
106
|
+
const uRepo = new UserRepository(await getDialect());
|
|
107
|
+
const rRepo = new RoleRepository(await getDialect());
|
|
100
108
|
const userWithRoles = await uRepo.findByIdWithRoles(userId);
|
|
101
109
|
if (userWithRoles?.roles?.length) {
|
|
102
110
|
const roleNames = [];
|
package/dist/lib/password.js
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.hashPassword = hashPassword;
|
|
7
|
-
exports.comparePassword = comparePassword;
|
|
8
1
|
// @mosta/auth — Password hashing wrapper
|
|
9
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
10
|
-
|
|
3
|
+
import bcrypt from 'bcryptjs';
|
|
11
4
|
const DEFAULT_ROUNDS = 12;
|
|
12
|
-
async function hashPassword(plain, rounds = DEFAULT_ROUNDS) {
|
|
13
|
-
return
|
|
5
|
+
export async function hashPassword(plain, rounds = DEFAULT_ROUNDS) {
|
|
6
|
+
return bcrypt.hash(plain, rounds);
|
|
14
7
|
}
|
|
15
|
-
async function comparePassword(plain, hashed) {
|
|
16
|
-
return
|
|
8
|
+
export async function comparePassword(plain, hashed) {
|
|
9
|
+
return bcrypt.compare(plain, hashed);
|
|
17
10
|
}
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPermissionsForRoleFromDB = getPermissionsForRoleFromDB;
|
|
4
1
|
// @mosta/auth — Server-side permission lookup (DO NOT import client-side)
|
|
5
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { getDialect } from '@mostajs/orm';
|
|
4
|
+
import { RoleRepository } from '../repositories/role.repository';
|
|
8
5
|
/**
|
|
9
6
|
* Resolve permissions for a role by querying the database.
|
|
10
7
|
* Falls back to the provided static map if DB lookup fails.
|
|
11
8
|
*/
|
|
12
|
-
async function getPermissionsForRoleFromDB(role, fallbackMap) {
|
|
9
|
+
export async function getPermissionsForRoleFromDB(role, fallbackMap) {
|
|
13
10
|
try {
|
|
14
|
-
const repo = new
|
|
11
|
+
const repo = new RoleRepository(await getDialect());
|
|
15
12
|
const dbRole = await repo.findByName(role);
|
|
16
13
|
if (dbRole) {
|
|
17
14
|
const roleWithPerms = await repo.findByIdWithPermissions(dbRole.id);
|
package/dist/lib/permissions.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// @mosta/auth — Permission helpers (client-safe)
|
|
3
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.hasPermission = hasPermission;
|
|
6
|
-
exports.getPermissionsForRole = getPermissionsForRole;
|
|
7
3
|
/**
|
|
8
4
|
* Check if a user has a specific permission.
|
|
9
5
|
* Supports wildcard '*' for full access.
|
|
10
6
|
*/
|
|
11
|
-
function hasPermission(userPermissions, requiredPermission) {
|
|
7
|
+
export function hasPermission(userPermissions, requiredPermission) {
|
|
12
8
|
if (!userPermissions || userPermissions.length === 0)
|
|
13
9
|
return false;
|
|
14
10
|
if (userPermissions.includes('*'))
|
|
@@ -18,6 +14,6 @@ function hasPermission(userPermissions, requiredPermission) {
|
|
|
18
14
|
/**
|
|
19
15
|
* Get permissions for a role from a static map.
|
|
20
16
|
*/
|
|
21
|
-
function getPermissionsForRole(rolePermissions, role) {
|
|
17
|
+
export function getPermissionsForRole(rolePermissions, role) {
|
|
22
18
|
return rolePermissions[role] || [];
|
|
23
19
|
}
|
package/dist/lib/rbac-seed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CategoryDefinition, PermissionDefinition, RoleDefinition } from '../types';
|
|
1
|
+
import type { CategoryDefinition, PermissionDefinition, RoleDefinition } from '../types/index';
|
|
2
2
|
export interface SeedRBACOptions {
|
|
3
3
|
categories: CategoryDefinition[];
|
|
4
4
|
permissions: PermissionDefinition[];
|
package/dist/lib/rbac-seed.js
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.seedRBAC = seedRBAC;
|
|
4
1
|
// @mosta/auth — RBAC Seed function
|
|
5
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import { getDialect } from '@mostajs/orm';
|
|
4
|
+
import { PermissionCategoryRepository } from '../repositories/permission-category.repository';
|
|
5
|
+
import { PermissionRepository } from '../repositories/permission.repository';
|
|
6
|
+
import { RoleRepository } from '../repositories/role.repository';
|
|
10
7
|
/**
|
|
11
8
|
* Idempotent seed of categories, permissions and roles.
|
|
12
9
|
* Uses upsert — safe to call multiple times.
|
|
13
10
|
*/
|
|
14
|
-
async function seedRBAC(options) {
|
|
15
|
-
const dialect = await
|
|
16
|
-
const catRepo = new
|
|
17
|
-
const permRepo = new
|
|
18
|
-
const roleRepo = new
|
|
11
|
+
export async function seedRBAC(options) {
|
|
12
|
+
const dialect = await getDialect();
|
|
13
|
+
const catRepo = new PermissionCategoryRepository(dialect);
|
|
14
|
+
const permRepo = new PermissionRepository(dialect);
|
|
15
|
+
const roleRepo = new RoleRepository(dialect);
|
|
19
16
|
// 1. Upsert categories
|
|
20
17
|
for (const cat of options.categories) {
|
|
21
18
|
await catRepo.upsert({ name: cat.name }, cat);
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createAuthMiddleware = createAuthMiddleware;
|
|
4
1
|
// @mosta/auth — Configurable authentication middleware for Next.js
|
|
5
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
6
|
-
|
|
3
|
+
import { NextResponse } from 'next/server';
|
|
7
4
|
/**
|
|
8
5
|
* Create a Next.js middleware that protects routes based on session cookie.
|
|
9
6
|
*/
|
|
10
|
-
function createAuthMiddleware(options) {
|
|
7
|
+
export function createAuthMiddleware(options) {
|
|
11
8
|
const publicPaths = options?.publicPaths ?? ['/login', '/api/auth', '/setup', '/api/setup'];
|
|
12
9
|
const protectedPrefixes = options?.protectedPrefixes ?? ['/dashboard', '/admin', '/agent'];
|
|
13
10
|
const loginPath = options?.loginPath ?? '/login';
|
|
@@ -15,19 +12,21 @@ function createAuthMiddleware(options) {
|
|
|
15
12
|
const { pathname } = req.nextUrl;
|
|
16
13
|
// Allow public paths
|
|
17
14
|
if (publicPaths.some((p) => pathname.startsWith(p))) {
|
|
18
|
-
return
|
|
15
|
+
return NextResponse.next();
|
|
19
16
|
}
|
|
20
|
-
// Allow static files
|
|
21
|
-
if (pathname.startsWith('/_next') ||
|
|
22
|
-
|
|
17
|
+
// Allow static files (only known static extensions, not any path with a dot)
|
|
18
|
+
if (pathname.startsWith('/_next') ||
|
|
19
|
+
pathname.startsWith('/favicon') ||
|
|
20
|
+
/\.(?:ico|png|jpg|jpeg|gif|svg|webp|css|js|map|woff2?|ttf|eot|json|webmanifest)$/i.test(pathname)) {
|
|
21
|
+
return NextResponse.next();
|
|
23
22
|
}
|
|
24
23
|
// Check for session token
|
|
25
24
|
const token = req.cookies.get('authjs.session-token')?.value ||
|
|
26
25
|
req.cookies.get('__Secure-authjs.session-token')?.value;
|
|
27
26
|
// Redirect protected routes to login if no session
|
|
28
27
|
if (!token && protectedPrefixes.some((p) => pathname.startsWith(p))) {
|
|
29
|
-
return
|
|
28
|
+
return NextResponse.redirect(new URL(loginPath, req.url));
|
|
30
29
|
}
|
|
31
|
-
return
|
|
30
|
+
return NextResponse.next();
|
|
32
31
|
};
|
|
33
32
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseRepository } from '@mostajs/orm';
|
|
2
2
|
import type { IDialect } from '@mostajs/orm';
|
|
3
|
-
import type { PermissionCategoryDTO } from '../types';
|
|
3
|
+
import type { PermissionCategoryDTO } from '../types/index';
|
|
4
4
|
export declare class PermissionCategoryRepository extends BaseRepository<PermissionCategoryDTO> {
|
|
5
5
|
constructor(dialect: IDialect);
|
|
6
6
|
/** Find all sorted by order then name */
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PermissionCategoryRepository = void 0;
|
|
4
1
|
// @mosta/auth — PermissionCategoryRepository
|
|
5
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class PermissionCategoryRepository extends
|
|
3
|
+
import { BaseRepository } from '@mostajs/orm';
|
|
4
|
+
import { PermissionCategorySchema } from '../schemas/permission-category.schema';
|
|
5
|
+
export class PermissionCategoryRepository extends BaseRepository {
|
|
9
6
|
constructor(dialect) {
|
|
10
|
-
super(
|
|
7
|
+
super(PermissionCategorySchema, dialect);
|
|
11
8
|
}
|
|
12
9
|
/** Find all sorted by order then name */
|
|
13
10
|
async findAllOrdered() {
|
|
@@ -18,4 +15,3 @@ class PermissionCategoryRepository extends orm_1.BaseRepository {
|
|
|
18
15
|
return this.findOne({ name });
|
|
19
16
|
}
|
|
20
17
|
}
|
|
21
|
-
exports.PermissionCategoryRepository = PermissionCategoryRepository;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseRepository } from '@mostajs/orm';
|
|
2
2
|
import type { IDialect } from '@mostajs/orm';
|
|
3
|
-
import type { PermissionDTO } from '../types';
|
|
3
|
+
import type { PermissionDTO } from '../types/index';
|
|
4
4
|
export declare class PermissionRepository extends BaseRepository<PermissionDTO> {
|
|
5
5
|
constructor(dialect: IDialect);
|
|
6
6
|
/** Find all sorted by category then name */
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PermissionRepository = void 0;
|
|
4
1
|
// @mosta/auth — PermissionRepository
|
|
5
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class PermissionRepository extends
|
|
3
|
+
import { BaseRepository } from '@mostajs/orm';
|
|
4
|
+
import { PermissionSchema } from '../schemas/permission.schema';
|
|
5
|
+
export class PermissionRepository extends BaseRepository {
|
|
9
6
|
constructor(dialect) {
|
|
10
|
-
super(
|
|
7
|
+
super(PermissionSchema, dialect);
|
|
11
8
|
}
|
|
12
9
|
/** Find all sorted by category then name */
|
|
13
10
|
async findAllSorted() {
|
|
@@ -22,4 +19,3 @@ class PermissionRepository extends orm_1.BaseRepository {
|
|
|
22
19
|
return this.count({ category });
|
|
23
20
|
}
|
|
24
21
|
}
|
|
25
|
-
exports.PermissionRepository = PermissionRepository;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseRepository } from '@mostajs/orm';
|
|
2
2
|
import type { IDialect } from '@mostajs/orm';
|
|
3
|
-
import type { RoleDTO } from '../types';
|
|
3
|
+
import type { RoleDTO } from '../types/index';
|
|
4
4
|
export declare class RoleRepository extends BaseRepository<RoleDTO> {
|
|
5
5
|
constructor(dialect: IDialect);
|
|
6
6
|
/** Find all roles with permissions populated */
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RoleRepository = void 0;
|
|
4
1
|
// @mosta/auth — RoleRepository
|
|
5
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class RoleRepository extends
|
|
3
|
+
import { BaseRepository } from '@mostajs/orm';
|
|
4
|
+
import { RoleSchema } from '../schemas/role.schema';
|
|
5
|
+
export class RoleRepository extends BaseRepository {
|
|
9
6
|
constructor(dialect) {
|
|
10
|
-
super(
|
|
7
|
+
super(RoleSchema, dialect);
|
|
11
8
|
}
|
|
12
9
|
/** Find all roles with permissions populated */
|
|
13
10
|
async findAllWithPermissions() {
|
|
@@ -34,4 +31,3 @@ class RoleRepository extends orm_1.BaseRepository {
|
|
|
34
31
|
return this.updateMany({ permissions: permissionId }, { $pull: { permissions: permissionId } });
|
|
35
32
|
}
|
|
36
33
|
}
|
|
37
|
-
exports.RoleRepository = RoleRepository;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseRepository } from '@mostajs/orm';
|
|
2
2
|
import type { IDialect, FilterQuery, QueryOptions } from '@mostajs/orm';
|
|
3
|
-
import type { UserDTO } from '../types';
|
|
3
|
+
import type { UserDTO } from '../types/index';
|
|
4
4
|
export declare class UserRepository extends BaseRepository<UserDTO> {
|
|
5
5
|
constructor(dialect: IDialect);
|
|
6
6
|
/** List users without password field */
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.UserRepository = void 0;
|
|
4
1
|
// @mosta/auth — UserRepository
|
|
5
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class UserRepository extends
|
|
3
|
+
import { BaseRepository } from '@mostajs/orm';
|
|
4
|
+
import { UserSchema } from '../schemas/user.schema';
|
|
5
|
+
export class UserRepository extends BaseRepository {
|
|
9
6
|
constructor(dialect) {
|
|
10
|
-
super(
|
|
7
|
+
super(UserSchema, dialect);
|
|
11
8
|
}
|
|
12
9
|
/** List users without password field */
|
|
13
10
|
async findAllSafe(filter = {}, options) {
|
|
@@ -46,4 +43,3 @@ class UserRepository extends orm_1.BaseRepository {
|
|
|
46
43
|
return this.pull(userId, 'roles', roleId);
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
|
-
exports.UserRepository = UserRepository;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PermissionCategorySchema = void 0;
|
|
4
|
-
exports.PermissionCategorySchema = {
|
|
1
|
+
export const PermissionCategorySchema = {
|
|
5
2
|
name: 'PermissionCategory',
|
|
6
3
|
collection: 'permission_categories',
|
|
7
4
|
timestamps: true,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,8 +7,6 @@ export interface MostaAuthConfig {
|
|
|
7
7
|
defaultPermissions?: PermissionDefinition[];
|
|
8
8
|
/** Default permission categories to seed */
|
|
9
9
|
defaultCategories?: CategoryDefinition[];
|
|
10
|
-
/** bcrypt salt rounds (default: 12) */
|
|
11
|
-
bcryptRounds?: number;
|
|
12
10
|
/** NextAuth pages */
|
|
13
11
|
pages?: {
|
|
14
12
|
signIn?: string;
|
package/dist/types/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,35 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostajs/auth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Reusable authentication and RBAC module — NextAuth + Users + Roles + Permissions",
|
|
5
5
|
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
7
8
|
"main": "dist/index.js",
|
|
8
9
|
"types": "dist/index.d.ts",
|
|
9
10
|
"exports": {
|
|
10
11
|
".": {
|
|
11
12
|
"types": "./dist/index.d.ts",
|
|
12
13
|
"import": "./dist/index.js",
|
|
13
|
-
"require": "./dist/index.js",
|
|
14
14
|
"default": "./dist/index.js"
|
|
15
15
|
},
|
|
16
16
|
"./components/SessionProvider": {
|
|
17
17
|
"types": "./dist/components/SessionProvider.d.ts",
|
|
18
18
|
"import": "./dist/components/SessionProvider.js",
|
|
19
|
-
"require": "./dist/components/SessionProvider.js",
|
|
20
19
|
"default": "./dist/components/SessionProvider.js"
|
|
21
20
|
},
|
|
22
21
|
"./components/PermissionGuard": {
|
|
23
22
|
"types": "./dist/components/PermissionGuard.d.ts",
|
|
24
23
|
"import": "./dist/components/PermissionGuard.js",
|
|
25
|
-
"require": "./dist/components/PermissionGuard.js",
|
|
26
24
|
"default": "./dist/components/PermissionGuard.js"
|
|
27
25
|
},
|
|
28
26
|
"./hooks/usePermissions": {
|
|
29
27
|
"types": "./dist/hooks/usePermissions.d.ts",
|
|
30
28
|
"import": "./dist/hooks/usePermissions.js",
|
|
31
|
-
"require": "./dist/hooks/usePermissions.js",
|
|
32
29
|
"default": "./dist/hooks/usePermissions.js"
|
|
30
|
+
},
|
|
31
|
+
"./lib/auth": {
|
|
32
|
+
"types": "./dist/lib/auth.d.ts",
|
|
33
|
+
"import": "./dist/lib/auth.js",
|
|
34
|
+
"default": "./dist/lib/auth.js"
|
|
35
|
+
},
|
|
36
|
+
"./lib/auth-check": {
|
|
37
|
+
"types": "./dist/lib/auth-check.d.ts",
|
|
38
|
+
"import": "./dist/lib/auth-check.js",
|
|
39
|
+
"default": "./dist/lib/auth-check.js"
|
|
40
|
+
},
|
|
41
|
+
"./lib/permissions": {
|
|
42
|
+
"types": "./dist/lib/permissions.d.ts",
|
|
43
|
+
"import": "./dist/lib/permissions.js",
|
|
44
|
+
"default": "./dist/lib/permissions.js"
|
|
45
|
+
},
|
|
46
|
+
"./lib/permissions-server": {
|
|
47
|
+
"types": "./dist/lib/permissions-server.d.ts",
|
|
48
|
+
"import": "./dist/lib/permissions-server.js",
|
|
49
|
+
"default": "./dist/lib/permissions-server.js"
|
|
50
|
+
},
|
|
51
|
+
"./lib/password": {
|
|
52
|
+
"types": "./dist/lib/password.d.ts",
|
|
53
|
+
"import": "./dist/lib/password.js",
|
|
54
|
+
"default": "./dist/lib/password.js"
|
|
55
|
+
},
|
|
56
|
+
"./lib/rbac-seed": {
|
|
57
|
+
"types": "./dist/lib/rbac-seed.d.ts",
|
|
58
|
+
"import": "./dist/lib/rbac-seed.js",
|
|
59
|
+
"default": "./dist/lib/rbac-seed.js"
|
|
60
|
+
},
|
|
61
|
+
"./middleware/auth-middleware": {
|
|
62
|
+
"types": "./dist/middleware/auth-middleware.d.ts",
|
|
63
|
+
"import": "./dist/middleware/auth-middleware.js",
|
|
64
|
+
"default": "./dist/middleware/auth-middleware.js"
|
|
33
65
|
}
|
|
34
66
|
},
|
|
35
67
|
"files": [
|
|
@@ -61,11 +93,11 @@
|
|
|
61
93
|
},
|
|
62
94
|
"dependencies": {
|
|
63
95
|
"@mostajs/orm": "^1.0.0",
|
|
64
|
-
"bcryptjs": "^2.4.3"
|
|
65
|
-
"next-auth": "5.0.0-beta.25"
|
|
96
|
+
"bcryptjs": "^2.4.3"
|
|
66
97
|
},
|
|
67
98
|
"peerDependencies": {
|
|
68
99
|
"next": ">=14",
|
|
100
|
+
"next-auth": ">=5.0.0-beta.25",
|
|
69
101
|
"react": ">=18"
|
|
70
102
|
},
|
|
71
103
|
"devDependencies": {
|