@effectify/node-better-auth 0.5.12 → 0.5.13-beta.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/package.json +1 -1
- package/dist/src/lib/handler.d.ts +6 -4
- package/dist/src/lib/handler.js +44 -39
- package/package.json +1 -1
package/dist/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Auth } from "better-auth";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
|
-
import { BetterAuthApiError } from "./better-auth-error.js";
|
|
4
3
|
import type { ConfigError } from "effect/Config";
|
|
5
4
|
import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";
|
|
6
5
|
import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
|
|
7
|
-
|
|
8
|
-
handler: Auth["handler"];
|
|
9
|
-
|
|
6
|
+
type BetterAuthHandler = Auth["handler"] | {
|
|
7
|
+
readonly handler: Auth["handler"];
|
|
8
|
+
readonly options?: Pick<Auth["options"], "trustedOrigins">;
|
|
9
|
+
};
|
|
10
|
+
export declare const toEffectHandler: (auth: BetterAuthHandler) => Effect.Effect<HttpServerResponse.HttpServerResponse, ConfigError, HttpServerRequest.HttpServerRequest>;
|
|
11
|
+
export {};
|
package/dist/src/lib/handler.js
CHANGED
|
@@ -5,8 +5,18 @@ import { BetterAuthApiError } from "./better-auth-error.js";
|
|
|
5
5
|
import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";
|
|
6
6
|
import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
|
|
7
7
|
import * as NodeHttpServerRequest from "@effect/platform-node/NodeHttpServerRequest";
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const canonicalOrigin = (value) => {
|
|
9
|
+
if (!URL.canParse(value))
|
|
10
|
+
return undefined;
|
|
11
|
+
const url = new URL(value);
|
|
12
|
+
if ((url.protocol !== "http:" && url.protocol !== "https:") ||
|
|
13
|
+
url.username !== "" ||
|
|
14
|
+
url.password !== "" ||
|
|
15
|
+
url.hostname.includes("*")) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
return url.origin;
|
|
19
|
+
};
|
|
10
20
|
export const toEffectHandler = (auth) => Effect.gen(function* () {
|
|
11
21
|
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
12
22
|
const nodeRequest = NodeHttpServerRequest.toIncomingMessage(request);
|
|
@@ -14,14 +24,21 @@ export const toEffectHandler = (auth) => Effect.gen(function* () {
|
|
|
14
24
|
const appUrl = yield* Config.url("BETTER_AUTH_URL");
|
|
15
25
|
// Debug: log the configured app URL so we can diagnose Config errors
|
|
16
26
|
yield* Effect.log(`toEffectHandler: BETTER_AUTH_URL=${String(appUrl)}`);
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
const allowedOrigins = new Set([appUrl.origin]);
|
|
28
|
+
const trustedOrigins = typeof auth === "function" ? undefined : auth.options?.trustedOrigins;
|
|
29
|
+
if (Array.isArray(trustedOrigins)) {
|
|
30
|
+
for (const trustedOrigin of trustedOrigins) {
|
|
31
|
+
if (typeof trustedOrigin !== "string")
|
|
32
|
+
continue;
|
|
33
|
+
const origin = canonicalOrigin(trustedOrigin);
|
|
34
|
+
if (origin !== undefined)
|
|
35
|
+
allowedOrigins.add(origin);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const requestOrigin = nodeRequest.headers.origin;
|
|
39
|
+
const origin = requestOrigin === undefined ? undefined : canonicalOrigin(requestOrigin);
|
|
40
|
+
if (origin !== undefined && allowedOrigins.has(origin)) {
|
|
41
|
+
nodeResponse.setHeader("Access-Control-Allow-Origin", origin);
|
|
25
42
|
nodeResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
26
43
|
nodeResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
27
44
|
nodeResponse.setHeader("Access-Control-Max-Age", "600");
|
|
@@ -35,40 +52,28 @@ export const toEffectHandler = (auth) => Effect.gen(function* () {
|
|
|
35
52
|
}
|
|
36
53
|
// Log incoming request for debugging
|
|
37
54
|
const authHeader = nodeRequest.headers.authorization;
|
|
38
|
-
yield* Effect.log(`toEffectHandler: incoming ${nodeRequest.method} ${String(nodeRequest.url)} headers: cookie=${nodeRequest.headers.cookie
|
|
55
|
+
yield* Effect.log(`toEffectHandler: incoming ${nodeRequest.method} ${String(nodeRequest.url)} headers: cookie=${nodeRequest.headers.cookie ? "present" : "none"}, auth=${authHeader ? "present" : "none"}`);
|
|
39
56
|
// If no cookie but has Authorization header (bearer token), set it as cookie for better-auth
|
|
40
57
|
if (!nodeRequest.headers.cookie && authHeader?.startsWith("Bearer ")) {
|
|
41
58
|
const token = authHeader.slice(7); // Remove "Bearer " prefix
|
|
42
59
|
nodeRequest.headers.cookie = `better-auth.session_token=${token}`;
|
|
43
60
|
yield* Effect.log(`toEffectHandler: using token from Authorization header as cookie`);
|
|
44
61
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
yield* Effect.log(`toEffectHandler:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Try to return the error to the client as JSON to make debugging easier
|
|
60
|
-
nodeResponse.statusCode = 500;
|
|
61
|
-
nodeResponse.setHeader("Content-Type", "application/json");
|
|
62
|
-
const payload = JSON.stringify({ error: String(err) });
|
|
63
|
-
nodeResponse.end(payload);
|
|
64
|
-
}
|
|
65
|
-
catch (writeErr) {
|
|
66
|
-
// If writing the error response fails, log that too
|
|
67
|
-
yield* Effect.log(`toEffectHandler: failed to write error response: ${String(writeErr)}`);
|
|
62
|
+
const handler = typeof auth === "function" ? auth : auth.handler;
|
|
63
|
+
return yield* Effect.tryPromise({
|
|
64
|
+
try: () => toNodeHandler(handler)(nodeRequest, nodeResponse),
|
|
65
|
+
catch: (cause) => new BetterAuthApiError({ cause }),
|
|
66
|
+
}).pipe(Effect.tap(() => Effect.log(`toEffectHandler: completed ${nodeRequest.method} ${String(nodeRequest.url)} -> ${nodeResponse.statusCode}`)), Effect.map(() => HttpServerResponse.empty({
|
|
67
|
+
status: nodeResponse.writableEnded ? nodeResponse.statusCode : 499,
|
|
68
|
+
})), Effect.catchTag("BetterAuthApiError", (error) => Effect.gen(function* () {
|
|
69
|
+
const errorMessage = `${String(error)}: ${String(error.cause)}`;
|
|
70
|
+
yield* Effect.log(`toEffectHandler: error handling ${nodeRequest.method} ${String(nodeRequest.url)}: ${errorMessage}`);
|
|
71
|
+
if (nodeResponse.headersSent || nodeResponse.writableEnded) {
|
|
72
|
+
if (!nodeResponse.writableEnded) {
|
|
73
|
+
yield* Effect.sync(() => nodeResponse.end());
|
|
74
|
+
}
|
|
75
|
+
return HttpServerResponse.empty({ status: nodeResponse.statusCode });
|
|
68
76
|
}
|
|
69
|
-
return HttpServerResponse.
|
|
70
|
-
}
|
|
71
|
-
return HttpServerResponse.empty({
|
|
72
|
-
status: nodeResponse.writableFinished ? nodeResponse.statusCode : 499,
|
|
73
|
-
});
|
|
77
|
+
return HttpServerResponse.jsonUnsafe({ error: "Internal Server Error" }, { status: 500 });
|
|
78
|
+
})));
|
|
74
79
|
});
|