@forjio/engine-auth 0.1.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.
Files changed (68) hide show
  1. package/dist/__tests__/api-keys.test.d.ts +2 -0
  2. package/dist/__tests__/api-keys.test.d.ts.map +1 -0
  3. package/dist/__tests__/api-keys.test.js +78 -0
  4. package/dist/__tests__/api-keys.test.js.map +1 -0
  5. package/dist/__tests__/helpers/express-mock.d.ts +13 -0
  6. package/dist/__tests__/helpers/express-mock.d.ts.map +1 -0
  7. package/dist/__tests__/helpers/express-mock.js +46 -0
  8. package/dist/__tests__/helpers/express-mock.js.map +1 -0
  9. package/dist/__tests__/helpers/redis-mock.d.ts +19 -0
  10. package/dist/__tests__/helpers/redis-mock.d.ts.map +1 -0
  11. package/dist/__tests__/helpers/redis-mock.js +69 -0
  12. package/dist/__tests__/helpers/redis-mock.js.map +1 -0
  13. package/dist/__tests__/jwt.test.d.ts +2 -0
  14. package/dist/__tests__/jwt.test.d.ts.map +1 -0
  15. package/dist/__tests__/jwt.test.js +98 -0
  16. package/dist/__tests__/jwt.test.js.map +1 -0
  17. package/dist/__tests__/middleware.test.d.ts +2 -0
  18. package/dist/__tests__/middleware.test.d.ts.map +1 -0
  19. package/dist/__tests__/middleware.test.js +173 -0
  20. package/dist/__tests__/middleware.test.js.map +1 -0
  21. package/dist/__tests__/session.test.d.ts +2 -0
  22. package/dist/__tests__/session.test.d.ts.map +1 -0
  23. package/dist/__tests__/session.test.js +96 -0
  24. package/dist/__tests__/session.test.js.map +1 -0
  25. package/dist/api-keys.d.ts +29 -0
  26. package/dist/api-keys.d.ts.map +1 -0
  27. package/dist/api-keys.js +50 -0
  28. package/dist/api-keys.js.map +1 -0
  29. package/dist/index.d.ts +10 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +31 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/jwt.d.ts +7 -0
  34. package/dist/jwt.d.ts.map +1 -0
  35. package/dist/jwt.js +66 -0
  36. package/dist/jwt.js.map +1 -0
  37. package/dist/middleware.d.ts +29 -0
  38. package/dist/middleware.d.ts.map +1 -0
  39. package/dist/middleware.js +94 -0
  40. package/dist/middleware.js.map +1 -0
  41. package/dist/oauth.d.ts +16 -0
  42. package/dist/oauth.d.ts.map +1 -0
  43. package/dist/oauth.js +78 -0
  44. package/dist/oauth.js.map +1 -0
  45. package/dist/session.d.ts +9 -0
  46. package/dist/session.d.ts.map +1 -0
  47. package/dist/session.js +61 -0
  48. package/dist/session.js.map +1 -0
  49. package/dist/types.d.ts +50 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +3 -0
  52. package/dist/types.js.map +1 -0
  53. package/package.json +34 -0
  54. package/src/__tests__/api-keys.test.ts +94 -0
  55. package/src/__tests__/helpers/express-mock.ts +47 -0
  56. package/src/__tests__/helpers/redis-mock.ts +65 -0
  57. package/src/__tests__/jwt.test.ts +122 -0
  58. package/src/__tests__/middleware.test.ts +220 -0
  59. package/src/__tests__/session.test.ts +120 -0
  60. package/src/api-keys.ts +56 -0
  61. package/src/index.ts +46 -0
  62. package/src/jwt.ts +61 -0
  63. package/src/middleware.ts +113 -0
  64. package/src/oauth.ts +111 -0
  65. package/src/session.ts +78 -0
  66. package/src/types.ts +45 -0
  67. package/tsconfig.json +19 -0
  68. package/vitest.config.ts +21 -0
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const redis_mock_1 = require("./helpers/redis-mock");
5
+ // Mock ioredis before importing session module
6
+ const redisMock = new redis_mock_1.RedisMock();
7
+ vitest_1.vi.mock('ioredis', () => ({
8
+ default: vitest_1.vi.fn(() => redisMock),
9
+ }));
10
+ // Import after mock is set up
11
+ const session_1 = require("../session");
12
+ const TEST_SESSION = {
13
+ userId: 'user-456',
14
+ email: 'session@example.com',
15
+ role: 'user',
16
+ productSlug: 'portal',
17
+ expiresAt: Date.now() + 3600_000, // 1 hour from now
18
+ };
19
+ (0, vitest_1.describe)('session store', () => {
20
+ let store;
21
+ (0, vitest_1.beforeEach)(() => {
22
+ redisMock.clear();
23
+ store = (0, session_1.createSessionStore)('redis://localhost:6379');
24
+ });
25
+ (0, vitest_1.describe)('create', () => {
26
+ (0, vitest_1.it)('should create session and return session ID', async () => {
27
+ const sessionId = await store.create(TEST_SESSION);
28
+ (0, vitest_1.expect)(sessionId).toBeDefined();
29
+ (0, vitest_1.expect)(typeof sessionId).toBe('string');
30
+ (0, vitest_1.expect)(sessionId.length).toBeGreaterThan(0);
31
+ });
32
+ (0, vitest_1.it)('should store session data retrievable by ID', async () => {
33
+ const sessionId = await store.create(TEST_SESSION);
34
+ const retrieved = await store.get(sessionId);
35
+ (0, vitest_1.expect)(retrieved).not.toBeNull();
36
+ (0, vitest_1.expect)(retrieved.userId).toBe('user-456');
37
+ (0, vitest_1.expect)(retrieved.email).toBe('session@example.com');
38
+ (0, vitest_1.expect)(retrieved.role).toBe('user');
39
+ });
40
+ });
41
+ (0, vitest_1.describe)('get', () => {
42
+ (0, vitest_1.it)('should return session data for valid ID', async () => {
43
+ const sessionId = await store.create(TEST_SESSION);
44
+ const session = await store.get(sessionId);
45
+ (0, vitest_1.expect)(session).not.toBeNull();
46
+ (0, vitest_1.expect)(session.userId).toBe(TEST_SESSION.userId);
47
+ (0, vitest_1.expect)(session.productSlug).toBe(TEST_SESSION.productSlug);
48
+ });
49
+ (0, vitest_1.it)('should return null for non-existent ID', async () => {
50
+ const session = await store.get('non-existent-id');
51
+ (0, vitest_1.expect)(session).toBeNull();
52
+ });
53
+ (0, vitest_1.it)('should return null for logically expired session', async () => {
54
+ const expiredSession = {
55
+ ...TEST_SESSION,
56
+ expiresAt: Date.now() - 1000, // already expired
57
+ };
58
+ const sessionId = await store.create(expiredSession);
59
+ const session = await store.get(sessionId);
60
+ (0, vitest_1.expect)(session).toBeNull();
61
+ });
62
+ });
63
+ (0, vitest_1.describe)('destroy', () => {
64
+ (0, vitest_1.it)('should remove session', async () => {
65
+ const sessionId = await store.create(TEST_SESSION);
66
+ // Verify it exists first
67
+ (0, vitest_1.expect)(await store.get(sessionId)).not.toBeNull();
68
+ await store.destroy(sessionId);
69
+ (0, vitest_1.expect)(await store.get(sessionId)).toBeNull();
70
+ });
71
+ });
72
+ (0, vitest_1.describe)('destroyAllForUser', () => {
73
+ (0, vitest_1.it)('should remove all sessions for a user', async () => {
74
+ const id1 = await store.create(TEST_SESSION);
75
+ const id2 = await store.create({ ...TEST_SESSION, productSlug: 'other' });
76
+ // Both should exist
77
+ (0, vitest_1.expect)(await store.get(id1)).not.toBeNull();
78
+ (0, vitest_1.expect)(await store.get(id2)).not.toBeNull();
79
+ await store.destroyAllForUser(TEST_SESSION.userId);
80
+ (0, vitest_1.expect)(await store.get(id1)).toBeNull();
81
+ (0, vitest_1.expect)(await store.get(id2)).toBeNull();
82
+ });
83
+ (0, vitest_1.it)('should not affect other users sessions', async () => {
84
+ const id1 = await store.create(TEST_SESSION);
85
+ const otherSession = {
86
+ ...TEST_SESSION,
87
+ userId: 'other-user',
88
+ };
89
+ const id2 = await store.create(otherSession);
90
+ await store.destroyAllForUser(TEST_SESSION.userId);
91
+ (0, vitest_1.expect)(await store.get(id1)).toBeNull();
92
+ (0, vitest_1.expect)(await store.get(id2)).not.toBeNull();
93
+ });
94
+ });
95
+ });
96
+ //# sourceMappingURL=session.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.test.js","sourceRoot":"","sources":["../../src/__tests__/session.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export interface CreateApiKeyInput {
2
+ userId: string;
3
+ productSlug: string;
4
+ scopes: string[];
5
+ expiresAt?: Date | null;
6
+ }
7
+ export interface ApiKeyWithHash {
8
+ plainKey: string;
9
+ hashedKey: string;
10
+ }
11
+ /**
12
+ * Generates a new API key with the `ek_` prefix.
13
+ * Optionally accepts a custom prefix that replaces the default.
14
+ */
15
+ export declare function generateApiKey(prefix?: string): string;
16
+ /**
17
+ * Creates a SHA-256 hash of an API key for secure storage.
18
+ * Only the hash should be persisted — never store the plain key.
19
+ */
20
+ export declare function hashApiKey(key: string): string;
21
+ /**
22
+ * Validates that a key matches the expected `ek_` format and length.
23
+ */
24
+ export declare function validateApiKeyFormat(key: string): boolean;
25
+ /**
26
+ * Generates a new API key and returns both the plain key and its hash.
27
+ */
28
+ export declare function createApiKeyPair(prefix?: string): ApiKeyWithHash;
29
+ //# sourceMappingURL=api-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.d.ts","sourceRoot":"","sources":["../src/api-keys.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAItD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,CAIhE"}
@@ -0,0 +1,50 @@
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.generateApiKey = generateApiKey;
7
+ exports.hashApiKey = hashApiKey;
8
+ exports.validateApiKeyFormat = validateApiKeyFormat;
9
+ exports.createApiKeyPair = createApiKeyPair;
10
+ const crypto_1 = __importDefault(require("crypto"));
11
+ const API_KEY_PREFIX = 'ek_';
12
+ const API_KEY_BYTES = 32;
13
+ const EXPECTED_KEY_LENGTH = API_KEY_PREFIX.length + API_KEY_BYTES * 2; // hex encoding
14
+ /**
15
+ * Generates a new API key with the `ek_` prefix.
16
+ * Optionally accepts a custom prefix that replaces the default.
17
+ */
18
+ function generateApiKey(prefix) {
19
+ const keyPrefix = prefix || API_KEY_PREFIX;
20
+ const randomPart = crypto_1.default.randomBytes(API_KEY_BYTES).toString('hex');
21
+ return `${keyPrefix}${randomPart}`;
22
+ }
23
+ /**
24
+ * Creates a SHA-256 hash of an API key for secure storage.
25
+ * Only the hash should be persisted — never store the plain key.
26
+ */
27
+ function hashApiKey(key) {
28
+ return crypto_1.default.createHash('sha256').update(key).digest('hex');
29
+ }
30
+ /**
31
+ * Validates that a key matches the expected `ek_` format and length.
32
+ */
33
+ function validateApiKeyFormat(key) {
34
+ if (!key.startsWith(API_KEY_PREFIX))
35
+ return false;
36
+ if (key.length !== EXPECTED_KEY_LENGTH)
37
+ return false;
38
+ // Verify the hex portion is valid
39
+ const hexPart = key.slice(API_KEY_PREFIX.length);
40
+ return /^[a-f0-9]+$/.test(hexPart);
41
+ }
42
+ /**
43
+ * Generates a new API key and returns both the plain key and its hash.
44
+ */
45
+ function createApiKeyPair(prefix) {
46
+ const plainKey = generateApiKey(prefix);
47
+ const hashedKey = hashApiKey(plainKey);
48
+ return { plainKey, hashedKey };
49
+ }
50
+ //# sourceMappingURL=api-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../src/api-keys.ts"],"names":[],"mappings":";;;;;AAsBA,wCAIC;AAMD,gCAEC;AAKD,oDAOC;AAKD,4CAIC;AAvDD,oDAA4B;AAE5B,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7B,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,mBAAmB,GAAG,cAAc,CAAC,MAAM,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,eAAe;AActF;;;GAGG;AACH,SAAgB,cAAc,CAAC,MAAe;IAC5C,MAAM,SAAS,GAAG,MAAM,IAAI,cAAc,CAAC;IAC3C,MAAM,UAAU,GAAG,gBAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrE,OAAO,GAAG,SAAS,GAAG,UAAU,EAAE,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,SAAgB,UAAU,CAAC,GAAW;IACpC,OAAO,gBAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB,CAAC,GAAW;IAC9C,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC;QAAE,OAAO,KAAK,CAAC;IAClD,IAAI,GAAG,CAAC,MAAM,KAAK,mBAAmB;QAAE,OAAO,KAAK,CAAC;IAErD,kCAAkC;IAClC,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,OAAO,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAAC,MAAe;IAC9C,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACvC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export type { EngineUser, ApiKey, SessionData, JwtPayload, AuthConfig, } from './types';
2
+ export { signAccessToken, signRefreshToken, signRememberMeToken, verifyAccessToken, verifyRefreshToken, } from './jwt';
3
+ export { createSessionStore } from './session';
4
+ export type { SessionStore } from './session';
5
+ export { generateApiKey, hashApiKey, validateApiKeyFormat, createApiKeyPair, } from './api-keys';
6
+ export type { CreateApiKeyInput, ApiKeyWithHash } from './api-keys';
7
+ export { configureGoogleStrategy, configureGithubStrategy, setupOAuth, } from './oauth';
8
+ export type { OAuthProfile, FindOrCreateUser } from './oauth';
9
+ export { authenticateToken, requireApiKey, requireProduct, requireRole, } from './middleware';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,UAAU,EACV,UAAU,GACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,OAAO,CAAC;AAGf,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG9C,OAAO,EACL,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGpE,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG9D,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireRole = exports.requireProduct = exports.requireApiKey = exports.authenticateToken = exports.setupOAuth = exports.configureGithubStrategy = exports.configureGoogleStrategy = exports.createApiKeyPair = exports.validateApiKeyFormat = exports.hashApiKey = exports.generateApiKey = exports.createSessionStore = exports.verifyRefreshToken = exports.verifyAccessToken = exports.signRememberMeToken = exports.signRefreshToken = exports.signAccessToken = void 0;
4
+ // JWT utilities
5
+ var jwt_1 = require("./jwt");
6
+ Object.defineProperty(exports, "signAccessToken", { enumerable: true, get: function () { return jwt_1.signAccessToken; } });
7
+ Object.defineProperty(exports, "signRefreshToken", { enumerable: true, get: function () { return jwt_1.signRefreshToken; } });
8
+ Object.defineProperty(exports, "signRememberMeToken", { enumerable: true, get: function () { return jwt_1.signRememberMeToken; } });
9
+ Object.defineProperty(exports, "verifyAccessToken", { enumerable: true, get: function () { return jwt_1.verifyAccessToken; } });
10
+ Object.defineProperty(exports, "verifyRefreshToken", { enumerable: true, get: function () { return jwt_1.verifyRefreshToken; } });
11
+ // Session store
12
+ var session_1 = require("./session");
13
+ Object.defineProperty(exports, "createSessionStore", { enumerable: true, get: function () { return session_1.createSessionStore; } });
14
+ // API key utilities
15
+ var api_keys_1 = require("./api-keys");
16
+ Object.defineProperty(exports, "generateApiKey", { enumerable: true, get: function () { return api_keys_1.generateApiKey; } });
17
+ Object.defineProperty(exports, "hashApiKey", { enumerable: true, get: function () { return api_keys_1.hashApiKey; } });
18
+ Object.defineProperty(exports, "validateApiKeyFormat", { enumerable: true, get: function () { return api_keys_1.validateApiKeyFormat; } });
19
+ Object.defineProperty(exports, "createApiKeyPair", { enumerable: true, get: function () { return api_keys_1.createApiKeyPair; } });
20
+ // OAuth strategies
21
+ var oauth_1 = require("./oauth");
22
+ Object.defineProperty(exports, "configureGoogleStrategy", { enumerable: true, get: function () { return oauth_1.configureGoogleStrategy; } });
23
+ Object.defineProperty(exports, "configureGithubStrategy", { enumerable: true, get: function () { return oauth_1.configureGithubStrategy; } });
24
+ Object.defineProperty(exports, "setupOAuth", { enumerable: true, get: function () { return oauth_1.setupOAuth; } });
25
+ // Express middleware
26
+ var middleware_1 = require("./middleware");
27
+ Object.defineProperty(exports, "authenticateToken", { enumerable: true, get: function () { return middleware_1.authenticateToken; } });
28
+ Object.defineProperty(exports, "requireApiKey", { enumerable: true, get: function () { return middleware_1.requireApiKey; } });
29
+ Object.defineProperty(exports, "requireProduct", { enumerable: true, get: function () { return middleware_1.requireProduct; } });
30
+ Object.defineProperty(exports, "requireRole", { enumerable: true, get: function () { return middleware_1.requireRole; } });
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AASA,gBAAgB;AAChB,6BAMe;AALb,sGAAA,eAAe,OAAA;AACf,uGAAA,gBAAgB,OAAA;AAChB,0GAAA,mBAAmB,OAAA;AACnB,wGAAA,iBAAiB,OAAA;AACjB,yGAAA,kBAAkB,OAAA;AAGpB,gBAAgB;AAChB,qCAA+C;AAAtC,6GAAA,kBAAkB,OAAA;AAG3B,oBAAoB;AACpB,uCAKoB;AAJlB,0GAAA,cAAc,OAAA;AACd,sGAAA,UAAU,OAAA;AACV,gHAAA,oBAAoB,OAAA;AACpB,4GAAA,gBAAgB,OAAA;AAIlB,mBAAmB;AACnB,iCAIiB;AAHf,gHAAA,uBAAuB,OAAA;AACvB,gHAAA,uBAAuB,OAAA;AACvB,mGAAA,UAAU,OAAA;AAIZ,qBAAqB;AACrB,2CAKsB;AAJpB,+GAAA,iBAAiB,OAAA;AACjB,2GAAA,aAAa,OAAA;AACb,4GAAA,cAAc,OAAA;AACd,yGAAA,WAAW,OAAA"}
package/dist/jwt.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { JwtPayload, AuthConfig } from './types';
2
+ export declare function signAccessToken(payload: JwtPayload, config: AuthConfig): string;
3
+ export declare function signRefreshToken(payload: JwtPayload, config: AuthConfig): string;
4
+ export declare function signRememberMeToken(payload: JwtPayload, config: AuthConfig): string;
5
+ export declare function verifyAccessToken(token: string, config: AuthConfig): JwtPayload;
6
+ export declare function verifyRefreshToken(token: string, config: AuthConfig): JwtPayload;
7
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAuBjD,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAI/E;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAIhF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAInF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU,CAQ/E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU,CAQhF"}
package/dist/jwt.js ADDED
@@ -0,0 +1,66 @@
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.signAccessToken = signAccessToken;
7
+ exports.signRefreshToken = signRefreshToken;
8
+ exports.signRememberMeToken = signRememberMeToken;
9
+ exports.verifyAccessToken = verifyAccessToken;
10
+ exports.verifyRefreshToken = verifyRefreshToken;
11
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
12
+ // Expiry values in seconds to satisfy jsonwebtoken's type requirements
13
+ const DEFAULT_ACCESS_EXPIRY = 15 * 60; // 15 minutes
14
+ const DEFAULT_REFRESH_EXPIRY = 7 * 24 * 60 * 60; // 7 days
15
+ const REMEMBER_ME_EXPIRY = 30 * 24 * 60 * 60; // 30 days
16
+ function parseExpiry(value, fallback) {
17
+ if (!value)
18
+ return fallback;
19
+ // Parse simple duration strings: "15m", "7d", "30d", "1h"
20
+ const match = value.match(/^(\d+)(s|m|h|d)$/);
21
+ if (!match)
22
+ return fallback;
23
+ const num = parseInt(match[1], 10);
24
+ const unit = match[2];
25
+ switch (unit) {
26
+ case 's': return num;
27
+ case 'm': return num * 60;
28
+ case 'h': return num * 3600;
29
+ case 'd': return num * 86400;
30
+ default: return fallback;
31
+ }
32
+ }
33
+ function signAccessToken(payload, config) {
34
+ return jsonwebtoken_1.default.sign(payload, config.jwtSecret, {
35
+ expiresIn: parseExpiry(config.jwtExpiresIn, DEFAULT_ACCESS_EXPIRY),
36
+ });
37
+ }
38
+ function signRefreshToken(payload, config) {
39
+ return jsonwebtoken_1.default.sign(payload, config.jwtRefreshSecret, {
40
+ expiresIn: parseExpiry(config.jwtRefreshExpiresIn, DEFAULT_REFRESH_EXPIRY),
41
+ });
42
+ }
43
+ function signRememberMeToken(payload, config) {
44
+ return jsonwebtoken_1.default.sign(payload, config.jwtRefreshSecret, {
45
+ expiresIn: REMEMBER_ME_EXPIRY,
46
+ });
47
+ }
48
+ function verifyAccessToken(token, config) {
49
+ const decoded = jsonwebtoken_1.default.verify(token, config.jwtSecret);
50
+ return {
51
+ userId: decoded.userId,
52
+ email: decoded.email,
53
+ role: decoded.role,
54
+ subscriptionTier: decoded.subscriptionTier,
55
+ };
56
+ }
57
+ function verifyRefreshToken(token, config) {
58
+ const decoded = jsonwebtoken_1.default.verify(token, config.jwtRefreshSecret);
59
+ return {
60
+ userId: decoded.userId,
61
+ email: decoded.email,
62
+ role: decoded.role,
63
+ subscriptionTier: decoded.subscriptionTier,
64
+ };
65
+ }
66
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":";;;;;AAwBA,0CAIC;AAED,4CAIC;AAED,kDAIC;AAED,8CAQC;AAED,gDAQC;AA5DD,gEAA+B;AAG/B,uEAAuE;AACvE,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,aAAa;AACpD,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,SAAS;AAC1D,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU;AAExD,SAAS,WAAW,CAAC,KAAyB,EAAE,QAAgB;IAC9D,IAAI,CAAC,KAAK;QAAE,OAAO,QAAQ,CAAC;IAC5B,0DAA0D;IAC1D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK;QAAE,OAAO,QAAQ,CAAC;IAC5B,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,CAAC;QACrB,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;QAC1B,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,IAAI,CAAC;QAC5B,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,KAAK,CAAC;QAC7B,OAAO,CAAC,CAAC,OAAO,QAAQ,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,SAAgB,eAAe,CAAC,OAAmB,EAAE,MAAkB;IACrE,OAAO,sBAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE;QACzC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,YAAY,EAAE,qBAAqB,CAAC;KACnE,CAAC,CAAC;AACL,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAmB,EAAE,MAAkB;IACtE,OAAO,sBAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,gBAAgB,EAAE;QAChD,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,mBAAmB,EAAE,sBAAsB,CAAC;KAC3E,CAAC,CAAC;AACL,CAAC;AAED,SAAgB,mBAAmB,CAAC,OAAmB,EAAE,MAAkB;IACzE,OAAO,sBAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,gBAAgB,EAAE;QAChD,SAAS,EAAE,kBAAkB;KAC9B,CAAC,CAAC;AACL,CAAC;AAED,SAAgB,iBAAiB,CAAC,KAAa,EAAE,MAAkB;IACjE,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAgC,CAAC;IACnF,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;KAC3C,CAAC;AACJ,CAAC;AAED,SAAgB,kBAAkB,CAAC,KAAa,EAAE,MAAkB;IAClE,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,gBAAgB,CAAgC,CAAC;IAC1F,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;KAC3C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { AuthConfig, JwtPayload, ApiKey } from './types';
3
+ declare module 'express-serve-static-core' {
4
+ interface Request {
5
+ authUser?: JwtPayload;
6
+ apiKey?: ApiKey;
7
+ }
8
+ }
9
+ /**
10
+ * Middleware that validates a Bearer token from the Authorization header.
11
+ * On success, attaches the decoded JwtPayload to `req.user`.
12
+ */
13
+ export declare function authenticateToken(config: AuthConfig): (req: Request, res: Response, next: NextFunction) => void;
14
+ /**
15
+ * Middleware that validates an API key from the X-API-Key header.
16
+ * The `validateKey` callback should look up the hashed key in your DB.
17
+ */
18
+ export declare function requireApiKey(validateKey: (key: string) => Promise<ApiKey | null>): (req: Request, res: Response, next: NextFunction) => Promise<void>;
19
+ /**
20
+ * Middleware that ensures the API key is scoped to a specific product.
21
+ * Must be used after `requireApiKey`.
22
+ */
23
+ export declare function requireProduct(productSlug: string): (req: Request, res: Response, next: NextFunction) => void;
24
+ /**
25
+ * Middleware that restricts access to specific user roles.
26
+ * Must be used after `authenticateToken`.
27
+ */
28
+ export declare function requireRole(...roles: string[]): (req: Request, res: Response, next: NextFunction) => void;
29
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGzD,OAAO,QAAQ,2BAA2B,CAAC;IACzC,UAAU,OAAO;QACf,QAAQ,CAAC,EAAE,UAAU,CAAC;QACtB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;CACF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,IAC1C,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAkB/D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,IAEtC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CA4B9E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,IACxC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAa/D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,IACpC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAa/D"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authenticateToken = authenticateToken;
4
+ exports.requireApiKey = requireApiKey;
5
+ exports.requireProduct = requireProduct;
6
+ exports.requireRole = requireRole;
7
+ const jwt_1 = require("./jwt");
8
+ /**
9
+ * Middleware that validates a Bearer token from the Authorization header.
10
+ * On success, attaches the decoded JwtPayload to `req.user`.
11
+ */
12
+ function authenticateToken(config) {
13
+ return (req, res, next) => {
14
+ const authHeader = req.headers.authorization;
15
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
16
+ res.status(401).json({ error: 'Missing or invalid Authorization header' });
17
+ return;
18
+ }
19
+ const token = authHeader.slice(7);
20
+ try {
21
+ const payload = (0, jwt_1.verifyAccessToken)(token, config);
22
+ req.authUser = payload;
23
+ next();
24
+ }
25
+ catch {
26
+ res.status(401).json({ error: 'Invalid or expired token' });
27
+ }
28
+ };
29
+ }
30
+ /**
31
+ * Middleware that validates an API key from the X-API-Key header.
32
+ * The `validateKey` callback should look up the hashed key in your DB.
33
+ */
34
+ function requireApiKey(validateKey) {
35
+ return async (req, res, next) => {
36
+ const apiKey = req.headers['x-api-key'];
37
+ if (!apiKey) {
38
+ res.status(401).json({ error: 'Missing X-API-Key header' });
39
+ return;
40
+ }
41
+ try {
42
+ const keyRecord = await validateKey(apiKey);
43
+ if (!keyRecord) {
44
+ res.status(401).json({ error: 'Invalid API key' });
45
+ return;
46
+ }
47
+ // Check expiration
48
+ if (keyRecord.expiresAt && new Date(keyRecord.expiresAt) < new Date()) {
49
+ res.status(401).json({ error: 'API key has expired' });
50
+ return;
51
+ }
52
+ req.apiKey = keyRecord;
53
+ next();
54
+ }
55
+ catch {
56
+ res.status(500).json({ error: 'Failed to validate API key' });
57
+ }
58
+ };
59
+ }
60
+ /**
61
+ * Middleware that ensures the API key is scoped to a specific product.
62
+ * Must be used after `requireApiKey`.
63
+ */
64
+ function requireProduct(productSlug) {
65
+ return (req, res, next) => {
66
+ if (!req.apiKey) {
67
+ res.status(401).json({ error: 'No API key context' });
68
+ return;
69
+ }
70
+ if (req.apiKey.productSlug !== productSlug) {
71
+ res.status(403).json({ error: `API key not authorized for product: ${productSlug}` });
72
+ return;
73
+ }
74
+ next();
75
+ };
76
+ }
77
+ /**
78
+ * Middleware that restricts access to specific user roles.
79
+ * Must be used after `authenticateToken`.
80
+ */
81
+ function requireRole(...roles) {
82
+ return (req, res, next) => {
83
+ if (!req.authUser) {
84
+ res.status(401).json({ error: 'Not authenticated' });
85
+ return;
86
+ }
87
+ if (!roles.includes(req.authUser.role)) {
88
+ res.status(403).json({ error: 'Insufficient permissions' });
89
+ return;
90
+ }
91
+ next();
92
+ };
93
+ }
94
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { AuthConfig } from './types';
2
+ export interface OAuthProfile {
3
+ provider: 'google' | 'github';
4
+ providerId: string;
5
+ email: string;
6
+ name: string | null;
7
+ avatarUrl: string | null;
8
+ }
9
+ export type FindOrCreateUser = (profile: OAuthProfile) => Promise<{
10
+ id: string;
11
+ email: string;
12
+ }>;
13
+ export declare function configureGoogleStrategy(config: AuthConfig, findOrCreateUser: FindOrCreateUser): void;
14
+ export declare function configureGithubStrategy(config: AuthConfig, findOrCreateUser: FindOrCreateUser): void;
15
+ export declare function setupOAuth(config: AuthConfig, findOrCreateUser: FindOrCreateUser): void;
16
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,gBAAgB,GAAG,CAC7B,OAAO,EAAE,YAAY,KAClB,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE5C,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,UAAU,EAClB,gBAAgB,EAAE,gBAAgB,GACjC,IAAI,CAkCN;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,UAAU,EAClB,gBAAgB,EAAE,gBAAgB,GACjC,IAAI,CAuCN;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,UAAU,EAClB,gBAAgB,EAAE,gBAAgB,GACjC,IAAI,CAON"}
package/dist/oauth.js ADDED
@@ -0,0 +1,78 @@
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.configureGoogleStrategy = configureGoogleStrategy;
7
+ exports.configureGithubStrategy = configureGithubStrategy;
8
+ exports.setupOAuth = setupOAuth;
9
+ const passport_1 = __importDefault(require("passport"));
10
+ const passport_google_oauth20_1 = require("passport-google-oauth20");
11
+ const passport_github2_1 = require("passport-github2");
12
+ function configureGoogleStrategy(config, findOrCreateUser) {
13
+ if (!config.google)
14
+ return;
15
+ passport_1.default.use(new passport_google_oauth20_1.Strategy({
16
+ clientID: config.google.clientId,
17
+ clientSecret: config.google.clientSecret,
18
+ callbackURL: config.google.callbackUrl,
19
+ scope: ['profile', 'email'],
20
+ }, async (_accessToken, _refreshToken, profile, done) => {
21
+ try {
22
+ const email = profile.emails?.[0]?.value;
23
+ if (!email) {
24
+ return done(new Error('No email found in Google profile'));
25
+ }
26
+ const oauthProfile = {
27
+ provider: 'google',
28
+ providerId: profile.id,
29
+ email,
30
+ name: profile.displayName || null,
31
+ avatarUrl: profile.photos?.[0]?.value || null,
32
+ };
33
+ const user = await findOrCreateUser(oauthProfile);
34
+ return done(null, user);
35
+ }
36
+ catch (err) {
37
+ return done(err);
38
+ }
39
+ }));
40
+ }
41
+ function configureGithubStrategy(config, findOrCreateUser) {
42
+ if (!config.github)
43
+ return;
44
+ passport_1.default.use(new passport_github2_1.Strategy({
45
+ clientID: config.github.clientId,
46
+ clientSecret: config.github.clientSecret,
47
+ callbackURL: config.github.callbackUrl,
48
+ scope: ['user:email'],
49
+ }, async (_accessToken, _refreshToken, profile, done) => {
50
+ try {
51
+ const email = profile.emails?.[0]?.value;
52
+ if (!email) {
53
+ return done(new Error('No email found in GitHub profile'));
54
+ }
55
+ const oauthProfile = {
56
+ provider: 'github',
57
+ providerId: profile.id,
58
+ email,
59
+ name: profile.displayName || null,
60
+ avatarUrl: profile.photos?.[0]?.value || null,
61
+ };
62
+ const user = await findOrCreateUser(oauthProfile);
63
+ return done(null, user);
64
+ }
65
+ catch (err) {
66
+ return done(err);
67
+ }
68
+ }));
69
+ }
70
+ function setupOAuth(config, findOrCreateUser) {
71
+ if (config.google) {
72
+ configureGoogleStrategy(config, findOrCreateUser);
73
+ }
74
+ if (config.github) {
75
+ configureGithubStrategy(config, findOrCreateUser);
76
+ }
77
+ }
78
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { SessionData } from './types';
2
+ export interface SessionStore {
3
+ create(sessionData: SessionData): Promise<string>;
4
+ get(sessionId: string): Promise<SessionData | null>;
5
+ destroy(sessionId: string): Promise<void>;
6
+ destroyAllForUser(userId: string): Promise<void>;
7
+ }
8
+ export declare function createSessionStore(redisUrl: string): SessionStore;
9
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAMtC,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACpD,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CA8DjE"}