@mostajs/auth 2.3.3 → 2.4.0

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.
@@ -0,0 +1,57 @@
1
+ export interface CredentialsProviderConfig {
2
+ /** Lookup the user row by email (case-insensitive). Return null if not found.
3
+ * Conseillé : passer une fonction qui utilise UserRepository de @mostajs/rbac
4
+ * pour bénéficier de findByEmail (qui lowercase l'email côté repo). */
5
+ findUserByEmail: (email: string) => Promise<any | null>;
6
+ /** Default role assigned when the user has no role relations (default: 'user'). */
7
+ defaultRole?: string;
8
+ /** Custom check before allowing login (e.g. email verified, 2FA).
9
+ * Return false to block. Default: status !== 'disabled' && status !== 'locked'. */
10
+ checkUserAllowed?: (user: any) => boolean | Promise<boolean>;
11
+ /** Custom role resolution. Default: first user.roles[].name or fallback. */
12
+ resolveRole?: (user: any) => string | Promise<string>;
13
+ /** Custom display name. Default: "<firstName> <lastName>" trimmed → email. */
14
+ resolveName?: (user: any) => string;
15
+ /** Provider id (default: 'credentials'). Useful if multiple providers coexist. */
16
+ id?: string;
17
+ /** Provider display name (default: 'Email + Password'). */
18
+ name?: string;
19
+ }
20
+ /**
21
+ * Build a NextAuth Credentials provider config (returned as plain object,
22
+ * directly passable to `NextAuth({ providers: [provider], ... })` without
23
+ * any next-auth imports côté consumer).
24
+ *
25
+ * Usage :
26
+ * import { createCredentialsProvider } from '@mostajs/auth/server'
27
+ *
28
+ * NextAuth({
29
+ * providers: [
30
+ * createCredentialsProvider({
31
+ * findUserByEmail: async (email) => userRepo.findByEmail(email),
32
+ * defaultRole: 'user',
33
+ * }),
34
+ * ],
35
+ * })
36
+ */
37
+ export declare function createCredentialsProvider(config: CredentialsProviderConfig): {
38
+ id: string;
39
+ name: string;
40
+ type: "credentials";
41
+ credentials: {
42
+ email: {
43
+ label: string;
44
+ type: string;
45
+ };
46
+ password: {
47
+ label: string;
48
+ type: string;
49
+ };
50
+ };
51
+ authorize(credentials: any): Promise<{
52
+ id: any;
53
+ email: any;
54
+ name: string;
55
+ role: string;
56
+ } | null>;
57
+ };
@@ -0,0 +1,84 @@
1
+ // @mostajs/auth — Email + password credentials provider for NextAuth
2
+ //
3
+ // Factorise le boilerplate NextAuth Credentials que chaque app refait. Le
4
+ // consumer obtient un provider PRÊT À L'EMPLOI à passer à NextAuth({providers}) :
5
+ // il n'importe ni `next-auth/providers/credentials`, ni bcrypt, ni le
6
+ // UserRepository — tout est encapsulé.
7
+ //
8
+ // Pendant naturel de createApiKeyProvider (même style de retour : objet
9
+ // provider config plat). Différence : authentication via email/password
10
+ // (UI register/signin) vs API key (programmatic).
11
+ //
12
+ // Le caller fournit `findUserByEmail` (ou laisse passer pour un default
13
+ // fail-closed). Pour leverager directement @mostajs/rbac, il suffit de
14
+ // passer `(email) => new UserRepository(dialect).findByEmail(email)`.
15
+ //
16
+ // Author: Dr Hamid MADANI <drmdh@msn.com>
17
+ import { comparePassword } from './password.js';
18
+ function defaultStatusCheck(user) {
19
+ return user.status !== 'disabled' && user.status !== 'locked';
20
+ }
21
+ function defaultRoleResolver(user, fallback) {
22
+ if (user.roles?.length > 0) {
23
+ const r = user.roles[0];
24
+ return typeof r === 'string' ? r : (r?.name ?? fallback);
25
+ }
26
+ return user.role ?? fallback;
27
+ }
28
+ function defaultNameResolver(user) {
29
+ return `${user.firstName ?? ''} ${user.lastName ?? ''}`.trim() || (user.email ?? '');
30
+ }
31
+ /**
32
+ * Build a NextAuth Credentials provider config (returned as plain object,
33
+ * directly passable to `NextAuth({ providers: [provider], ... })` without
34
+ * any next-auth imports côté consumer).
35
+ *
36
+ * Usage :
37
+ * import { createCredentialsProvider } from '@mostajs/auth/server'
38
+ *
39
+ * NextAuth({
40
+ * providers: [
41
+ * createCredentialsProvider({
42
+ * findUserByEmail: async (email) => userRepo.findByEmail(email),
43
+ * defaultRole: 'user',
44
+ * }),
45
+ * ],
46
+ * })
47
+ */
48
+ export function createCredentialsProvider(config) {
49
+ const defaultRole = config.defaultRole ?? 'user';
50
+ const checkUserAllowed = config.checkUserAllowed ?? defaultStatusCheck;
51
+ const resolveRole = config.resolveRole ?? ((u) => defaultRoleResolver(u, defaultRole));
52
+ const resolveName = config.resolveName ?? defaultNameResolver;
53
+ return {
54
+ id: config.id ?? 'credentials',
55
+ name: config.name ?? 'Email + Password',
56
+ type: 'credentials',
57
+ credentials: {
58
+ email: { label: 'Email', type: 'email' },
59
+ password: { label: 'Password', type: 'password' },
60
+ },
61
+ async authorize(credentials) {
62
+ if (!credentials?.email || !credentials?.password)
63
+ return null;
64
+ const email = String(credentials.email).toLowerCase().trim();
65
+ const user = await config.findUserByEmail(email);
66
+ if (!user)
67
+ return null;
68
+ const allowed = await checkUserAllowed(user);
69
+ if (!allowed)
70
+ return null;
71
+ const valid = await comparePassword(String(credentials.password), user.password);
72
+ if (!valid)
73
+ return null;
74
+ const role = await resolveRole(user);
75
+ const name = resolveName(user);
76
+ return {
77
+ id: user.id,
78
+ email: user.email,
79
+ name,
80
+ role,
81
+ };
82
+ },
83
+ };
84
+ }
package/dist/server.d.ts CHANGED
@@ -20,5 +20,7 @@ export { createPasswordResetHandlers, generateResetToken } from './lib/password-
20
20
  export type { PasswordResetConfig } from './lib/password-reset';
