fix: bearer token interception for direct API calls

This commit is contained in:
Bereket Engida
2024-10-09 20:22:18 +03:00
parent 1dc573bca9
commit 0e77ed316e
5 changed files with 85 additions and 28 deletions

View File

@@ -1,5 +1,6 @@
import { betterAuth } from "better-auth"; import { betterAuth } from "better-auth";
import { import {
bearer,
organization, organization,
passkey, passkey,
phoneNumber, phoneNumber,
@@ -12,6 +13,7 @@ import { resend } from "./email/resend";
const from = process.env.BETTER_AUTH_EMAIL || "delivered@resend.dev"; const from = process.env.BETTER_AUTH_EMAIL || "delivered@resend.dev";
const to = process.env.TEST_EMAIL || ""; const to = process.env.TEST_EMAIL || "";
export const auth = betterAuth({ export const auth = betterAuth({
database: new LibsqlDialect({ database: new LibsqlDialect({
url: process.env.TURSO_DATABASE_URL || "", url: process.env.TURSO_DATABASE_URL || "",
@@ -80,6 +82,7 @@ export const auth = betterAuth({
}, },
}), }),
passkey(), passkey(),
bearer()
], ],
socialProviders: { socialProviders: {
github: { github: {

View File

@@ -110,11 +110,33 @@ export function getEndpoints<
let api: Record<string, any> = {}; let api: Record<string, any> = {};
for (const [key, value] of Object.entries(endpoints)) { for (const [key, value] of Object.entries(endpoints)) {
api[key] = async (context: any) => { api[key] = async (context: any) => {
/** let c = await ctx;
* TODO: move this to respond a json response for (const plugin of options.plugins || []) {
* instead of response object. if (plugin.hooks?.before) {
*/ for (const hook of plugin.hooks.before) {
const c = await ctx; const match = hook.matcher({
...value,
...context,
context: c,
});
if (match) {
const hookRes = await hook.handler({
...context,
context: {
...c,
...context.context,
},
});
if (hookRes && "context" in hookRes) {
c = {
...c,
...hookRes.context,
};
}
}
}
}
}
//@ts-ignore //@ts-ignore
const endpointRes = await value({ const endpointRes = await value({
...context, ...context,

View File

@@ -19,7 +19,6 @@ type InferAPI<API> = Omit<
export const betterAuth = <O extends BetterAuthOptions>(options: O) => { export const betterAuth = <O extends BetterAuthOptions>(options: O) => {
const authContext = init(options); const authContext = init(options);
const { api } = getEndpoints(authContext, options); const { api } = getEndpoints(authContext, options);
type API = typeof api;
return { return {
handler: async (request: Request) => { handler: async (request: Request) => {

View File

@@ -3,7 +3,7 @@ import { bearer } from ".";
import { getTestInstance } from "../../test-utils/test-instance"; import { getTestInstance } from "../../test-utils/test-instance";
describe("bearer", async () => { describe("bearer", async () => {
const { client, signInWithTestUser } = await getTestInstance({ const { client, signInWithTestUser, auth } = await getTestInstance({
plugins: [bearer()], plugins: [bearer()],
}); });
@@ -32,4 +32,15 @@ describe("bearer", async () => {
}); });
expect(sessions.data).toHaveLength(2); expect(sessions.data).toHaveLength(2);
}); });
it("should work on server actions", async () => {
const { res } = await signInWithTestUser();
token = res.data?.session.id || "";
const headers = new Headers();
headers.set("authorization", `Bearer ${token}`);
const session = await auth.api.getSession({
headers,
});
expect(session?.session.id).toBe(token);
});
}); });

View File

@@ -1,6 +1,4 @@
import { serializeSigned } from "better-call"; import { serializeSigned } from "better-call";
import { createAuthMiddleware } from "../../api/call";
import { BetterAuthError } from "../../error/better-auth-error";
import type { BetterAuthPlugin } from "../../types/plugins"; import type { BetterAuthPlugin } from "../../types/plugins";
/** /**
@@ -9,25 +7,49 @@ import type { BetterAuthPlugin } from "../../types/plugins";
export const bearer = () => { export const bearer = () => {
return { return {
id: "bearer", id: "bearer",
async onRequest(request, ctx) { hooks: {
const token = request.headers before: [
.get("authorization") {
?.replace("Bearer ", ""); matcher(context) {
if (!token) { return Boolean(
return; context.request?.headers.get("authorization") ||
} context.headers?.get("authorization"),
const headers = request.headers || new Headers(); );
const signedToken = await serializeSigned("", token, ctx.secret); },
headers.set( handler: async (c) => {
"cookie", const token =
`${ctx.authCookies.sessionToken.name}=${signedToken.replace("=", "")}`, c.request?.headers.get("authorization")?.replace("Bearer ", "") ||
); c.headers?.get("authorization")?.replace("Bearer ", "");
return { if (!token) {
request: new Request(request.url, { return;
method: request.method, }
headers, const signedToken = await serializeSigned(
}), "",
}; token,
c.context.secret,
);
if (c.request) {
c.request.headers.set(
"cookie",
`${
c.context.authCookies.sessionToken.name
}=${signedToken.replace("=", "")}`,
);
}
if (c.headers) {
c.headers.set(
"cookie",
`${
c.context.authCookies.sessionToken.name
}=${signedToken.replace("=", "")}`,
);
}
return {
context: c,
};
},
},
],
}, },
} satisfies BetterAuthPlugin; } satisfies BetterAuthPlugin;
}; };