Skip to content

Commit 384e4c8

Browse files
authored
fix: plugin inference using withCloudflare (#5)
1 parent 1e618d6 commit 384e4c8

File tree

3 files changed

+42
-23
lines changed

3 files changed

+42
-23
lines changed

examples/opennextjs/src/app/dashboard/page.tsx

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { initAuth } from "@/auth";
2-
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
3-
import { SessionWithGeolocation } from "better-auth-cloudflare";
2+
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
43
import { headers } from "next/headers";
4+
import Link from "next/link";
55
import { redirect } from "next/navigation";
66
import SignOutButton from "./SignOutButton"; // Import the client component
77

@@ -14,8 +14,11 @@ export default async function DashboardPage() {
1414
redirect("/"); // Redirect to home if no session
1515
}
1616

17-
// Access Cloudflare data from session.session?.cloudflare or session.session?.geo
18-
const cloudflareGeolocationData = session.session as SessionWithGeolocation;
17+
// Get geolocation data from our plugin's endpoint
18+
const cloudflareGeolocationData = await authInstance.api.getGeolocation({ headers: await headers() });
19+
20+
// Access another plugin's endpoint to demonstrate plugin type inference is still intact
21+
const openAPISpec = await authInstance.api.generateOpenAPISchema();
1922

2023
return (
2124
<div className="flex flex-col items-center justify-center min-h-screen p-8 font-[family-name:var(--font-geist-sans)]">
@@ -39,40 +42,50 @@ export default async function DashboardPage() {
3942
<strong>User ID:</strong> {session.user.id}
4043
</p>
4144
)}
42-
{cloudflareGeolocationData && (
45+
{cloudflareGeolocationData && "error" in cloudflareGeolocationData && (
46+
<p className="text-md">
47+
<strong>Error:</strong> {cloudflareGeolocationData.error}
48+
</p>
49+
)}
50+
{cloudflareGeolocationData && !("error" in cloudflareGeolocationData) && (
4351
<>
4452
<p className="text-md">
45-
<strong>Timezone:</strong> {cloudflareGeolocationData.timezone}
53+
<strong>Timezone:</strong> {cloudflareGeolocationData.timezone || "Unknown"}
4654
</p>
4755
<p className="text-md">
48-
<strong>City:</strong> {cloudflareGeolocationData.city}
56+
<strong>City:</strong> {cloudflareGeolocationData.city || "Unknown"}
4957
</p>
5058
<p className="text-md">
51-
<strong>Country:</strong> {cloudflareGeolocationData.country}
59+
<strong>Country:</strong> {cloudflareGeolocationData.country || "Unknown"}
5260
</p>
5361
<p className="text-md">
54-
<strong>Region:</strong> {cloudflareGeolocationData.region}
62+
<strong>Region:</strong> {cloudflareGeolocationData.region || "Unknown"}
5563
</p>
5664
<p className="text-md">
57-
<strong>Region Code:</strong> {cloudflareGeolocationData.regionCode}
65+
<strong>Region Code:</strong> {cloudflareGeolocationData.regionCode || "Unknown"}
5866
</p>
5967
<p className="text-md">
60-
<strong>Data Center:</strong> {cloudflareGeolocationData.colo}
68+
<strong>Data Center:</strong> {cloudflareGeolocationData.colo || "Unknown"}
6169
</p>
6270
{cloudflareGeolocationData.latitude && (
6371
<p className="text-md">
64-
<strong>Latitude:</strong> {cloudflareGeolocationData.latitude}
72+
<strong>Latitude:</strong> {cloudflareGeolocationData.latitude || "Unknown"}
6573
</p>
6674
)}
6775
{cloudflareGeolocationData.longitude && (
6876
<p className="text-md">
69-
<strong>Longitude:</strong> {cloudflareGeolocationData.longitude}
77+
<strong>Longitude:</strong> {cloudflareGeolocationData.longitude || "Unknown"}
7078
</p>
7179
)}
7280
</>
7381
)}
7482
<SignOutButton /> {/* Use the client component for sign out */}
7583
</CardContent>
84+
<CardFooter>
85+
<Link href="/api/auth/reference#tag/cloudflare/get/cloudflare/geolocation" className="underline">
86+
View OpenAPI v{openAPISpec.openapi} Schema for Cloudflare Geolocation
87+
</Link>
88+
</CardFooter>
7689
</Card>
7790
<footer className="absolute bottom-0 w-full text-center text-sm text-gray-500 py-4">
7891
Powered by{" "}

examples/opennextjs/src/auth/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { KVNamespace } from "@cloudflare/workers-types";
22
import { betterAuth } from "better-auth";
33
import { withCloudflare } from "better-auth-cloudflare";
44
import { drizzleAdapter } from "better-auth/adapters/drizzle";
5+
import { openAPI } from "better-auth/plugins";
56
import { getDb } from "../db";
67

78
// Define an asynchronous function to build your auth configuration
@@ -38,6 +39,7 @@ async function authBuilder() {
3839
enabled: true,
3940
// ... other rate limiting options
4041
},
42+
plugins: [openAPI()],
4143
// ... other Better Auth options
4244
}
4345
)

src/index.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { KVNamespace } from "@cloudflare/workers-types";
22
import { getCloudflareContext } from "@opennextjs/cloudflare";
33
import { type BetterAuthOptions, type BetterAuthPlugin, type SecondaryStorage, type Session } from "better-auth";
44
import { drizzleAdapter } from "better-auth/adapters/drizzle";
5-
import { createAuthEndpoint } from "better-auth/api";
5+
import { createAuthEndpoint, getSessionFromCtx } from "better-auth/api";
66
import { schema } from "./schema";
77
import type { CloudflareGeolocation, CloudflarePluginOptions, WithCloudflareOptions } from "./types";
88
export * from "./client";
@@ -31,17 +31,11 @@ export const cloudflare = (options?: CloudflarePluginOptions) => {
3131
method: "GET",
3232
},
3333
async ctx => {
34-
const session = ctx.context?.session;
34+
const session = await getSessionFromCtx(ctx);
3535
if (!session) {
3636
return ctx.json({ error: "Unauthorized" }, { status: 401 });
3737
}
3838

39-
// Original code threw an error if ctx.request was not available.
40-
// Retaining similar logic but returning a 500 status code.
41-
if (!ctx.request) {
42-
return ctx.json({ error: "Request is not available" }, { status: 500 });
43-
}
44-
4539
const cf = getCloudflareContext().cf;
4640
if (!cf) {
4741
return ctx.json({ error: "Cloudflare context is not available" }, { status: 404 });
@@ -141,6 +135,13 @@ export const getGeolocation = (): CloudflareGeolocation | undefined => {
141135
};
142136
};
143137

138+
/**
139+
* Type helper to infer the enhanced auth type with Cloudflare plugin
140+
*/
141+
type WithCloudflareAuth<T extends BetterAuthOptions> = T & {
142+
plugins: [ReturnType<typeof cloudflare>, ...(T["plugins"] extends readonly any[] ? T["plugins"] : [])];
143+
};
144+
144145
/**
145146
* Enhances BetterAuthOptions with Cloudflare-specific configurations.
146147
*
@@ -151,7 +152,10 @@ export const getGeolocation = (): CloudflareGeolocation | undefined => {
151152
* @param options - The base BetterAuthOptions to be enhanced.
152153
* @returns BetterAuthOptions configured for use with Cloudflare.
153154
*/
154-
export const withCloudflare = <T extends BetterAuthOptions>(cloudFlareOptions: WithCloudflareOptions, options: T) => {
155+
export const withCloudflare = <T extends BetterAuthOptions>(
156+
cloudFlareOptions: WithCloudflareOptions,
157+
options: T
158+
): WithCloudflareAuth<T> => {
155159
const autoDetectIpEnabled =
156160
cloudFlareOptions.autoDetectIpAddress === undefined || cloudFlareOptions.autoDetectIpAddress === true;
157161
const geolocationTrackingForSession =
@@ -191,7 +195,7 @@ export const withCloudflare = <T extends BetterAuthOptions>(cloudFlareOptions: W
191195
plugins: [cloudflare(cloudFlareOptions), ...(options.plugins ?? [])],
192196
advanced: updatedAdvanced,
193197
session: updatedSession,
194-
} as T;
198+
} as WithCloudflareAuth<T>;
195199
};
196200

197201
export type SessionWithGeolocation = Session & CloudflareGeolocation;

0 commit comments

Comments
 (0)