Skip to content
This repository has been archived by the owner on Oct 21, 2024. It is now read-only.

Commit

Permalink
fix signing tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
thdxr committed Feb 14, 2024
1 parent d502f5c commit 45f8987
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 80 deletions.
10 changes: 8 additions & 2 deletions sdk/js/src/auth/adapter/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ export type AdapterRoute = Hono;
export interface AdapterOptions<Properties> {
name: string;
algorithm: string;
publicKey: Promise<KeyLike>;
privateKey: Promise<KeyLike>;
encryption: {
publicKey: Promise<KeyLike>;
privateKey: Promise<KeyLike>;
};
signing: {
publicKey: Promise<KeyLike>;
privateKey: Promise<KeyLike>;
};
success: (ctx: Context, properties: Properties) => Promise<Response>;
forward: (ctx: Context, response: Response) => Response;
cookie: (ctx: Context, key: string, value: string, maxAge: number) => void;
Expand Down
4 changes: 2 additions & 2 deletions sdk/js/src/auth/adapter/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function CodeAdapter(config: {
),
)
.setProtectedHeader({ alg: "RSA-OAEP-512", enc: "A256GCM" })
.encrypt(await ctx.publicKey);
.encrypt(await ctx.encryption.publicKey);
ctx.cookie(c, "authorization", authorization, 60 * 10);
return ctx.forward(c, await config.onCodeRequest(code, claims as any));
});
Expand All @@ -51,7 +51,7 @@ export function CodeAdapter(config: {
const { code, claims } = JSON.parse(
new TextDecoder().decode(
await jose
.compactDecrypt(authorization!, await ctx.privateKey)
.compactDecrypt(authorization!, await ctx.encryption.privateKey)
.then((value) => value.plaintext),
),
);
Expand Down
4 changes: 2 additions & 2 deletions sdk/js/src/auth/adapter/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function LinkAdapter(config: {
const token = await new jose.SignJWT(c.req.query())
.setProtectedHeader({ alg: ctx.algorithm })
.setExpirationTime("10m")
.sign(await ctx.privateKey);
.sign(await ctx.signing.privateKey);

const url = new URL(new URL(c.req.url).origin);
url.pathname = `/${ctx.name}/callback`;
Expand All @@ -27,7 +27,7 @@ export function LinkAdapter(config: {
routes.get("/callback", async (c) => {
const token = c.req.query("token");
if (!token) throw new Error("Missing token parameter");
const verified = await jose.jwtVerify(token, await ctx.publicKey);
const verified = await jose.jwtVerify(token, await ctx.signing.publicKey);
const resp = await ctx.success(c, { claims: verified.payload as any });
return resp;
});
Expand Down
148 changes: 74 additions & 74 deletions sdk/js/src/auth/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,6 @@ export function AuthHandler<
};
}) {
const auth = Resource[process.env.AUTH_ID!];
const privateKey = jose.importPKCS8(auth.privateKey, "RSA-OAEP-512");
const publicKey = jose.importSPKI(auth.publicKey, "RSA-OAEP-512", {
extractable: true,
});
const app = new Hono();

if (!input.callbacks.auth.error) {
Expand All @@ -106,72 +102,15 @@ export function AuthHandler<
};
}

app.get("/token", async (c) => {
const form = await c.req.formData();
if (form.get("grant_type") !== "authorization_code") {
c.status(400);
return c.text("Invalid grant_type");
}
const code = form.get("code");
if (!code) {
c.status(400);
return c.text("Missing code");
}

const { payload } = await jose.jwtVerify(code as string, await privateKey);
if (payload.redirect_uri !== form.get("redirect_uri")) {
c.status(400);
return c.text("redirect_uri mismatch");
}
if (payload.client_id !== form.get("client_id")) {
c.status(400);
return c.text("client_id mismatch");
}

return c.json({
access_token: payload.token,
});
});

app.use("/:provider/authorize", async (c, next) => {
const provider = c.req.param("provider");
const response_type =
c.req.query("response_type") || getCookie(c, "response_type");
const redirect_uri =
c.req.query("redirect_uri") || getCookie(c, "redirect_uri");
const state = c.req.query("state") || getCookie(c, "state");

if (!provider) {
c.status(400);
return c.text("Missing provider");
}

if (!redirect_uri) {
c.status(400);
return c.text("Missing redirect_uri");
}

if (!response_type) {
c.status(400);
return c.text("Missing response_type");
}
options.cookie(c, "provider", provider, 60 * 10);
options.cookie(c, "response_type", response_type, 60 * 10);
options.cookie(c, "redirect_uri", redirect_uri, 60 * 10);
options.cookie(c, "state", state || "", 60 * 10);

if (input.callbacks.auth.start) {
await input.callbacks.auth.start(c.req.raw);
}
await next();
});

const options: {
algorithm: AdapterOptions<any>["algorithm"];
success: AdapterOptions<any>["success"];
forward: AdapterOptions<any>["forward"];
cookie: AdapterOptions<any>["cookie"];
} = {
const options: Omit<AdapterOptions<any>, "name"> = {
signing: {
privateKey: jose.importPKCS8(auth.privateKey, "RS512"),
publicKey: jose.importSPKI(auth.publicKey, "RS512"),
},
encryption: {
privateKey: jose.importPKCS8(auth.privateKey, "RSA-OAEP-512"),
publicKey: jose.importSPKI(auth.publicKey, "RSA-OAEP-512"),
},
algorithm: "RS512",
async success(ctx: Context, properties: any) {
const redirect_uri = getCookie(ctx, "redirect_uri");
Expand All @@ -188,7 +127,7 @@ export function AuthHandler<
const token = await new jose.SignJWT(session)
.setProtectedHeader({ alg: "RS512" })
.setExpirationTime("1yr")
.sign(await privateKey);
.sign(await options.signing.privateKey);

deleteCookie(ctx, "provider");
deleteCookie(ctx, "response_type");
Expand All @@ -215,7 +154,7 @@ export function AuthHandler<
})
.setProtectedHeader({ alg: "RS512" })
.setExpirationTime("30s")
.sign(await privateKey);
.sign(await options.signing.privateKey);
const location = new URL(redirect_uri);
location.searchParams.set("code", code);
location.searchParams.set("state", state || "");
Expand Down Expand Up @@ -247,12 +186,73 @@ export function AuthHandler<
},
};

app.get("/token", async (c) => {
const form = await c.req.formData();
if (form.get("grant_type") !== "authorization_code") {
c.status(400);
return c.text("Invalid grant_type");
}
const code = form.get("code");
if (!code) {
c.status(400);
return c.text("Missing code");
}

const { payload } = await jose.jwtVerify(
code as string,
await options.signing.publicKey,
);
if (payload.redirect_uri !== form.get("redirect_uri")) {
c.status(400);
return c.text("redirect_uri mismatch");
}
if (payload.client_id !== form.get("client_id")) {
c.status(400);
return c.text("client_id mismatch");
}

return c.json({
access_token: payload.token,
});
});

app.use("/:provider/authorize", async (c, next) => {
const provider = c.req.param("provider");
const response_type =
c.req.query("response_type") || getCookie(c, "response_type");
const redirect_uri =
c.req.query("redirect_uri") || getCookie(c, "redirect_uri");
const state = c.req.query("state") || getCookie(c, "state");

if (!provider) {
c.status(400);
return c.text("Missing provider");
}

if (!redirect_uri) {
c.status(400);
return c.text("Missing redirect_uri");
}

if (!response_type) {
c.status(400);
return c.text("Missing response_type");
}
options.cookie(c, "provider", provider, 60 * 10);
options.cookie(c, "response_type", response_type, 60 * 10);
options.cookie(c, "redirect_uri", redirect_uri, 60 * 10);
options.cookie(c, "state", state || "", 60 * 10);

if (input.callbacks.auth.start) {
await input.callbacks.auth.start(c.req.raw);
}
await next();
});

for (const [name, value] of Object.entries(input.providers)) {
const route = new Hono();
value(route, {
name,
publicKey,
privateKey,
...options,
});
app.route(`/${name}`, route);
Expand Down

0 comments on commit 45f8987

Please sign in to comment.