node-auth-kit 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.
Files changed (47) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +335 -0
  3. package/dist/index.d.ts +13 -0
  4. package/dist/index.js +27 -0
  5. package/dist/src/adapters/base.adapter.d.ts +21 -0
  6. package/dist/src/adapters/base.adapter.js +2 -0
  7. package/dist/src/adapters/mongoose.adapter.d.ts +53 -0
  8. package/dist/src/adapters/mongoose.adapter.js +78 -0
  9. package/dist/src/adapters/prisma.adapter.d.ts +29 -0
  10. package/dist/src/adapters/prisma.adapter.js +40 -0
  11. package/dist/src/config/default.config.d.ts +19 -0
  12. package/dist/src/config/default.config.js +17 -0
  13. package/dist/src/config/index.d.ts +4 -0
  14. package/dist/src/config/index.js +23 -0
  15. package/dist/src/controllers/registrations.controller.d.ts +3 -0
  16. package/dist/src/controllers/registrations.controller.js +25 -0
  17. package/dist/src/controllers/sessions.controller.d.ts +3 -0
  18. package/dist/src/controllers/sessions.controller.js +25 -0
  19. package/dist/src/core/auth.service.d.ts +35 -0
  20. package/dist/src/core/auth.service.js +113 -0
  21. package/dist/src/core/password.service.d.ts +9 -0
  22. package/dist/src/core/password.service.js +19 -0
  23. package/dist/src/core/token.service.d.ts +15 -0
  24. package/dist/src/core/token.service.js +26 -0
  25. package/dist/src/deviceAuth.d.ts +32 -0
  26. package/dist/src/deviceAuth.js +67 -0
  27. package/dist/src/factories/auth-service.factory.d.ts +2 -0
  28. package/dist/src/factories/auth-service.factory.js +30 -0
  29. package/dist/src/hooks/index.d.ts +5 -0
  30. package/dist/src/hooks/index.js +20 -0
  31. package/dist/src/middlewares/authenticate.middleware.d.ts +15 -0
  32. package/dist/src/middlewares/authenticate.middleware.js +46 -0
  33. package/dist/src/middlewares/authorize.middleware.d.ts +8 -0
  34. package/dist/src/middlewares/authorize.middleware.js +36 -0
  35. package/dist/src/overrides/index.d.ts +7 -0
  36. package/dist/src/overrides/index.js +13 -0
  37. package/dist/src/routes/auth.routes.d.ts +2 -0
  38. package/dist/src/routes/auth.routes.js +41 -0
  39. package/dist/src/types/index.d.ts +11 -0
  40. package/dist/src/types/index.js +2 -0
  41. package/dist/src/utils/index.d.ts +3 -0
  42. package/dist/src/utils/index.js +12 -0
  43. package/dist/src/utils/pick.d.ts +1 -0
  44. package/dist/src/utils/pick.js +13 -0
  45. package/dist/src/utils/response.d.ts +10 -0
  46. package/dist/src/utils/response.js +32 -0
  47. package/package.json +51 -0
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRegistrationsController = void 0;
4
+ const deviceAuth_1 = require("../deviceAuth");
5
+ const auth_service_factory_1 = require("../factories/auth-service.factory");
6
+ const sanitizeUser = (user) => {
7
+ const { password: _password, ...safeUser } = user;
8
+ return safeUser;
9
+ };
10
+ const sanitizeSignupResult = (result) => {
11
+ return {
12
+ ...result,
13
+ user: sanitizeUser(result.user),
14
+ };
15
+ };
16
+ const defaultRegistrationsController = async (data) => {
17
+ const authService = (0, auth_service_factory_1.createAuthService)();
18
+ const result = await authService.signup(data);
19
+ return sanitizeSignupResult(result);
20
+ };
21
+ const getRegistrationsController = () => {
22
+ const override = deviceAuth_1.deviceAuth.getControllerOverride('registrations');
23
+ return override ?? defaultRegistrationsController;
24
+ };
25
+ exports.getRegistrationsController = getRegistrationsController;
@@ -0,0 +1,3 @@
1
+ import type { LoginResult } from '../core/auth.service';
2
+ export type SessionsController = (email: string, password: string) => Promise<LoginResult>;
3
+ export declare const getSessionsController: () => SessionsController;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSessionsController = void 0;
4
+ const deviceAuth_1 = require("../deviceAuth");
5
+ const auth_service_factory_1 = require("../factories/auth-service.factory");
6
+ const sanitizeUser = (user) => {
7
+ const { password: _password, ...safeUser } = user;
8
+ return safeUser;
9
+ };
10
+ const sanitizeLoginResult = (result) => {
11
+ return {
12
+ ...result,
13
+ user: sanitizeUser(result.user),
14
+ };
15
+ };
16
+ const defaultSessionsController = async (email, password) => {
17
+ const authService = (0, auth_service_factory_1.createAuthService)();
18
+ const result = await authService.login(email, password);
19
+ return sanitizeLoginResult(result);
20
+ };
21
+ const getSessionsController = () => {
22
+ const override = deviceAuth_1.deviceAuth.getControllerOverride('sessions');
23
+ return override ?? defaultSessionsController;
24
+ };
25
+ exports.getSessionsController = getSessionsController;
@@ -0,0 +1,35 @@
1
+ import type { BaseAdapter, AdapterUser } from '../adapters/base.adapter';
2
+ import type { DeviceAuthConfig } from '../config';
3
+ import type { HookName } from '../deviceAuth';
4
+ import type { PasswordService } from './password.service';
5
+ import type { TokenService } from './token.service';
6
+ export interface AuthServiceDeps {
7
+ adapter: BaseAdapter;
8
+ config: DeviceAuthConfig;
9
+ passwordService: PasswordService;
10
+ tokenService: TokenService;
11
+ runHooks: (name: HookName, ...args: unknown[]) => Promise<void>;
12
+ }
13
+ export interface SignupResult {
14
+ user: AdapterUser;
15
+ accessToken?: string;
16
+ }
17
+ export interface LoginResult {
18
+ user: AdapterUser;
19
+ accessToken?: string;
20
+ }
21
+ export declare class AuthService {
22
+ private readonly adapter;
23
+ private readonly config;
24
+ private readonly passwordService;
25
+ private readonly tokenService;
26
+ private readonly runHooks;
27
+ constructor(deps: AuthServiceDeps);
28
+ signup(data: Record<string, unknown>): Promise<SignupResult>;
29
+ login(email: string, password: string): Promise<LoginResult>;
30
+ logout(): Promise<void>;
31
+ validateUserFromToken(token: string): Promise<AdapterUser | null>;
32
+ private ensureSignupFields;
33
+ private ensurePasswordRules;
34
+ private issueAccessTokenFromUser;
35
+ }
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthService = void 0;
4
+ class AuthService {
5
+ constructor(deps) {
6
+ this.adapter = deps.adapter;
7
+ this.config = deps.config;
8
+ this.passwordService = deps.passwordService;
9
+ this.tokenService = deps.tokenService;
10
+ this.runHooks = deps.runHooks;
11
+ }
12
+ async signup(data) {
13
+ this.ensureSignupFields(data);
14
+ const password = data['password'];
15
+ if (typeof password !== 'string') {
16
+ throw new Error('Password is required and must be a string');
17
+ }
18
+ this.ensurePasswordRules(password);
19
+ const hashedPassword = await this.passwordService.hashPassword(password);
20
+ const createData = {
21
+ ...data,
22
+ password: hashedPassword,
23
+ };
24
+ if (createData['role'] == null) {
25
+ createData['role'] = this.config.defaultRole;
26
+ }
27
+ await this.runHooks('beforeRegister', createData);
28
+ const user = await this.adapter.createUser(createData);
29
+ await this.runHooks('afterRegister', user);
30
+ if (this.config.authType === 'jwt') {
31
+ const accessToken = this.issueAccessTokenFromUser(user);
32
+ return { user, accessToken };
33
+ }
34
+ return { user };
35
+ }
36
+ async login(email, password) {
37
+ const user = await this.adapter.findUserByEmail(email);
38
+ if (!user) {
39
+ throw new Error('Invalid credentials');
40
+ }
41
+ const storedPassword = user['password'];
42
+ if (typeof storedPassword !== 'string') {
43
+ throw new Error('Invalid user password configuration');
44
+ }
45
+ await this.runHooks('beforeLogin', user);
46
+ const isValid = await this.passwordService.comparePassword(password, storedPassword);
47
+ if (!isValid) {
48
+ throw new Error('Invalid credentials');
49
+ }
50
+ await this.runHooks('afterLogin', user);
51
+ if (this.config.authType === 'jwt') {
52
+ const accessToken = this.issueAccessTokenFromUser(user);
53
+ return { user, accessToken };
54
+ }
55
+ return { user };
56
+ }
57
+ async logout() {
58
+ return;
59
+ }
60
+ async validateUserFromToken(token) {
61
+ if (this.config.authType !== 'jwt') {
62
+ throw new Error('Token-based validation is only available for jwt authType');
63
+ }
64
+ const payload = this.tokenService.verifyToken(token);
65
+ const userId = payload.sub;
66
+ if (userId == null) {
67
+ throw new Error('Token payload missing subject');
68
+ }
69
+ const user = await this.adapter.findUserById(userId);
70
+ if (user) {
71
+ const mutableUser = user;
72
+ if (mutableUser['id'] == null) {
73
+ mutableUser['id'] = userId;
74
+ }
75
+ if (Object.prototype.hasOwnProperty.call(payload, 'role')) {
76
+ mutableUser['role'] = payload['role'];
77
+ }
78
+ }
79
+ return user;
80
+ }
81
+ ensureSignupFields(data) {
82
+ for (const field of this.config.signupFields) {
83
+ if (data[field] == null) {
84
+ throw new Error(`Missing required signup field: ${field}`);
85
+ }
86
+ }
87
+ }
88
+ ensurePasswordRules(password) {
89
+ const rules = this.config.password;
90
+ if (password.length < rules.minLength) {
91
+ throw new Error('Password does not meet minimum length requirement');
92
+ }
93
+ if (rules.requireNumbers && !/[0-9]/.test(password)) {
94
+ throw new Error('Password must contain at least one number');
95
+ }
96
+ if (rules.requireSpecialChars && !/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
97
+ throw new Error('Password must contain at least one special character');
98
+ }
99
+ }
100
+ issueAccessTokenFromUser(user) {
101
+ const userId = user['id'];
102
+ if (userId == null) {
103
+ throw new Error('User record does not contain an id field');
104
+ }
105
+ const role = user['role'] ?? null;
106
+ const payload = {
107
+ sub: userId,
108
+ role,
109
+ };
110
+ return this.tokenService.generateAccessToken(payload);
111
+ }
112
+ }
113
+ exports.AuthService = AuthService;
@@ -0,0 +1,9 @@
1
+ export interface PasswordServiceOptions {
2
+ saltRounds: number;
3
+ }
4
+ export declare class PasswordService {
5
+ private readonly saltRounds;
6
+ constructor(options: PasswordServiceOptions);
7
+ hashPassword(plainPassword: string): Promise<string>;
8
+ comparePassword(plainPassword: string, hashedPassword: string): Promise<boolean>;
9
+ }
@@ -0,0 +1,19 @@
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.PasswordService = void 0;
7
+ const bcrypt_1 = __importDefault(require("bcrypt"));
8
+ class PasswordService {
9
+ constructor(options) {
10
+ this.saltRounds = options.saltRounds;
11
+ }
12
+ async hashPassword(plainPassword) {
13
+ return bcrypt_1.default.hash(plainPassword, this.saltRounds);
14
+ }
15
+ async comparePassword(plainPassword, hashedPassword) {
16
+ return bcrypt_1.default.compare(plainPassword, hashedPassword);
17
+ }
18
+ }
19
+ exports.PasswordService = PasswordService;
@@ -0,0 +1,15 @@
1
+ export interface TokenServiceOptions {
2
+ secret: string;
3
+ expiresIn: string;
4
+ }
5
+ export interface AccessTokenPayload {
6
+ sub: string | number;
7
+ [key: string]: unknown;
8
+ }
9
+ export declare class TokenService {
10
+ private readonly secret;
11
+ private readonly expiresIn;
12
+ constructor(options: TokenServiceOptions);
13
+ generateAccessToken(payload: AccessTokenPayload): string;
14
+ verifyToken(token: string): AccessTokenPayload;
15
+ }
@@ -0,0 +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.TokenService = void 0;
7
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
8
+ class TokenService {
9
+ constructor(options) {
10
+ this.secret = options.secret;
11
+ this.expiresIn = options.expiresIn;
12
+ }
13
+ generateAccessToken(payload) {
14
+ // Casts are used here to avoid tight coupling to jsonwebtoken's type overloads,
15
+ // while preserving correct runtime behavior.
16
+ return jsonwebtoken_1.default.sign(payload, this.secret, { expiresIn: this.expiresIn });
17
+ }
18
+ verifyToken(token) {
19
+ const decoded = jsonwebtoken_1.default.verify(token, this.secret);
20
+ if (typeof decoded === 'string') {
21
+ throw new Error('Invalid token payload type');
22
+ }
23
+ return decoded;
24
+ }
25
+ }
26
+ exports.TokenService = TokenService;
@@ -0,0 +1,32 @@
1
+ import type { DeviceAuthConfig } from './config';
2
+ import type { BaseAdapter } from './adapters/base.adapter';
3
+ export type HookName = 'beforeRegister' | 'afterRegister' | 'beforeLogin' | 'afterLogin';
4
+ export type HookFn = (...args: unknown[]) => unknown | Promise<unknown>;
5
+ export interface ControllerOverrides {
6
+ [controllerName: string]: any;
7
+ }
8
+ declare class DeviceAuthCore {
9
+ private _config;
10
+ private _adapter;
11
+ private _hooks;
12
+ private _controllerOverrides;
13
+ constructor();
14
+ /** Initialize global configuration (Devise-style initializer). */
15
+ init(config?: Partial<DeviceAuthConfig>): this;
16
+ /** Register the database adapter (Prisma, Mongoose, etc.). */
17
+ useAdapter(adapter: BaseAdapter): this;
18
+ /** Register a hook callback for a given lifecycle event. */
19
+ registerHook(name: HookName, fn: HookFn): this;
20
+ /** Internal: execute all hooks for an event, without breaking core flow. */
21
+ runHooks(name: HookName, ...args: unknown[]): Promise<void>;
22
+ /** Register or override controllers (full or partial). */
23
+ override(controllerName: string, implementation: unknown): this;
24
+ /** Get an overridden controller implementation, if any. */
25
+ getControllerOverride<T = unknown>(controllerName: string): T | undefined;
26
+ /** Expose current config (read-only by convention). */
27
+ get config(): DeviceAuthConfig;
28
+ /** Expose current adapter. Will throw if not configured. */
29
+ get adapter(): BaseAdapter;
30
+ }
31
+ export declare const deviceAuth: DeviceAuthCore;
32
+ export type { DeviceAuthConfig };
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deviceAuth = void 0;
4
+ const config_1 = require("./config");
5
+ class DeviceAuthCore {
6
+ constructor() {
7
+ this._adapter = null;
8
+ this._hooks = {};
9
+ this._controllerOverrides = {};
10
+ this._config = (0, config_1.mergeConfig)();
11
+ }
12
+ /** Initialize global configuration (Devise-style initializer). */
13
+ init(config) {
14
+ this._config = (0, config_1.mergeConfig)(config);
15
+ return this;
16
+ }
17
+ /** Register the database adapter (Prisma, Mongoose, etc.). */
18
+ useAdapter(adapter) {
19
+ this._adapter = adapter;
20
+ return this;
21
+ }
22
+ /** Register a hook callback for a given lifecycle event. */
23
+ registerHook(name, fn) {
24
+ if (!this._hooks[name]) {
25
+ this._hooks[name] = [];
26
+ }
27
+ this._hooks[name].push(fn);
28
+ return this;
29
+ }
30
+ /** Internal: execute all hooks for an event, without breaking core flow. */
31
+ async runHooks(name, ...args) {
32
+ const fns = this._hooks[name];
33
+ if (!fns || fns.length === 0)
34
+ return;
35
+ for (const fn of fns) {
36
+ try {
37
+ // eslint-disable-next-line @typescript-eslint/await-thenable
38
+ await fn(...args);
39
+ }
40
+ catch (err) {
41
+ // Intentionally swallow errors to avoid breaking core flow
42
+ // In future we can add optional logging hooks
43
+ }
44
+ }
45
+ }
46
+ /** Register or override controllers (full or partial). */
47
+ override(controllerName, implementation) {
48
+ this._controllerOverrides[controllerName] = implementation;
49
+ return this;
50
+ }
51
+ /** Get an overridden controller implementation, if any. */
52
+ getControllerOverride(controllerName) {
53
+ return this._controllerOverrides[controllerName];
54
+ }
55
+ /** Expose current config (read-only by convention). */
56
+ get config() {
57
+ return this._config;
58
+ }
59
+ /** Expose current adapter. Will throw if not configured. */
60
+ get adapter() {
61
+ if (!this._adapter) {
62
+ throw new Error('device_auth adapter is not configured. Call deviceAuth.useAdapter().');
63
+ }
64
+ return this._adapter;
65
+ }
66
+ }
67
+ exports.deviceAuth = new DeviceAuthCore();
@@ -0,0 +1,2 @@
1
+ import { AuthService } from '../core/auth.service';
2
+ export declare const createAuthService: () => AuthService;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAuthService = void 0;
4
+ const deviceAuth_1 = require("../deviceAuth");
5
+ const auth_service_1 = require("../core/auth.service");
6
+ const password_service_1 = require("../core/password.service");
7
+ const token_service_1 = require("../core/token.service");
8
+ const createAuthService = () => {
9
+ const config = deviceAuth_1.deviceAuth.config;
10
+ const adapter = deviceAuth_1.deviceAuth.adapter;
11
+ const passwordService = new password_service_1.PasswordService({
12
+ saltRounds: config.password.saltRounds,
13
+ });
14
+ const secret = process.env.DEVICE_AUTH_JWT_SECRET;
15
+ if (!secret) {
16
+ throw new Error('DEVICE_AUTH_JWT_SECRET environment variable is required for JWT authentication. Set DEVICE_AUTH_JWT_SECRET to a strong, random secret in your environment configuration.');
17
+ }
18
+ const tokenService = new token_service_1.TokenService({
19
+ secret,
20
+ expiresIn: config.token.accessTokenTtl,
21
+ });
22
+ return new auth_service_1.AuthService({
23
+ adapter,
24
+ config,
25
+ passwordService,
26
+ tokenService,
27
+ runHooks: deviceAuth_1.deviceAuth.runHooks.bind(deviceAuth_1.deviceAuth),
28
+ });
29
+ };
30
+ exports.createAuthService = createAuthService;
@@ -0,0 +1,5 @@
1
+ import { type HookFn } from '../deviceAuth';
2
+ export declare const beforeRegister: (fn: HookFn) => void;
3
+ export declare const afterRegister: (fn: HookFn) => void;
4
+ export declare const beforeLogin: (fn: HookFn) => void;
5
+ export declare const afterLogin: (fn: HookFn) => void;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.afterLogin = exports.beforeLogin = exports.afterRegister = exports.beforeRegister = void 0;
4
+ const deviceAuth_1 = require("../deviceAuth");
5
+ const beforeRegister = (fn) => {
6
+ deviceAuth_1.deviceAuth.registerHook('beforeRegister', fn);
7
+ };
8
+ exports.beforeRegister = beforeRegister;
9
+ const afterRegister = (fn) => {
10
+ deviceAuth_1.deviceAuth.registerHook('afterRegister', fn);
11
+ };
12
+ exports.afterRegister = afterRegister;
13
+ const beforeLogin = (fn) => {
14
+ deviceAuth_1.deviceAuth.registerHook('beforeLogin', fn);
15
+ };
16
+ exports.beforeLogin = beforeLogin;
17
+ const afterLogin = (fn) => {
18
+ deviceAuth_1.deviceAuth.registerHook('afterLogin', fn);
19
+ };
20
+ exports.afterLogin = afterLogin;
@@ -0,0 +1,15 @@
1
+ import type { AdapterUser } from '../adapters/base.adapter';
2
+ interface RequestLike {
3
+ headers?: Record<string, unknown>;
4
+ session?: any;
5
+ }
6
+ interface ResponseLike {
7
+ status(code: number): this;
8
+ json(body: any): this;
9
+ }
10
+ type NextFunction = () => void;
11
+ export interface AuthenticatedRequest extends RequestLike {
12
+ user?: AdapterUser | null;
13
+ }
14
+ export declare const authenticate: (req: AuthenticatedRequest, res: ResponseLike, next: NextFunction) => Promise<void>;
15
+ export {};
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authenticate = void 0;
4
+ const deviceAuth_1 = require("../deviceAuth");
5
+ const auth_service_factory_1 = require("../factories/auth-service.factory");
6
+ const sendUnauthorized = (res) => {
7
+ res.status(401).json({ error: 'Unauthorized' });
8
+ };
9
+ const authenticate = async (req, res, next) => {
10
+ const config = deviceAuth_1.deviceAuth.config;
11
+ try {
12
+ if (config.authType === 'jwt') {
13
+ const authHeader = req.headers?.['authorization'];
14
+ if (!authHeader || typeof authHeader !== 'string') {
15
+ sendUnauthorized(res);
16
+ return;
17
+ }
18
+ const [scheme, token] = authHeader.split(' ');
19
+ if (scheme !== 'Bearer' || !token) {
20
+ sendUnauthorized(res);
21
+ return;
22
+ }
23
+ const authService = (0, auth_service_factory_1.createAuthService)();
24
+ const user = await authService.validateUserFromToken(token);
25
+ if (!user) {
26
+ sendUnauthorized(res);
27
+ return;
28
+ }
29
+ req.user = user;
30
+ next();
31
+ return;
32
+ }
33
+ // Basic session-based auth support: expect req.session.user to be populated by the host app.
34
+ const sessionUser = req.session?.user;
35
+ if (!sessionUser) {
36
+ sendUnauthorized(res);
37
+ return;
38
+ }
39
+ req.user = sessionUser;
40
+ next();
41
+ }
42
+ catch (err) {
43
+ sendUnauthorized(res);
44
+ }
45
+ };
46
+ exports.authenticate = authenticate;
@@ -0,0 +1,8 @@
1
+ import type { AuthenticatedRequest } from './authenticate.middleware';
2
+ interface ResponseLike {
3
+ status(code: number): this;
4
+ json(body: any): this;
5
+ }
6
+ type NextFunction = () => void;
7
+ export declare const authorize: (...roles: string[]) => (req: AuthenticatedRequest, res: ResponseLike, next: NextFunction) => void;
8
+ export {};
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authorize = void 0;
4
+ const getUserRole = (user) => {
5
+ const role = user['role'];
6
+ if (role == null)
7
+ return null;
8
+ if (typeof role === 'string')
9
+ return role;
10
+ return String(role);
11
+ };
12
+ const sendUnauthorized = (res) => {
13
+ res.status(401).json({ error: 'Unauthorized' });
14
+ };
15
+ const authorize = (...roles) => {
16
+ return (req, res, next) => {
17
+ const fromReq = req.user;
18
+ const fromSession = req.session?.user;
19
+ const user = fromReq ?? fromSession ?? null;
20
+ if (!user) {
21
+ sendUnauthorized(res);
22
+ return;
23
+ }
24
+ const userRole = getUserRole(user);
25
+ if (roles.length === 0) {
26
+ next();
27
+ return;
28
+ }
29
+ if (userRole != null && roles.includes(userRole)) {
30
+ next();
31
+ return;
32
+ }
33
+ res.status(403).json({ error: 'Forbidden' });
34
+ };
35
+ };
36
+ exports.authorize = authorize;
@@ -0,0 +1,7 @@
1
+ import type { RegistrationsController } from '../controllers/registrations.controller';
2
+ import type { SessionsController } from '../controllers/sessions.controller';
3
+ export interface ControllerOverrideConfig {
4
+ registrations?: RegistrationsController;
5
+ sessions?: SessionsController;
6
+ }
7
+ export declare const applyControllerOverrides: (config: ControllerOverrideConfig) => void;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyControllerOverrides = void 0;
4
+ const deviceAuth_1 = require("../deviceAuth");
5
+ const applyControllerOverrides = (config) => {
6
+ if (config.registrations) {
7
+ deviceAuth_1.deviceAuth.override('registrations', config.registrations);
8
+ }
9
+ if (config.sessions) {
10
+ deviceAuth_1.deviceAuth.override('sessions', config.sessions);
11
+ }
12
+ };
13
+ exports.applyControllerOverrides = applyControllerOverrides;
@@ -0,0 +1,2 @@
1
+ import { Router } from 'express';
2
+ export declare const createAuthRouter: () => Router;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAuthRouter = void 0;
4
+ const express_1 = require("express");
5
+ const registrations_controller_1 = require("../controllers/registrations.controller");
6
+ const sessions_controller_1 = require("../controllers/sessions.controller");
7
+ const createAuthRouter = () => {
8
+ const router = (0, express_1.Router)();
9
+ const registrationsController = (0, registrations_controller_1.getRegistrationsController)();
10
+ const sessionsController = (0, sessions_controller_1.getSessionsController)();
11
+ router.post('/signup', async (req, res, next) => {
12
+ try {
13
+ const data = req.body ?? {};
14
+ if (!data || typeof data !== 'object' || Array.isArray(data)) {
15
+ return res.status(400).json({ error: 'Invalid signup payload' });
16
+ }
17
+ const result = await registrationsController(data);
18
+ res.json(result);
19
+ }
20
+ catch (error) {
21
+ const message = error instanceof Error ? error.message : 'An unexpected error occurred';
22
+ res.status(400).json({ error: message });
23
+ }
24
+ });
25
+ router.post('/login', async (req, res, next) => {
26
+ try {
27
+ const { email, password } = req.body ?? {};
28
+ if (typeof email !== 'string' || typeof password !== 'string') {
29
+ return res.status(400).json({ error: 'Invalid login payload' });
30
+ }
31
+ const result = await sessionsController(email, password);
32
+ res.json(result);
33
+ }
34
+ catch (error) {
35
+ const message = error instanceof Error ? error.message : 'An unexpected error occurred';
36
+ res.status(400).json({ error: message });
37
+ }
38
+ });
39
+ return router;
40
+ };
41
+ exports.createAuthRouter = createAuthRouter;
@@ -0,0 +1,11 @@
1
+ import type { BaseAdapter } from '../adapters/base.adapter';
2
+ import type { DeviceAuthConfig } from '../config';
3
+ export type Role = string;
4
+ export interface User extends Record<string, unknown> {
5
+ id?: string | number;
6
+ email?: string;
7
+ password?: string;
8
+ role?: Role;
9
+ }
10
+ export type Adapter = BaseAdapter;
11
+ export type Config = DeviceAuthConfig;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ export { pick } from './pick';
2
+ export type { ResponseLike } from './response';
3
+ export { sendJson, sendError, badRequest, unauthorized, forbidden, internalError, } from './response';
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.internalError = exports.forbidden = exports.unauthorized = exports.badRequest = exports.sendError = exports.sendJson = exports.pick = void 0;
4
+ var pick_1 = require("./pick");
5
+ Object.defineProperty(exports, "pick", { enumerable: true, get: function () { return pick_1.pick; } });
6
+ var response_1 = require("./response");
7
+ Object.defineProperty(exports, "sendJson", { enumerable: true, get: function () { return response_1.sendJson; } });
8
+ Object.defineProperty(exports, "sendError", { enumerable: true, get: function () { return response_1.sendError; } });
9
+ Object.defineProperty(exports, "badRequest", { enumerable: true, get: function () { return response_1.badRequest; } });
10
+ Object.defineProperty(exports, "unauthorized", { enumerable: true, get: function () { return response_1.unauthorized; } });
11
+ Object.defineProperty(exports, "forbidden", { enumerable: true, get: function () { return response_1.forbidden; } });
12
+ Object.defineProperty(exports, "internalError", { enumerable: true, get: function () { return response_1.internalError; } });
@@ -0,0 +1 @@
1
+ export declare const pick: <T extends object, const K extends readonly (keyof T)[]>(source: T, keys: K) => Pick<T, K[number]>;