21
21
  export { createApiKeyProvider } from './lib/apikey-provider';
22
22
  export type { ApiKeyProviderConfig } from './lib/apikey-provider';
23
+ export { createCredentialsProvider } from './lib/credentials-provider';
24
+ export type { CredentialsProviderConfig } from './lib/credentials-provider';
23
25
  export { enrichTokenWithPlan, enrichSessionWithPlan } from './lib/session-enrichment';
24
26
  export type { SessionEnrichmentConfig } from './lib/session-enrichment';
package/dist/server.js CHANGED
@@ -27,5 +27,6 @@ export { createVerificationHandlers, generateVerifyToken } from './lib/email-ver
27
27
  export { createPasswordResetHandlers, generateResetToken } from './lib/password-reset.js';
28
28
  // API key provider
29
29
  export { createApiKeyProvider } from './lib/apikey-provider.js';
30
+ export { createCredentialsProvider } from './lib/credentials-provider.js';
30
31
  // Session enrichment
31
32
  export { enrichTokenWithPlan, enrichSessionWithPlan } from './lib/session-enrichment.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/auth",
3
- "version": "2.3.3",
3
+ "version": "2.4.0",
4
4
  "description": "Authentication — NextAuth, password hashing, session management",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "AGPL-3.0-or-later",
@@ -83,6 +83,11 @@
83
83
  "import": "./dist/lib/apikey-provider.js",
84
84
  "default": "./dist/lib/apikey-provider.js"
85
85
  },
86
+ "./lib/credentials-provider": {
87
+ "types": "./dist/lib/credentials-provider.d.ts",
88
+ "import": "./dist/lib/credentials-provider.js",
89
+ "default": "./dist/lib/credentials-provider.js"
90
+ },
86
91
  "./lib/check-request": {
87
92
  "types": "./dist/lib/check-request.d.ts",
88
93
  "import": "./dist/lib/check-request.js",
@@ -120,7 +125,7 @@
120
125
  },
121
126
  "scripts": {
122
127
  "build": "tsc && npm run fix-esm",
123
- "fix-esm": "find dist -name '*.js' -exec sed -i -E \"s|from '(\\\\.{1,2}/[^']+)'(;?)|from '\\\\1.js'\\\\2|g; s|from \\\"(\\\\.{1,2}/[^\\\"]+)\\\"(;?)|from \\\"\\\\1.js\\\"\\\\2|g\" {} \\; && find dist -name '*.js' -exec sed -i -E \"s|\\\\.js\\\\.js|.js|g\" {} \\;",
128
+ "fix-esm": "find dist -name '*.js' -exec sed -i -E \"s|from '(\\\\.{1,2}/[^']+)'(;?)|from '\\\\1.js'\\\\2|g; s|from \\\"(\\\\.{1,2}/[^\\\"]+)\\\"(;?)|from \\\"\\\\1.js\\\"\\\\2|g\" {} \\; && find dist -name '*.js' -exec sed -i -E \"s|\\\\.js\\\\.js|.js|g; s|\\\\.json\\\\.js|.json|g; s|\\\\.css\\\\.js|.css|g\" {} \\;",
124
129
  "prepublishOnly": "npm run build"
125
130
  },
126
131
  "dependencies": {