@agentlensai/server 0.10.0 → 0.11.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.
- package/dist/cloud/auth/api-key-middleware.d.ts +66 -0
- package/dist/cloud/auth/api-key-middleware.d.ts.map +1 -0
- package/dist/cloud/auth/api-key-middleware.js +147 -0
- package/dist/cloud/auth/api-key-middleware.js.map +1 -0
- package/dist/cloud/auth/api-keys.d.ts +90 -0
- package/dist/cloud/auth/api-keys.d.ts.map +1 -0
- package/dist/cloud/auth/api-keys.js +162 -0
- package/dist/cloud/auth/api-keys.js.map +1 -0
- package/dist/cloud/auth/audit-log.d.ts +66 -0
- package/dist/cloud/auth/audit-log.d.ts.map +1 -0
- package/dist/cloud/auth/audit-log.js +92 -0
- package/dist/cloud/auth/audit-log.js.map +1 -0
- package/dist/cloud/auth/auth-service.d.ts +77 -0
- package/dist/cloud/auth/auth-service.d.ts.map +1 -0
- package/dist/cloud/auth/auth-service.js +229 -0
- package/dist/cloud/auth/auth-service.js.map +1 -0
- package/dist/cloud/auth/brute-force.d.ts +36 -0
- package/dist/cloud/auth/brute-force.d.ts.map +1 -0
- package/dist/cloud/auth/brute-force.js +67 -0
- package/dist/cloud/auth/brute-force.js.map +1 -0
- package/dist/cloud/auth/index.d.ts +11 -0
- package/dist/cloud/auth/index.d.ts.map +1 -0
- package/dist/cloud/auth/index.js +11 -0
- package/dist/cloud/auth/index.js.map +1 -0
- package/dist/cloud/auth/jwt.d.ts +34 -0
- package/dist/cloud/auth/jwt.d.ts.map +1 -0
- package/dist/cloud/auth/jwt.js +68 -0
- package/dist/cloud/auth/jwt.js.map +1 -0
- package/dist/cloud/auth/oauth.d.ts +37 -0
- package/dist/cloud/auth/oauth.d.ts.map +1 -0
- package/dist/cloud/auth/oauth.js +120 -0
- package/dist/cloud/auth/oauth.js.map +1 -0
- package/dist/cloud/auth/passwords.d.ts +25 -0
- package/dist/cloud/auth/passwords.d.ts.map +1 -0
- package/dist/cloud/auth/passwords.js +50 -0
- package/dist/cloud/auth/passwords.js.map +1 -0
- package/dist/cloud/auth/rbac.d.ts +51 -0
- package/dist/cloud/auth/rbac.d.ts.map +1 -0
- package/dist/cloud/auth/rbac.js +89 -0
- package/dist/cloud/auth/rbac.js.map +1 -0
- package/dist/cloud/auth/tokens.d.ts +18 -0
- package/dist/cloud/auth/tokens.d.ts.map +1 -0
- package/dist/cloud/auth/tokens.js +29 -0
- package/dist/cloud/auth/tokens.js.map +1 -0
- package/dist/cloud/billing/billing-service.d.ts +44 -0
- package/dist/cloud/billing/billing-service.d.ts.map +1 -0
- package/dist/cloud/billing/billing-service.js +153 -0
- package/dist/cloud/billing/billing-service.js.map +1 -0
- package/dist/cloud/billing/index.d.ts +11 -0
- package/dist/cloud/billing/index.d.ts.map +1 -0
- package/dist/cloud/billing/index.js +11 -0
- package/dist/cloud/billing/index.js.map +1 -0
- package/dist/cloud/billing/invoice-service.d.ts +57 -0
- package/dist/cloud/billing/invoice-service.d.ts.map +1 -0
- package/dist/cloud/billing/invoice-service.js +123 -0
- package/dist/cloud/billing/invoice-service.js.map +1 -0
- package/dist/cloud/billing/plan-management.d.ts +46 -0
- package/dist/cloud/billing/plan-management.d.ts.map +1 -0
- package/dist/cloud/billing/plan-management.js +157 -0
- package/dist/cloud/billing/plan-management.js.map +1 -0
- package/dist/cloud/billing/quota-enforcement.d.ts +53 -0
- package/dist/cloud/billing/quota-enforcement.d.ts.map +1 -0
- package/dist/cloud/billing/quota-enforcement.js +143 -0
- package/dist/cloud/billing/quota-enforcement.js.map +1 -0
- package/dist/cloud/billing/stripe-client.d.ts +142 -0
- package/dist/cloud/billing/stripe-client.d.ts.map +1 -0
- package/dist/cloud/billing/stripe-client.js +169 -0
- package/dist/cloud/billing/stripe-client.js.map +1 -0
- package/dist/cloud/billing/trial-service.d.ts +47 -0
- package/dist/cloud/billing/trial-service.d.ts.map +1 -0
- package/dist/cloud/billing/trial-service.js +104 -0
- package/dist/cloud/billing/trial-service.js.map +1 -0
- package/dist/cloud/billing/usage-metering.d.ts +83 -0
- package/dist/cloud/billing/usage-metering.d.ts.map +1 -0
- package/dist/cloud/billing/usage-metering.js +174 -0
- package/dist/cloud/billing/usage-metering.js.map +1 -0
- package/dist/cloud/ingestion/backpressure.d.ts +107 -0
- package/dist/cloud/ingestion/backpressure.d.ts.map +1 -0
- package/dist/cloud/ingestion/backpressure.js +134 -0
- package/dist/cloud/ingestion/backpressure.js.map +1 -0
- package/dist/cloud/ingestion/batch-writer.d.ts +115 -0
- package/dist/cloud/ingestion/batch-writer.d.ts.map +1 -0
- package/dist/cloud/ingestion/batch-writer.js +319 -0
- package/dist/cloud/ingestion/batch-writer.js.map +1 -0
- package/dist/cloud/ingestion/dlq-manager.d.ts +116 -0
- package/dist/cloud/ingestion/dlq-manager.d.ts.map +1 -0
- package/dist/cloud/ingestion/dlq-manager.js +244 -0
- package/dist/cloud/ingestion/dlq-manager.js.map +1 -0
- package/dist/cloud/ingestion/event-queue.d.ts +105 -0
- package/dist/cloud/ingestion/event-queue.d.ts.map +1 -0
- package/dist/cloud/ingestion/event-queue.js +185 -0
- package/dist/cloud/ingestion/event-queue.js.map +1 -0
- package/dist/cloud/ingestion/gateway.d.ts +68 -0
- package/dist/cloud/ingestion/gateway.d.ts.map +1 -0
- package/dist/cloud/ingestion/gateway.js +198 -0
- package/dist/cloud/ingestion/gateway.js.map +1 -0
- package/dist/cloud/ingestion/index.d.ts +7 -0
- package/dist/cloud/ingestion/index.d.ts.map +1 -0
- package/dist/cloud/ingestion/index.js +7 -0
- package/dist/cloud/ingestion/index.js.map +1 -0
- package/dist/cloud/ingestion/rate-limiter.d.ts +73 -0
- package/dist/cloud/ingestion/rate-limiter.d.ts.map +1 -0
- package/dist/cloud/ingestion/rate-limiter.js +153 -0
- package/dist/cloud/ingestion/rate-limiter.js.map +1 -0
- package/dist/cloud/migrate.d.ts +45 -0
- package/dist/cloud/migrate.d.ts.map +1 -0
- package/dist/cloud/migrate.js +147 -0
- package/dist/cloud/migrate.js.map +1 -0
- package/dist/cloud/migration/export-import.d.ts +56 -0
- package/dist/cloud/migration/export-import.d.ts.map +1 -0
- package/dist/cloud/migration/export-import.js +289 -0
- package/dist/cloud/migration/export-import.js.map +1 -0
- package/dist/cloud/migration/index.d.ts +5 -0
- package/dist/cloud/migration/index.d.ts.map +1 -0
- package/dist/cloud/migration/index.js +5 -0
- package/dist/cloud/migration/index.js.map +1 -0
- package/dist/cloud/org-service.d.ts +68 -0
- package/dist/cloud/org-service.d.ts.map +1 -0
- package/dist/cloud/org-service.js +169 -0
- package/dist/cloud/org-service.js.map +1 -0
- package/dist/cloud/partition-maintenance.d.ts +29 -0
- package/dist/cloud/partition-maintenance.d.ts.map +1 -0
- package/dist/cloud/partition-maintenance.js +96 -0
- package/dist/cloud/partition-maintenance.js.map +1 -0
- package/dist/cloud/retention/index.d.ts +7 -0
- package/dist/cloud/retention/index.d.ts.map +1 -0
- package/dist/cloud/retention/index.js +7 -0
- package/dist/cloud/retention/index.js.map +1 -0
- package/dist/cloud/retention/partition-management.d.ts +61 -0
- package/dist/cloud/retention/partition-management.d.ts.map +1 -0
- package/dist/cloud/retention/partition-management.js +167 -0
- package/dist/cloud/retention/partition-management.js.map +1 -0
- package/dist/cloud/retention/retention-job.d.ts +70 -0
- package/dist/cloud/retention/retention-job.d.ts.map +1 -0
- package/dist/cloud/retention/retention-job.js +160 -0
- package/dist/cloud/retention/retention-job.js.map +1 -0
- package/dist/cloud/retention/retention-policy.d.ts +27 -0
- package/dist/cloud/retention/retention-policy.d.ts.map +1 -0
- package/dist/cloud/retention/retention-policy.js +36 -0
- package/dist/cloud/retention/retention-policy.js.map +1 -0
- package/dist/cloud/routes/api-key-routes.d.ts +38 -0
- package/dist/cloud/routes/api-key-routes.d.ts.map +1 -0
- package/dist/cloud/routes/api-key-routes.js +84 -0
- package/dist/cloud/routes/api-key-routes.js.map +1 -0
- package/dist/cloud/routes/audit-routes.d.ts +36 -0
- package/dist/cloud/routes/audit-routes.d.ts.map +1 -0
- package/dist/cloud/routes/audit-routes.js +47 -0
- package/dist/cloud/routes/audit-routes.js.map +1 -0
- package/dist/cloud/routes/billing-routes.d.ts +51 -0
- package/dist/cloud/routes/billing-routes.d.ts.map +1 -0
- package/dist/cloud/routes/billing-routes.js +114 -0
- package/dist/cloud/routes/billing-routes.js.map +1 -0
- package/dist/cloud/routes/onboarding-routes.d.ts +34 -0
- package/dist/cloud/routes/onboarding-routes.d.ts.map +1 -0
- package/dist/cloud/routes/onboarding-routes.js +58 -0
- package/dist/cloud/routes/onboarding-routes.js.map +1 -0
- package/dist/cloud/routes/org-routes.d.ts +80 -0
- package/dist/cloud/routes/org-routes.d.ts.map +1 -0
- package/dist/cloud/routes/org-routes.js +153 -0
- package/dist/cloud/routes/org-routes.js.map +1 -0
- package/dist/cloud/routes/usage-routes.d.ts +18 -0
- package/dist/cloud/routes/usage-routes.d.ts.map +1 -0
- package/dist/cloud/routes/usage-routes.js +66 -0
- package/dist/cloud/routes/usage-routes.js.map +1 -0
- package/dist/cloud/storage/adapter.d.ts +102 -0
- package/dist/cloud/storage/adapter.d.ts.map +1 -0
- package/dist/cloud/storage/adapter.js +21 -0
- package/dist/cloud/storage/adapter.js.map +1 -0
- package/dist/cloud/storage/index.d.ts +8 -0
- package/dist/cloud/storage/index.d.ts.map +1 -0
- package/dist/cloud/storage/index.js +7 -0
- package/dist/cloud/storage/index.js.map +1 -0
- package/dist/cloud/storage/postgres-adapter.d.ts +34 -0
- package/dist/cloud/storage/postgres-adapter.d.ts.map +1 -0
- package/dist/cloud/storage/postgres-adapter.js +544 -0
- package/dist/cloud/storage/postgres-adapter.js.map +1 -0
- package/dist/cloud/storage/sqlite-adapter.d.ts +29 -0
- package/dist/cloud/storage/sqlite-adapter.d.ts.map +1 -0
- package/dist/cloud/storage/sqlite-adapter.js +176 -0
- package/dist/cloud/storage/sqlite-adapter.js.map +1 -0
- package/dist/cloud/tenant-pool.d.ts +49 -0
- package/dist/cloud/tenant-pool.d.ts.map +1 -0
- package/dist/cloud/tenant-pool.js +61 -0
- package/dist/cloud/tenant-pool.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Service — orchestrates user creation, login, OAuth, and sessions.
|
|
3
|
+
*
|
|
4
|
+
* Uses MigrationClient (pg Pool) for database operations.
|
|
5
|
+
* In-memory brute-force protection (replaceable with Redis in prod).
|
|
6
|
+
*/
|
|
7
|
+
import type { MigrationClient } from '../migrate.js';
|
|
8
|
+
import { BruteForceProtection } from './brute-force.js';
|
|
9
|
+
import type { OAuthUserProfile } from './oauth.js';
|
|
10
|
+
export interface AuthServiceConfig {
|
|
11
|
+
jwtSecret: string;
|
|
12
|
+
jwtExpiresInSeconds?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface AuthUser {
|
|
15
|
+
id: string;
|
|
16
|
+
email: string;
|
|
17
|
+
email_verified: boolean;
|
|
18
|
+
password_hash: string | null;
|
|
19
|
+
display_name: string | null;
|
|
20
|
+
avatar_url: string | null;
|
|
21
|
+
oauth_provider: string | null;
|
|
22
|
+
oauth_provider_id: string | null;
|
|
23
|
+
}
|
|
24
|
+
export interface AuthResult {
|
|
25
|
+
token: string;
|
|
26
|
+
user: {
|
|
27
|
+
id: string;
|
|
28
|
+
email: string;
|
|
29
|
+
name: string | null;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export declare class AuthService {
|
|
33
|
+
private db;
|
|
34
|
+
private config;
|
|
35
|
+
private bruteForce;
|
|
36
|
+
constructor(db: MigrationClient, config: AuthServiceConfig, bruteForce?: BruteForceProtection);
|
|
37
|
+
/**
|
|
38
|
+
* Handle OAuth callback: find or create user, return JWT.
|
|
39
|
+
*/
|
|
40
|
+
oauthLogin(profile: OAuthUserProfile): Promise<AuthResult>;
|
|
41
|
+
private createOAuthUser;
|
|
42
|
+
/**
|
|
43
|
+
* Register a new user with email and password.
|
|
44
|
+
* Returns verification token (to be sent via email).
|
|
45
|
+
*/
|
|
46
|
+
register(email: string, password: string, displayName?: string): Promise<{
|
|
47
|
+
user: AuthUser;
|
|
48
|
+
verificationToken: string;
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Verify email address with token.
|
|
52
|
+
*/
|
|
53
|
+
verifyEmail(token: string): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Login with email and password.
|
|
56
|
+
*/
|
|
57
|
+
login(email: string, password: string): Promise<AuthResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Request password reset. Returns token to be sent via email.
|
|
60
|
+
* Always succeeds (even if email not found) to prevent enumeration.
|
|
61
|
+
*/
|
|
62
|
+
requestPasswordReset(email: string): Promise<string | null>;
|
|
63
|
+
/**
|
|
64
|
+
* Reset password using token.
|
|
65
|
+
*/
|
|
66
|
+
resetPassword(token: string, newPassword: string): Promise<boolean>;
|
|
67
|
+
private issueToken;
|
|
68
|
+
private findUserByEmail;
|
|
69
|
+
private findUserById;
|
|
70
|
+
private findUserByOAuth;
|
|
71
|
+
private createDefaultOrg;
|
|
72
|
+
}
|
|
73
|
+
export declare class AuthError extends Error {
|
|
74
|
+
code: string;
|
|
75
|
+
constructor(code: string, message: string);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=auth-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../../../src/cloud/auth/auth-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAIrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC1D;AAED,qBAAa,WAAW;IAEpB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,UAAU;gBAFV,EAAE,EAAE,eAAe,EACnB,MAAM,EAAE,iBAAiB,EACzB,UAAU,uBAA6B;IAOjD;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;YA6BlD,eAAe;IAmB7B;;;OAGG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC;IAqCzD;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBlD;;OAEG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAmCjE;;;OAGG;IACG,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAsBjE;;OAEG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAkC3D,UAAU;YAsBV,eAAe;YAKf,YAAY;YAKZ,eAAe;YAQf,gBAAgB;CAsB/B;AAED,qBAAa,SAAU,SAAQ,KAAK;IAEzB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM;CAKlB"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Service — orchestrates user creation, login, OAuth, and sessions.
|
|
3
|
+
*
|
|
4
|
+
* Uses MigrationClient (pg Pool) for database operations.
|
|
5
|
+
* In-memory brute-force protection (replaceable with Redis in prod).
|
|
6
|
+
*/
|
|
7
|
+
import { hashPassword, verifyPassword, validatePasswordComplexity } from './passwords.js';
|
|
8
|
+
import { signJwt, verifyJwt } from './jwt.js';
|
|
9
|
+
import { generateToken, hashToken, verifyToken } from './tokens.js';
|
|
10
|
+
import { BruteForceProtection } from './brute-force.js';
|
|
11
|
+
export class AuthService {
|
|
12
|
+
db;
|
|
13
|
+
config;
|
|
14
|
+
bruteForce;
|
|
15
|
+
constructor(db, config, bruteForce = new BruteForceProtection()) {
|
|
16
|
+
this.db = db;
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.bruteForce = bruteForce;
|
|
19
|
+
}
|
|
20
|
+
// ═══════════════════════════════════════════
|
|
21
|
+
// OAuth Login / Registration
|
|
22
|
+
// ═══════════════════════════════════════════
|
|
23
|
+
/**
|
|
24
|
+
* Handle OAuth callback: find or create user, return JWT.
|
|
25
|
+
*/
|
|
26
|
+
async oauthLogin(profile) {
|
|
27
|
+
// 1. Try to find by OAuth provider + ID
|
|
28
|
+
let user = await this.findUserByOAuth(profile.provider, profile.providerId);
|
|
29
|
+
if (!user) {
|
|
30
|
+
// 2. Try to find by email (link OAuth to existing account)
|
|
31
|
+
user = await this.findUserByEmail(profile.email);
|
|
32
|
+
if (user) {
|
|
33
|
+
// Link OAuth to existing user
|
|
34
|
+
await this.db.query(`UPDATE users SET oauth_provider = $1, oauth_provider_id = $2,
|
|
35
|
+
email_verified = TRUE, avatar_url = COALESCE(avatar_url, $3),
|
|
36
|
+
display_name = COALESCE(display_name, $4), updated_at = now()
|
|
37
|
+
WHERE id = $5`, [profile.provider, profile.providerId, profile.avatarUrl, profile.name, user.id]);
|
|
38
|
+
user.email_verified = true;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// 3. Create new user
|
|
42
|
+
user = await this.createOAuthUser(profile);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Issue JWT
|
|
46
|
+
const token = await this.issueToken(user);
|
|
47
|
+
return { token, user: { id: user.id, email: user.email, name: user.display_name } };
|
|
48
|
+
}
|
|
49
|
+
async createOAuthUser(profile) {
|
|
50
|
+
// Create user
|
|
51
|
+
const result = await this.db.query(`INSERT INTO users (email, email_verified, display_name, avatar_url, oauth_provider, oauth_provider_id)
|
|
52
|
+
VALUES ($1, TRUE, $2, $3, $4, $5)
|
|
53
|
+
RETURNING *`, [profile.email, profile.name, profile.avatarUrl, profile.provider, profile.providerId]);
|
|
54
|
+
const user = result.rows[0];
|
|
55
|
+
// Create default personal org
|
|
56
|
+
await this.createDefaultOrg(user);
|
|
57
|
+
return user;
|
|
58
|
+
}
|
|
59
|
+
// ═══════════════════════════════════════════
|
|
60
|
+
// Email/Password Registration
|
|
61
|
+
// ═══════════════════════════════════════════
|
|
62
|
+
/**
|
|
63
|
+
* Register a new user with email and password.
|
|
64
|
+
* Returns verification token (to be sent via email).
|
|
65
|
+
*/
|
|
66
|
+
async register(email, password, displayName) {
|
|
67
|
+
// Validate password
|
|
68
|
+
const complexity = validatePasswordComplexity(password);
|
|
69
|
+
if (!complexity.valid) {
|
|
70
|
+
throw new AuthError('invalid_password', complexity.errors.join('; '));
|
|
71
|
+
}
|
|
72
|
+
// Check if email already exists
|
|
73
|
+
const existing = await this.findUserByEmail(email);
|
|
74
|
+
if (existing) {
|
|
75
|
+
throw new AuthError('email_exists', 'An account with this email already exists');
|
|
76
|
+
}
|
|
77
|
+
// Hash password & create user
|
|
78
|
+
const passwordHash = await hashPassword(password);
|
|
79
|
+
const verificationToken = generateToken();
|
|
80
|
+
const tokenHash = hashToken(verificationToken);
|
|
81
|
+
const result = await this.db.query(`INSERT INTO users (email, email_verified, password_hash, display_name)
|
|
82
|
+
VALUES ($1, FALSE, $2, $3)
|
|
83
|
+
RETURNING *`, [email, passwordHash, displayName ?? null]);
|
|
84
|
+
const user = result.rows[0];
|
|
85
|
+
// Store verification token (using a simple approach: store in users table or a tokens table)
|
|
86
|
+
// For simplicity, we'll use the email_verification_token approach
|
|
87
|
+
await this.db.query(`INSERT INTO _email_tokens (user_id, token_hash, type, expires_at)
|
|
88
|
+
VALUES ($1, $2, 'verification', now() + interval '24 hours')`, [user.id, tokenHash]);
|
|
89
|
+
return { user, verificationToken };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Verify email address with token.
|
|
93
|
+
*/
|
|
94
|
+
async verifyEmail(token) {
|
|
95
|
+
const tokenHash = hashToken(token);
|
|
96
|
+
const result = await this.db.query(`SELECT user_id FROM _email_tokens
|
|
97
|
+
WHERE token_hash = $1 AND type = 'verification' AND expires_at > now()`, [tokenHash]);
|
|
98
|
+
if (result.rows.length === 0)
|
|
99
|
+
return false;
|
|
100
|
+
const userId = result.rows[0].user_id;
|
|
101
|
+
await this.db.query(`UPDATE users SET email_verified = TRUE, updated_at = now() WHERE id = $1`, [userId]);
|
|
102
|
+
await this.db.query(`DELETE FROM _email_tokens WHERE token_hash = $1`, [tokenHash]);
|
|
103
|
+
// Create default org after verification
|
|
104
|
+
const user = await this.findUserById(userId);
|
|
105
|
+
if (user)
|
|
106
|
+
await this.createDefaultOrg(user);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
// ═══════════════════════════════════════════
|
|
110
|
+
// Email/Password Login
|
|
111
|
+
// ═══════════════════════════════════════════
|
|
112
|
+
/**
|
|
113
|
+
* Login with email and password.
|
|
114
|
+
*/
|
|
115
|
+
async login(email, password) {
|
|
116
|
+
// Check brute-force lock
|
|
117
|
+
if (this.bruteForce.isLocked(email)) {
|
|
118
|
+
throw new AuthError('account_locked', 'Account temporarily locked due to too many failed attempts');
|
|
119
|
+
}
|
|
120
|
+
const user = await this.findUserByEmail(email);
|
|
121
|
+
if (!user || !user.password_hash) {
|
|
122
|
+
this.bruteForce.recordFailure(email);
|
|
123
|
+
throw new AuthError('invalid_credentials', 'Invalid email or password');
|
|
124
|
+
}
|
|
125
|
+
if (!user.email_verified) {
|
|
126
|
+
throw new AuthError('email_not_verified', 'Please verify your email before logging in');
|
|
127
|
+
}
|
|
128
|
+
const valid = await verifyPassword(password, user.password_hash);
|
|
129
|
+
if (!valid) {
|
|
130
|
+
const locked = this.bruteForce.recordFailure(email);
|
|
131
|
+
if (locked) {
|
|
132
|
+
throw new AuthError('account_locked', 'Account temporarily locked due to too many failed attempts');
|
|
133
|
+
}
|
|
134
|
+
throw new AuthError('invalid_credentials', 'Invalid email or password');
|
|
135
|
+
}
|
|
136
|
+
this.bruteForce.recordSuccess(email);
|
|
137
|
+
const token = await this.issueToken(user);
|
|
138
|
+
return { token, user: { id: user.id, email: user.email, name: user.display_name } };
|
|
139
|
+
}
|
|
140
|
+
// ═══════════════════════════════════════════
|
|
141
|
+
// Password Reset
|
|
142
|
+
// ═══════════════════════════════════════════
|
|
143
|
+
/**
|
|
144
|
+
* Request password reset. Returns token to be sent via email.
|
|
145
|
+
* Always succeeds (even if email not found) to prevent enumeration.
|
|
146
|
+
*/
|
|
147
|
+
async requestPasswordReset(email) {
|
|
148
|
+
const user = await this.findUserByEmail(email);
|
|
149
|
+
if (!user)
|
|
150
|
+
return null; // Don't reveal whether email exists
|
|
151
|
+
const token = generateToken();
|
|
152
|
+
const tokenHash = hashToken(token);
|
|
153
|
+
// Delete old reset tokens for this user
|
|
154
|
+
await this.db.query(`DELETE FROM _email_tokens WHERE user_id = $1 AND type = 'reset'`, [user.id]);
|
|
155
|
+
await this.db.query(`INSERT INTO _email_tokens (user_id, token_hash, type, expires_at)
|
|
156
|
+
VALUES ($1, $2, 'reset', now() + interval '1 hour')`, [user.id, tokenHash]);
|
|
157
|
+
return token;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Reset password using token.
|
|
161
|
+
*/
|
|
162
|
+
async resetPassword(token, newPassword) {
|
|
163
|
+
const complexity = validatePasswordComplexity(newPassword);
|
|
164
|
+
if (!complexity.valid) {
|
|
165
|
+
throw new AuthError('invalid_password', complexity.errors.join('; '));
|
|
166
|
+
}
|
|
167
|
+
const tokenHash = hashToken(token);
|
|
168
|
+
const result = await this.db.query(`SELECT user_id FROM _email_tokens
|
|
169
|
+
WHERE token_hash = $1 AND type = 'reset' AND expires_at > now()`, [tokenHash]);
|
|
170
|
+
if (result.rows.length === 0)
|
|
171
|
+
return false;
|
|
172
|
+
const userId = result.rows[0].user_id;
|
|
173
|
+
const passwordHash = await hashPassword(newPassword);
|
|
174
|
+
await this.db.query(`UPDATE users SET password_hash = $1, updated_at = now() WHERE id = $2`, [passwordHash, userId]);
|
|
175
|
+
await this.db.query(`DELETE FROM _email_tokens WHERE token_hash = $1`, [tokenHash]);
|
|
176
|
+
// Clear brute-force records for this user's email
|
|
177
|
+
const user = await this.findUserById(userId);
|
|
178
|
+
if (user)
|
|
179
|
+
this.bruteForce.recordSuccess(user.email);
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
// ═══════════════════════════════════════════
|
|
183
|
+
// Token Issuance
|
|
184
|
+
// ═══════════════════════════════════════════
|
|
185
|
+
async issueToken(user) {
|
|
186
|
+
// Fetch user's org memberships
|
|
187
|
+
const orgsResult = await this.db.query(`SELECT om.org_id, om.role FROM org_members om WHERE om.user_id = $1`, [user.id]);
|
|
188
|
+
const orgs = orgsResult.rows.map((r) => ({
|
|
189
|
+
org_id: r.org_id,
|
|
190
|
+
role: r.role,
|
|
191
|
+
}));
|
|
192
|
+
return signJwt({ sub: user.id, email: user.email, name: user.display_name, orgs }, this.config.jwtSecret, this.config.jwtExpiresInSeconds ?? 7 * 24 * 3600);
|
|
193
|
+
}
|
|
194
|
+
// ═══════════════════════════════════════════
|
|
195
|
+
// Helpers
|
|
196
|
+
// ═══════════════════════════════════════════
|
|
197
|
+
async findUserByEmail(email) {
|
|
198
|
+
const result = await this.db.query(`SELECT * FROM users WHERE email = $1`, [email]);
|
|
199
|
+
return result.rows[0] ?? null;
|
|
200
|
+
}
|
|
201
|
+
async findUserById(id) {
|
|
202
|
+
const result = await this.db.query(`SELECT * FROM users WHERE id = $1`, [id]);
|
|
203
|
+
return result.rows[0] ?? null;
|
|
204
|
+
}
|
|
205
|
+
async findUserByOAuth(provider, providerId) {
|
|
206
|
+
const result = await this.db.query(`SELECT * FROM users WHERE oauth_provider = $1 AND oauth_provider_id = $2`, [provider, providerId]);
|
|
207
|
+
return result.rows[0] ?? null;
|
|
208
|
+
}
|
|
209
|
+
async createDefaultOrg(user) {
|
|
210
|
+
// Check if user already has an org
|
|
211
|
+
const existing = await this.db.query(`SELECT 1 FROM org_members WHERE user_id = $1 LIMIT 1`, [user.id]);
|
|
212
|
+
if (existing.rows.length > 0)
|
|
213
|
+
return;
|
|
214
|
+
const displayName = user.display_name || user.email.split('@')[0];
|
|
215
|
+
const slug = `${displayName.toLowerCase().replace(/[^a-z0-9]/g, '-')}-${user.id.slice(0, 8)}`;
|
|
216
|
+
const orgResult = await this.db.query(`INSERT INTO orgs (name, slug) VALUES ($1, $2) RETURNING id`, [`${displayName}'s Org`, slug]);
|
|
217
|
+
const orgId = orgResult.rows[0].id;
|
|
218
|
+
await this.db.query(`INSERT INTO org_members (org_id, user_id, role) VALUES ($1, $2, 'owner')`, [orgId, user.id]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
export class AuthError extends Error {
|
|
222
|
+
code;
|
|
223
|
+
constructor(code, message) {
|
|
224
|
+
super(message);
|
|
225
|
+
this.code = code;
|
|
226
|
+
this.name = 'AuthError';
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=auth-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-service.js","sourceRoot":"","sources":["../../../src/cloud/auth/auth-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brute-force protection: in-memory rate limiter.
|
|
3
|
+
* Locks account after 10 failed attempts in 15 minutes.
|
|
4
|
+
*
|
|
5
|
+
* In production, this would use Redis. For now, in-memory is sufficient
|
|
6
|
+
* for single-instance deployments and testing.
|
|
7
|
+
*/
|
|
8
|
+
export interface BruteForceConfig {
|
|
9
|
+
maxAttempts: number;
|
|
10
|
+
windowMs: number;
|
|
11
|
+
lockDurationMs: number;
|
|
12
|
+
}
|
|
13
|
+
export declare class BruteForceProtection {
|
|
14
|
+
private records;
|
|
15
|
+
private config;
|
|
16
|
+
constructor(config?: Partial<BruteForceConfig>);
|
|
17
|
+
/**
|
|
18
|
+
* Check if a key (email) is currently locked.
|
|
19
|
+
*/
|
|
20
|
+
isLocked(key: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Record a failed attempt. Returns true if account is now locked.
|
|
23
|
+
*/
|
|
24
|
+
recordFailure(key: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Clear attempts on successful login.
|
|
27
|
+
*/
|
|
28
|
+
recordSuccess(key: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Reset all records (for testing).
|
|
31
|
+
*/
|
|
32
|
+
reset(): void;
|
|
33
|
+
}
|
|
34
|
+
/** Singleton instance */
|
|
35
|
+
export declare const bruteForce: BruteForceProtection;
|
|
36
|
+
//# sourceMappingURL=brute-force.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brute-force.d.ts","sourceRoot":"","sources":["../../../src/cloud/auth/brute-force.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAaD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,MAAM,CAAmB;gBAErB,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM;IAIlD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAW9B;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAmBnC;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIhC;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED,yBAAyB;AACzB,eAAO,MAAM,UAAU,sBAA6B,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brute-force protection: in-memory rate limiter.
|
|
3
|
+
* Locks account after 10 failed attempts in 15 minutes.
|
|
4
|
+
*
|
|
5
|
+
* In production, this would use Redis. For now, in-memory is sufficient
|
|
6
|
+
* for single-instance deployments and testing.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_CONFIG = {
|
|
9
|
+
maxAttempts: 10,
|
|
10
|
+
windowMs: 15 * 60 * 1000,
|
|
11
|
+
lockDurationMs: 15 * 60 * 1000,
|
|
12
|
+
};
|
|
13
|
+
export class BruteForceProtection {
|
|
14
|
+
records = new Map();
|
|
15
|
+
config;
|
|
16
|
+
constructor(config = {}) {
|
|
17
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if a key (email) is currently locked.
|
|
21
|
+
*/
|
|
22
|
+
isLocked(key) {
|
|
23
|
+
const record = this.records.get(key);
|
|
24
|
+
if (!record?.lockedUntil)
|
|
25
|
+
return false;
|
|
26
|
+
if (Date.now() >= record.lockedUntil) {
|
|
27
|
+
// Lock expired, clear
|
|
28
|
+
this.records.delete(key);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Record a failed attempt. Returns true if account is now locked.
|
|
35
|
+
*/
|
|
36
|
+
recordFailure(key) {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
let record = this.records.get(key);
|
|
39
|
+
if (!record) {
|
|
40
|
+
record = { attempts: [], lockedUntil: null };
|
|
41
|
+
this.records.set(key, record);
|
|
42
|
+
}
|
|
43
|
+
// Prune old attempts outside the window
|
|
44
|
+
record.attempts = record.attempts.filter((t) => now - t < this.config.windowMs);
|
|
45
|
+
record.attempts.push(now);
|
|
46
|
+
if (record.attempts.length >= this.config.maxAttempts) {
|
|
47
|
+
record.lockedUntil = now + this.config.lockDurationMs;
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Clear attempts on successful login.
|
|
54
|
+
*/
|
|
55
|
+
recordSuccess(key) {
|
|
56
|
+
this.records.delete(key);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Reset all records (for testing).
|
|
60
|
+
*/
|
|
61
|
+
reset() {
|
|
62
|
+
this.records.clear();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Singleton instance */
|
|
66
|
+
export const bruteForce = new BruteForceProtection();
|
|
67
|
+
//# sourceMappingURL=brute-force.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brute-force.js","sourceRoot":"","sources":["../../../src/cloud/auth/brute-force.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,cAAc,GAAqB;IACvC,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IACxB,cAAc,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;CAC/B,CAAC;AAEF,MAAM,OAAO,oBAAoB;IACvB,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC3C,MAAM,CAAmB;IAEjC,YAAY,SAAoC,EAAE;QAChD,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IACjD,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,GAAW;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,EAAE,WAAW;YAAE,OAAO,KAAK,CAAC;QACvC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACrC,sBAAsB;YACtB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,GAAW;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YAC7C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC;QAED,wCAAwC;QACxC,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChF,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE1B,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,CAAC,WAAW,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;YACtD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,GAAW;QACvB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF;AAED,yBAAyB;AACzB,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { AuthService, AuthError, type AuthServiceConfig, type AuthResult, type AuthUser } from './auth-service.js';
|
|
2
|
+
export { signJwt, verifyJwt, JWT_COOKIE_OPTIONS, type JwtPayload } from './jwt.js';
|
|
3
|
+
export { hashPassword, verifyPassword, validatePasswordComplexity } from './passwords.js';
|
|
4
|
+
export { generateToken, hashToken, verifyToken } from './tokens.js';
|
|
5
|
+
export { BruteForceProtection } from './brute-force.js';
|
|
6
|
+
export { ApiKeyService, ApiKeyError, generateApiKey, type ApiKeyRecord, type CreateApiKeyInput, type CreateApiKeyResult, type ApiKeyEnvironment, } from './api-keys.js';
|
|
7
|
+
export { ApiKeyAuthMiddleware, ApiKeyAuthError, InMemoryApiKeyCache, type ApiKeyAuthContext, type ApiKeyCache, type CacheEntry, } from './api-key-middleware.js';
|
|
8
|
+
export { AuditLogService, type AuditAction, type ActorType, type AuditResult, type AuditEntry, type WriteAuditEntry, type AuditQueryFilters, } from './audit-log.js';
|
|
9
|
+
export { requireRole, requireActionCategory, isRoleAllowed, categorizeAction, PERMISSION_MATRIX, type Role, type ActionCategory, type RbacRequest, type RbacResult, } from './rbac.js';
|
|
10
|
+
export { type OAuthConfig, type OAuthProviderConfig, type OAuthUserProfile, getGoogleAuthUrl, exchangeGoogleCode, getGoogleProfile, getGithubAuthUrl, exchangeGithubCode, getGithubProfile, } from './oauth.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cloud/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACnH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EACL,aAAa,EACb,WAAW,EACX,cAAc,EACd,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,IAAI,EACT,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { AuthService, AuthError } from './auth-service.js';
|
|
2
|
+
export { signJwt, verifyJwt, JWT_COOKIE_OPTIONS } from './jwt.js';
|
|
3
|
+
export { hashPassword, verifyPassword, validatePasswordComplexity } from './passwords.js';
|
|
4
|
+
export { generateToken, hashToken, verifyToken } from './tokens.js';
|
|
5
|
+
export { BruteForceProtection } from './brute-force.js';
|
|
6
|
+
export { ApiKeyService, ApiKeyError, generateApiKey, } from './api-keys.js';
|
|
7
|
+
export { ApiKeyAuthMiddleware, ApiKeyAuthError, InMemoryApiKeyCache, } from './api-key-middleware.js';
|
|
8
|
+
export { AuditLogService, } from './audit-log.js';
|
|
9
|
+
export { requireRole, requireActionCategory, isRoleAllowed, categorizeAction, PERMISSION_MATRIX, } from './rbac.js';
|
|
10
|
+
export { getGoogleAuthUrl, exchangeGoogleCode, getGoogleProfile, getGithubAuthUrl, exchangeGithubCode, getGithubProfile, } from './oauth.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cloud/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAA0D,MAAM,mBAAmB,CAAC;AACnH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAmB,MAAM,UAAU,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EACL,aAAa,EACb,WAAW,EACX,cAAc,GAKf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,mBAAmB,GAIpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,eAAe,GAOhB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAKlB,MAAM,WAAW,CAAC;AACnB,OAAO,EAIL,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT utilities using Node.js built-in crypto (HMAC-SHA256).
|
|
3
|
+
* No external dependencies.
|
|
4
|
+
*/
|
|
5
|
+
export interface JwtPayload {
|
|
6
|
+
sub: string;
|
|
7
|
+
email: string;
|
|
8
|
+
name: string | null;
|
|
9
|
+
orgs: Array<{
|
|
10
|
+
org_id: string;
|
|
11
|
+
role: string;
|
|
12
|
+
}>;
|
|
13
|
+
iat: number;
|
|
14
|
+
exp: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Sign a JWT payload. Returns a compact JWT string.
|
|
18
|
+
*/
|
|
19
|
+
export declare function signJwt(payload: Omit<JwtPayload, 'iat' | 'exp'>, secret: string, expiresInSeconds?: number): string;
|
|
20
|
+
/**
|
|
21
|
+
* Verify and decode a JWT. Returns null if invalid or expired.
|
|
22
|
+
*/
|
|
23
|
+
export declare function verifyJwt(token: string, secret: string): JwtPayload | null;
|
|
24
|
+
/**
|
|
25
|
+
* Cookie options for JWT storage.
|
|
26
|
+
*/
|
|
27
|
+
export declare const JWT_COOKIE_OPTIONS: {
|
|
28
|
+
httpOnly: boolean;
|
|
29
|
+
secure: boolean;
|
|
30
|
+
sameSite: "Strict";
|
|
31
|
+
path: string;
|
|
32
|
+
maxAge: number;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../../src/cloud/auth/jwt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAaD;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,SAAgB,GAAG,MAAM,CAe1H;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAwB1E;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;CAM9B,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT utilities using Node.js built-in crypto (HMAC-SHA256).
|
|
3
|
+
* No external dependencies.
|
|
4
|
+
*/
|
|
5
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
6
|
+
const ALG = 'HS256';
|
|
7
|
+
function base64url(data) {
|
|
8
|
+
const buf = typeof data === 'string' ? Buffer.from(data, 'utf-8') : data;
|
|
9
|
+
return buf.toString('base64url');
|
|
10
|
+
}
|
|
11
|
+
function base64urlDecode(str) {
|
|
12
|
+
return Buffer.from(str, 'base64url').toString('utf-8');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Sign a JWT payload. Returns a compact JWT string.
|
|
16
|
+
*/
|
|
17
|
+
export function signJwt(payload, secret, expiresInSeconds = 7 * 24 * 3600) {
|
|
18
|
+
const now = Math.floor(Date.now() / 1000);
|
|
19
|
+
const fullPayload = {
|
|
20
|
+
...payload,
|
|
21
|
+
iat: now,
|
|
22
|
+
exp: now + expiresInSeconds,
|
|
23
|
+
};
|
|
24
|
+
const header = base64url(JSON.stringify({ alg: ALG, typ: 'JWT' }));
|
|
25
|
+
const body = base64url(JSON.stringify(fullPayload));
|
|
26
|
+
const signature = createHmac('sha256', secret)
|
|
27
|
+
.update(`${header}.${body}`)
|
|
28
|
+
.digest('base64url');
|
|
29
|
+
return `${header}.${body}.${signature}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Verify and decode a JWT. Returns null if invalid or expired.
|
|
33
|
+
*/
|
|
34
|
+
export function verifyJwt(token, secret) {
|
|
35
|
+
try {
|
|
36
|
+
const parts = token.split('.');
|
|
37
|
+
if (parts.length !== 3)
|
|
38
|
+
return null;
|
|
39
|
+
const [header, body, signature] = parts;
|
|
40
|
+
const expectedSig = createHmac('sha256', secret)
|
|
41
|
+
.update(`${header}.${body}`)
|
|
42
|
+
.digest('base64url');
|
|
43
|
+
const sigBuf = Buffer.from(signature, 'base64url');
|
|
44
|
+
const expectedBuf = Buffer.from(expectedSig, 'base64url');
|
|
45
|
+
if (sigBuf.length !== expectedBuf.length || !timingSafeEqual(sigBuf, expectedBuf))
|
|
46
|
+
return null;
|
|
47
|
+
const payload = JSON.parse(base64urlDecode(body));
|
|
48
|
+
// Check expiry
|
|
49
|
+
const now = Math.floor(Date.now() / 1000);
|
|
50
|
+
if (payload.exp && payload.exp < now)
|
|
51
|
+
return null;
|
|
52
|
+
return payload;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Cookie options for JWT storage.
|
|
60
|
+
*/
|
|
61
|
+
export const JWT_COOKIE_OPTIONS = {
|
|
62
|
+
httpOnly: true,
|
|
63
|
+
secure: true,
|
|
64
|
+
sameSite: 'Strict',
|
|
65
|
+
path: '/',
|
|
66
|
+
maxAge: 7 * 24 * 3600, // 7 days in seconds
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=jwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../../src/cloud/auth/jwt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth provider integration (Google + GitHub).
|
|
3
|
+
*
|
|
4
|
+
* This module handles:
|
|
5
|
+
* 1. Generating OAuth authorization URLs
|
|
6
|
+
* 2. Exchanging authorization codes for tokens
|
|
7
|
+
* 3. Fetching user profile from OAuth providers
|
|
8
|
+
* 4. Creating/linking user records on first login
|
|
9
|
+
* 5. Issuing JWT session cookies
|
|
10
|
+
*/
|
|
11
|
+
export interface OAuthProviderConfig {
|
|
12
|
+
clientId: string;
|
|
13
|
+
clientSecret: string;
|
|
14
|
+
redirectUri: string;
|
|
15
|
+
}
|
|
16
|
+
export interface OAuthUserProfile {
|
|
17
|
+
provider: 'google' | 'github';
|
|
18
|
+
providerId: string;
|
|
19
|
+
email: string;
|
|
20
|
+
name: string | null;
|
|
21
|
+
avatarUrl: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface OAuthConfig {
|
|
24
|
+
google?: OAuthProviderConfig;
|
|
25
|
+
github?: OAuthProviderConfig;
|
|
26
|
+
}
|
|
27
|
+
export declare function getGoogleAuthUrl(config: OAuthProviderConfig, state: string): string;
|
|
28
|
+
export declare function exchangeGoogleCode(config: OAuthProviderConfig, code: string): Promise<{
|
|
29
|
+
accessToken: string;
|
|
30
|
+
}>;
|
|
31
|
+
export declare function getGoogleProfile(accessToken: string): Promise<OAuthUserProfile>;
|
|
32
|
+
export declare function getGithubAuthUrl(config: OAuthProviderConfig, state: string): string;
|
|
33
|
+
export declare function exchangeGithubCode(config: OAuthProviderConfig, code: string): Promise<{
|
|
34
|
+
accessToken: string;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function getGithubProfile(accessToken: string): Promise<OAuthUserProfile>;
|
|
37
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../../src/cloud/auth/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,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,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUnF;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAelC;AAED,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAarF;AAWD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAQnF;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBlC;AAED,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwBrF"}
|