clearauth 0.3.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/CHANGELOG.md +235 -0
- package/LICENSE +21 -0
- package/README.md +417 -0
- package/dist/auth/handler.d.ts +38 -0
- package/dist/auth/handler.js +483 -0
- package/dist/auth/handler.js.map +1 -0
- package/dist/auth/login.d.ts +69 -0
- package/dist/auth/login.js +103 -0
- package/dist/auth/login.js.map +1 -0
- package/dist/auth/register.d.ts +72 -0
- package/dist/auth/register.js +122 -0
- package/dist/auth/register.js.map +1 -0
- package/dist/auth/reset-password.d.ts +106 -0
- package/dist/auth/reset-password.js +213 -0
- package/dist/auth/reset-password.js.map +1 -0
- package/dist/auth/utils.d.ts +58 -0
- package/dist/auth/utils.js +121 -0
- package/dist/auth/utils.js.map +1 -0
- package/dist/auth/verify-email.d.ts +70 -0
- package/dist/auth/verify-email.js +137 -0
- package/dist/auth/verify-email.js.map +1 -0
- package/dist/createMechAuth.d.ts +178 -0
- package/dist/createMechAuth.js +215 -0
- package/dist/createMechAuth.js.map +1 -0
- package/dist/database/schema.d.ts +135 -0
- package/dist/database/schema.js +37 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/edge.d.ts +4 -0
- package/dist/edge.js +6 -0
- package/dist/edge.js.map +1 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.js +44 -0
- package/dist/errors.js.map +1 -0
- package/dist/handler.d.ts +100 -0
- package/dist/handler.js +213 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +22 -0
- package/dist/logger.js +40 -0
- package/dist/logger.js.map +1 -0
- package/dist/mech-kysely.d.ts +22 -0
- package/dist/mech-kysely.js +88 -0
- package/dist/mech-kysely.js.map +1 -0
- package/dist/mech-sql-client.d.ts +85 -0
- package/dist/mech-sql-client.js +155 -0
- package/dist/mech-sql-client.js.map +1 -0
- package/dist/node.d.ts +4 -0
- package/dist/node.js +10 -0
- package/dist/node.js.map +1 -0
- package/dist/oauth/arctic-providers.d.ts +60 -0
- package/dist/oauth/arctic-providers.js +94 -0
- package/dist/oauth/arctic-providers.js.map +1 -0
- package/dist/oauth/callbacks.d.ts +155 -0
- package/dist/oauth/callbacks.js +286 -0
- package/dist/oauth/callbacks.js.map +1 -0
- package/dist/oauth/github.d.ts +47 -0
- package/dist/oauth/github.js +136 -0
- package/dist/oauth/github.js.map +1 -0
- package/dist/oauth/google.d.ts +49 -0
- package/dist/oauth/google.js +104 -0
- package/dist/oauth/google.js.map +1 -0
- package/dist/oauth/handler.d.ts +31 -0
- package/dist/oauth/handler.js +277 -0
- package/dist/oauth/handler.js.map +1 -0
- package/dist/password-hasher-argon2.d.ts +7 -0
- package/dist/password-hasher-argon2.js +16 -0
- package/dist/password-hasher-argon2.js.map +1 -0
- package/dist/password-hasher.d.ts +12 -0
- package/dist/password-hasher.js +115 -0
- package/dist/password-hasher.js.map +1 -0
- package/dist/react.d.ts +152 -0
- package/dist/react.js +296 -0
- package/dist/react.js.map +1 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/cors.d.ts +65 -0
- package/dist/utils/cors.js +152 -0
- package/dist/utils/cors.js.map +1 -0
- package/dist/utils/normalize-auth-path.d.ts +1 -0
- package/dist/utils/normalize-auth-path.js +8 -0
- package/dist/utils/normalize-auth-path.js.map +1 -0
- package/dist/validation.d.ts +23 -0
- package/dist/validation.js +70 -0
- package/dist/validation.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { ClearAuthSqlError, ClearAuthNetworkError, ClearAuthTimeoutError, ClearAuthRateLimitError, } from "./errors.js";
|
|
2
|
+
import { getDefaultLogger } from "./logger.js";
|
|
3
|
+
import { validateUrl, validateUuid, validateNonEmpty, validatePositive, validateRange } from "./validation.js";
|
|
4
|
+
/**
|
|
5
|
+
* HTTP client for Mech Storage PostgreSQL API
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const client = new MechSqlClient()
|
|
10
|
+
* const { rows } = await client.execute("SELECT * FROM users WHERE id = $1", [123])
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export class MechSqlClient {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
// Resolve values with smart defaults
|
|
16
|
+
this.baseUrl = config.baseUrl ?? "https://storage.mechdna.net";
|
|
17
|
+
this.appId = config.appId;
|
|
18
|
+
// appSchemaId defaults to appId if not explicitly provided (most common case)
|
|
19
|
+
this.appSchemaId = config.appSchemaId ?? this.appId;
|
|
20
|
+
this.apiKey = config.apiKey;
|
|
21
|
+
this.timeout = config.timeout ?? 30000;
|
|
22
|
+
this.logger = config.logger ?? getDefaultLogger();
|
|
23
|
+
this.maxRetries = config.maxRetries ?? 2;
|
|
24
|
+
// Validate configuration
|
|
25
|
+
validateUrl(this.baseUrl, "baseUrl");
|
|
26
|
+
validateNonEmpty(this.appId, "appId");
|
|
27
|
+
validateUuid(this.appId, "appId");
|
|
28
|
+
validateNonEmpty(this.appSchemaId, "appSchemaId");
|
|
29
|
+
validateUuid(this.appSchemaId, "appSchemaId");
|
|
30
|
+
validateNonEmpty(this.apiKey, "apiKey");
|
|
31
|
+
validatePositive(this.timeout, "timeout");
|
|
32
|
+
validateRange(this.maxRetries, 0, 5, "maxRetries");
|
|
33
|
+
this.logger.debug("MechSqlClient initialized", {
|
|
34
|
+
baseUrl: this.baseUrl,
|
|
35
|
+
appId: this.appId,
|
|
36
|
+
timeout: this.timeout,
|
|
37
|
+
maxRetries: this.maxRetries
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Execute a SQL query against Mech Storage
|
|
42
|
+
*
|
|
43
|
+
* @param sql - SQL query string (use $1, $2, etc. for parameters)
|
|
44
|
+
* @param params - Query parameters
|
|
45
|
+
* @returns Object with rows and rowCount
|
|
46
|
+
* @throws ClearAuthSqlError, ClearAuthNetworkError, ClearAuthTimeoutError, ClearAuthRateLimitError
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* const { rows } = await client.execute(
|
|
51
|
+
* "SELECT * FROM users WHERE email = $1",
|
|
52
|
+
* ["user@example.com"]
|
|
53
|
+
* )
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
async execute(sql, params = []) {
|
|
57
|
+
return this.executeWithRetry(sql, params, 0);
|
|
58
|
+
}
|
|
59
|
+
async executeWithRetry(sql, params, attempt) {
|
|
60
|
+
try {
|
|
61
|
+
return await this.executeOnce(sql, params);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (attempt < this.maxRetries && this.isRetryable(err)) {
|
|
65
|
+
const delay = Math.pow(2, attempt) * 100; // exponential backoff
|
|
66
|
+
this.logger.warn(`Query failed, retrying in ${delay}ms (attempt ${attempt + 1}/${this.maxRetries})`, {
|
|
67
|
+
error: err.message,
|
|
68
|
+
attempt
|
|
69
|
+
});
|
|
70
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
71
|
+
return this.executeWithRetry(sql, params, attempt + 1);
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async executeOnce(sql, params) {
|
|
77
|
+
const url = `${this.baseUrl.replace(/\/$/, "")}/api/apps/${this.appId}/postgresql/query`;
|
|
78
|
+
this.logger.debug("Executing SQL query", {
|
|
79
|
+
url,
|
|
80
|
+
paramCount: params.length,
|
|
81
|
+
sqlLength: sql.length
|
|
82
|
+
});
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
85
|
+
try {
|
|
86
|
+
const res = await fetch(url, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: {
|
|
89
|
+
"Content-Type": "application/json",
|
|
90
|
+
"X-API-Key": this.apiKey,
|
|
91
|
+
"X-App-ID": this.appSchemaId
|
|
92
|
+
},
|
|
93
|
+
body: JSON.stringify({ sql, params }),
|
|
94
|
+
signal: controller.signal
|
|
95
|
+
});
|
|
96
|
+
// Handle rate limiting
|
|
97
|
+
if (res.status === 429) {
|
|
98
|
+
const retryAfter = parseInt(res.headers.get("Retry-After") ?? "60", 10) * 1000;
|
|
99
|
+
this.logger.warn("Rate limited by Mech Storage", { retryAfter });
|
|
100
|
+
throw new ClearAuthRateLimitError(retryAfter, { statusCode: 429 });
|
|
101
|
+
}
|
|
102
|
+
if (!res.ok) {
|
|
103
|
+
const text = await res.text();
|
|
104
|
+
this.logger.error("HTTP error from Mech Storage", {
|
|
105
|
+
status: res.status,
|
|
106
|
+
statusText: res.statusText,
|
|
107
|
+
responseLength: text.length
|
|
108
|
+
});
|
|
109
|
+
throw new ClearAuthNetworkError(`HTTP ${res.status}: ${res.statusText}`, res.status, {
|
|
110
|
+
responseText: text.substring(0, 500) // truncate for logging
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const json = await res.json();
|
|
114
|
+
if (json.success === false) {
|
|
115
|
+
this.logger.error("Mech Storage returned error", {
|
|
116
|
+
code: json.error?.code,
|
|
117
|
+
message: json.error?.message
|
|
118
|
+
});
|
|
119
|
+
throw new ClearAuthSqlError(json.error?.message ?? "Unknown error", {
|
|
120
|
+
code: json.error?.code,
|
|
121
|
+
hints: json.error?.hints
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const rows = (json.rows ?? []);
|
|
125
|
+
const rowCount = json.rowCount ?? rows.length;
|
|
126
|
+
this.logger.debug("Query executed successfully", {
|
|
127
|
+
rowCount,
|
|
128
|
+
rowsReturned: rows.length
|
|
129
|
+
});
|
|
130
|
+
return { rows, rowCount };
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
134
|
+
this.logger.error("Query timeout", { timeout: this.timeout });
|
|
135
|
+
throw new ClearAuthTimeoutError(`Query timed out after ${this.timeout}ms`, {
|
|
136
|
+
timeout: this.timeout
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
clearTimeout(timeoutId);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
isRetryable(err) {
|
|
146
|
+
if (err instanceof ClearAuthRateLimitError)
|
|
147
|
+
return true;
|
|
148
|
+
if (err instanceof ClearAuthTimeoutError)
|
|
149
|
+
return true;
|
|
150
|
+
if (err instanceof ClearAuthNetworkError && err.statusCode && err.statusCode >= 500)
|
|
151
|
+
return true;
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=mech-sql-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mech-sql-client.js","sourceRoot":"","sources":["../src/mech-sql-client.ts"],"names":[],"mappings":"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"}
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CreateClearAuthOptions } from "./createMechAuth.js";
|
|
2
|
+
export declare function createClearAuthNode(options: CreateClearAuthOptions): import("./types.js").ClearAuthConfig;
|
|
3
|
+
export { defaultSessionConfig, longSessionConfig, shortSessionConfig } from "./createMechAuth.js";
|
|
4
|
+
export type { ClearAuthConfig } from "./types.js";
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createClearAuth } from "./createMechAuth.js";
|
|
2
|
+
import { createArgon2idPasswordHasher } from "./password-hasher-argon2.js";
|
|
3
|
+
export function createClearAuthNode(options) {
|
|
4
|
+
return createClearAuth({
|
|
5
|
+
...options,
|
|
6
|
+
passwordHasher: options.passwordHasher ?? createArgon2idPasswordHasher(),
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export { defaultSessionConfig, longSessionConfig, shortSessionConfig } from "./createMechAuth.js";
|
|
10
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAA;AAE1E,MAAM,UAAU,mBAAmB,CAAC,OAA+B;IACjE,OAAO,eAAe,CAAC;QACrB,GAAG,OAAO;QACV,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,4BAA4B,EAAE;KACzE,CAAC,CAAA;AACJ,CAAC;AAED,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arctic OAuth Provider Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates Arctic OAuth provider instances for GitHub and Google authentication.
|
|
5
|
+
* Arctic is a lightweight OAuth library that works across all JavaScript runtimes
|
|
6
|
+
* including Cloudflare Workers.
|
|
7
|
+
*
|
|
8
|
+
* @see https://arcticjs.dev/
|
|
9
|
+
*/
|
|
10
|
+
import { GitHub, Google } from 'arctic';
|
|
11
|
+
import type { ClearAuthConfig } from '../types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Create GitHub OAuth provider instance
|
|
14
|
+
*
|
|
15
|
+
* @param config - ClearAuth configuration
|
|
16
|
+
* @returns Arctic GitHub provider instance
|
|
17
|
+
* @throws Error if GitHub OAuth is not configured
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const github = createGitHubProvider(config)
|
|
22
|
+
* const url = await github.createAuthorizationURL(state, { scopes: ['user:email'] })
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function createGitHubProvider(config: ClearAuthConfig): GitHub;
|
|
26
|
+
/**
|
|
27
|
+
* Create Google OAuth provider instance
|
|
28
|
+
*
|
|
29
|
+
* @param config - ClearAuth configuration
|
|
30
|
+
* @returns Arctic Google provider instance
|
|
31
|
+
* @throws Error if Google OAuth is not configured
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const google = createGoogleProvider(config)
|
|
36
|
+
* const url = await google.createAuthorizationURL(state, codeVerifier, { scopes: ['email', 'profile'] })
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function createGoogleProvider(config: ClearAuthConfig): Google;
|
|
40
|
+
/**
|
|
41
|
+
* Get configured OAuth providers
|
|
42
|
+
*
|
|
43
|
+
* Returns a map of provider names to Arctic provider instances.
|
|
44
|
+
* Only includes providers that are configured.
|
|
45
|
+
*
|
|
46
|
+
* @param config - ClearAuth configuration
|
|
47
|
+
* @returns Map of provider names to Arctic provider instances
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* const providers = getConfiguredProviders(config)
|
|
52
|
+
* if (providers.github) {
|
|
53
|
+
* // GitHub is configured
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function getConfiguredProviders(config: ClearAuthConfig): {
|
|
58
|
+
github?: GitHub;
|
|
59
|
+
google?: Google;
|
|
60
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arctic OAuth Provider Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates Arctic OAuth provider instances for GitHub and Google authentication.
|
|
5
|
+
* Arctic is a lightweight OAuth library that works across all JavaScript runtimes
|
|
6
|
+
* including Cloudflare Workers.
|
|
7
|
+
*
|
|
8
|
+
* @see https://arcticjs.dev/
|
|
9
|
+
*/
|
|
10
|
+
import { GitHub, Google } from 'arctic';
|
|
11
|
+
/**
|
|
12
|
+
* Create GitHub OAuth provider instance
|
|
13
|
+
*
|
|
14
|
+
* @param config - ClearAuth configuration
|
|
15
|
+
* @returns Arctic GitHub provider instance
|
|
16
|
+
* @throws Error if GitHub OAuth is not configured
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const github = createGitHubProvider(config)
|
|
21
|
+
* const url = await github.createAuthorizationURL(state, { scopes: ['user:email'] })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function createGitHubProvider(config) {
|
|
25
|
+
if (!config.oauth?.github) {
|
|
26
|
+
throw new Error('GitHub OAuth is not configured');
|
|
27
|
+
}
|
|
28
|
+
const { clientId, clientSecret, redirectUri } = config.oauth.github;
|
|
29
|
+
if (!clientId || !clientSecret || !redirectUri) {
|
|
30
|
+
throw new Error('GitHub OAuth configuration is incomplete (missing clientId, clientSecret, or redirectUri)');
|
|
31
|
+
}
|
|
32
|
+
return new GitHub(clientId, clientSecret, redirectUri);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create Google OAuth provider instance
|
|
36
|
+
*
|
|
37
|
+
* @param config - ClearAuth configuration
|
|
38
|
+
* @returns Arctic Google provider instance
|
|
39
|
+
* @throws Error if Google OAuth is not configured
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const google = createGoogleProvider(config)
|
|
44
|
+
* const url = await google.createAuthorizationURL(state, codeVerifier, { scopes: ['email', 'profile'] })
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function createGoogleProvider(config) {
|
|
48
|
+
if (!config.oauth?.google) {
|
|
49
|
+
throw new Error('Google OAuth is not configured');
|
|
50
|
+
}
|
|
51
|
+
const { clientId, clientSecret, redirectUri } = config.oauth.google;
|
|
52
|
+
if (!clientId || !clientSecret || !redirectUri) {
|
|
53
|
+
throw new Error('Google OAuth configuration is incomplete (missing clientId, clientSecret, or redirectUri)');
|
|
54
|
+
}
|
|
55
|
+
return new Google(clientId, clientSecret, redirectUri);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get configured OAuth providers
|
|
59
|
+
*
|
|
60
|
+
* Returns a map of provider names to Arctic provider instances.
|
|
61
|
+
* Only includes providers that are configured.
|
|
62
|
+
*
|
|
63
|
+
* @param config - ClearAuth configuration
|
|
64
|
+
* @returns Map of provider names to Arctic provider instances
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* const providers = getConfiguredProviders(config)
|
|
69
|
+
* if (providers.github) {
|
|
70
|
+
* // GitHub is configured
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export function getConfiguredProviders(config) {
|
|
75
|
+
const providers = {};
|
|
76
|
+
if (config.oauth?.github) {
|
|
77
|
+
try {
|
|
78
|
+
providers.github = createGitHubProvider(config);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
console.error('Failed to create GitHub provider:', err);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (config.oauth?.google) {
|
|
85
|
+
try {
|
|
86
|
+
providers.google = createGoogleProvider(config);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.error('Failed to create Google provider:', err);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return providers;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=arctic-providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arctic-providers.js","sourceRoot":"","sources":["../../src/oauth/arctic-providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAGvC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAuB;IAC1D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;IACnD,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAA;IAEnE,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY,IAAI,CAAC,WAAW,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC,CAAA;IAC9G,CAAC;IAED,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,CAAA;AACxD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAuB;IAC1D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;IACnD,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAA;IAEnE,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY,IAAI,CAAC,WAAW,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC,CAAA;IAC9G,CAAC;IAED,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,CAAA;AACxD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAuB;IAI5D,MAAM,SAAS,GAAyC,EAAE,CAAA;IAE1D,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,SAAS,CAAC,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAA;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAA;QACzD,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,SAAS,CAAC,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAA;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAA;QACzD,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Callback Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared logic for OAuth callback handling including:
|
|
5
|
+
* - State parameter validation (CSRF protection)
|
|
6
|
+
* - User upsert (create or update user by OAuth provider ID)
|
|
7
|
+
* - Session creation after successful OAuth
|
|
8
|
+
* - Error handling
|
|
9
|
+
*/
|
|
10
|
+
import type { Kysely } from 'kysely';
|
|
11
|
+
import type { Database, User } from '../database/schema.js';
|
|
12
|
+
import type { OAuthUserProfile, RequestContext } from '../types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Upsert user from OAuth profile
|
|
15
|
+
*
|
|
16
|
+
* Creates a new user or updates an existing user based on the OAuth provider ID.
|
|
17
|
+
* Uses github_id or google_id column to identify existing users.
|
|
18
|
+
*
|
|
19
|
+
* @param db - Kysely database instance
|
|
20
|
+
* @param provider - OAuth provider name ('github' or 'google')
|
|
21
|
+
* @param profile - Normalized OAuth user profile
|
|
22
|
+
* @returns User record from database
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const user = await upsertOAuthUser(db, 'github', profile)
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function upsertOAuthUser(db: Kysely<Database>, provider: 'github' | 'google', profile: OAuthUserProfile): Promise<User>;
|
|
30
|
+
/**
|
|
31
|
+
* Create session for user
|
|
32
|
+
*
|
|
33
|
+
* Creates a new session record in the database and returns the session ID.
|
|
34
|
+
* Sessions expire after the configured duration (default: 30 days).
|
|
35
|
+
*
|
|
36
|
+
* @param db - Kysely database instance
|
|
37
|
+
* @param userId - User ID to create session for
|
|
38
|
+
* @param expiresInSeconds - Session expiration time in seconds (default: 2592000 = 30 days)
|
|
39
|
+
* @param context - Optional request context (IP address, user agent)
|
|
40
|
+
* @returns Session ID
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const sessionId = await createSession(db, user.id, 2592000, { ipAddress, userAgent })
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function createSession(db: Kysely<Database>, userId: string, expiresInSeconds?: number, // 30 days
|
|
48
|
+
context?: RequestContext): Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* Validate session
|
|
51
|
+
*
|
|
52
|
+
* Checks if a session exists and is not expired.
|
|
53
|
+
*
|
|
54
|
+
* @param db - Kysely database instance
|
|
55
|
+
* @param sessionId - Session ID to validate
|
|
56
|
+
* @returns User if session is valid, null otherwise
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const user = await validateSession(db, sessionId)
|
|
61
|
+
* if (!user) {
|
|
62
|
+
* return new Response('Unauthorized', { status: 401 })
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function validateSession(db: Kysely<Database>, sessionId: string): Promise<User | null>;
|
|
67
|
+
/**
|
|
68
|
+
* Delete session (logout)
|
|
69
|
+
*
|
|
70
|
+
* Removes a session from the database.
|
|
71
|
+
*
|
|
72
|
+
* @param db - Kysely database instance
|
|
73
|
+
* @param sessionId - Session ID to delete
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* await deleteSession(db, sessionId)
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function deleteSession(db: Kysely<Database>, sessionId: string): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Delete all sessions for a user
|
|
83
|
+
*
|
|
84
|
+
* Removes all sessions for a specific user (useful for password changes, etc.)
|
|
85
|
+
*
|
|
86
|
+
* @param db - Kysely database instance
|
|
87
|
+
* @param userId - User ID to delete sessions for
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* await deleteAllUserSessions(db, userId)
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function deleteAllUserSessions(db: Kysely<Database>, userId: string): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Clean up expired sessions
|
|
97
|
+
*
|
|
98
|
+
* Removes all expired sessions from the database.
|
|
99
|
+
* This should be run periodically as a background job.
|
|
100
|
+
*
|
|
101
|
+
* @param db - Kysely database instance
|
|
102
|
+
* @returns Number of sessions deleted
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const deleted = await cleanupExpiredSessions(db)
|
|
107
|
+
* console.log(`Cleaned up ${deleted} expired sessions`)
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export declare function cleanupExpiredSessions(db: Kysely<Database>): Promise<number>;
|
|
111
|
+
/**
|
|
112
|
+
* Parse cookie header
|
|
113
|
+
*
|
|
114
|
+
* Parses the Cookie header and returns a map of cookie names to values.
|
|
115
|
+
*
|
|
116
|
+
* @param cookieHeader - Cookie header string
|
|
117
|
+
* @returns Map of cookie names to values
|
|
118
|
+
*
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
export declare function parseCookies(cookieHeader: string): Record<string, string>;
|
|
122
|
+
/**
|
|
123
|
+
* Create cookie header
|
|
124
|
+
*
|
|
125
|
+
* Creates a Set-Cookie header string with appropriate security attributes.
|
|
126
|
+
*
|
|
127
|
+
* @param name - Cookie name
|
|
128
|
+
* @param value - Cookie value
|
|
129
|
+
* @param options - Cookie options
|
|
130
|
+
* @returns Set-Cookie header string
|
|
131
|
+
*
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
134
|
+
export declare function createCookieHeader(name: string, value: string, options?: {
|
|
135
|
+
httpOnly?: boolean;
|
|
136
|
+
secure?: boolean;
|
|
137
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
138
|
+
path?: string;
|
|
139
|
+
maxAge?: number;
|
|
140
|
+
expires?: Date;
|
|
141
|
+
}): string;
|
|
142
|
+
/**
|
|
143
|
+
* Create delete cookie header
|
|
144
|
+
*
|
|
145
|
+
* Creates a Set-Cookie header that deletes a cookie.
|
|
146
|
+
*
|
|
147
|
+
* @param name - Cookie name
|
|
148
|
+
* @param options - Cookie options (path, etc.)
|
|
149
|
+
* @returns Set-Cookie header string
|
|
150
|
+
*
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
export declare function createDeleteCookieHeader(name: string, options?: {
|
|
154
|
+
path?: string;
|
|
155
|
+
}): string;
|