Skip to content

Add kid to JWKs #221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions docs/pages/setup/manual.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ const keys = await generateKeyPair("RS256", {
});
const privateKey = await exportPKCS8(keys.privateKey);
const publicKey = await exportJWK(keys.publicKey);
const jwks = JSON.stringify({ keys: [{ use: "sig", ...publicKey }] });
const kid = crypto.randomUUID();
const jwks = JSON.stringify({ keys: [{ use: "sig", kid, ...publicKey }] });

process.stdout.write(
`JWT_PRIVATE_KEY="${privateKey.trimEnd().replace(/\n/g, " ")}"`,
Expand Down Expand Up @@ -127,5 +128,5 @@ export default http;

</Steps>

Continue to [Step 3](/setup#add-authentication-tables-to-your-schema) in
the Setup guide.
Continue to [Step 3](/setup#add-authentication-tables-to-your-schema) in the
Setup guide.
3 changes: 2 additions & 1 deletion src/cli/generateKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export async function generateKeys() {
const keys = await generateKeyPair("RS256");
const privateKey = await exportPKCS8(keys.privateKey);
const publicKey = await exportJWK(keys.publicKey);
const jwks = JSON.stringify({ keys: [{ use: "sig", ...publicKey }] });
const kid = crypto.randomUUID();
const jwks = JSON.stringify({ keys: [{ use: "sig", kid, ...publicKey }] });
return {
JWT_PRIVATE_KEY: `${privateKey.trimEnd().replace(/\n/g, " ")}`,
JWKS: jwks,
Expand Down
20 changes: 18 additions & 2 deletions src/server/implementation/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GenericId } from "convex/values";
import { ConvexAuthConfig } from "../index.js";
import { SignJWT, importPKCS8 } from "jose";
import { SignJWT, importPKCS8, type JWK } from "jose";
import { requireEnv } from "../utils.js";
import { TOKEN_SUB_CLAIM_DIVIDER } from "./utils.js";

Expand All @@ -17,13 +17,29 @@ export async function generateToken(
const expirationTime = new Date(
Date.now() + (config.jwt?.durationMs ?? DEFAULT_JWT_DURATION_MS),
);
const latestJwk = getLatestJwk();
return await new SignJWT({
sub: args.userId + TOKEN_SUB_CLAIM_DIVIDER + args.sessionId,
})
.setProtectedHeader({ alg: "RS256" })
.setProtectedHeader({ alg: "RS256", kid: latestJwk.kid, typ: "JWT" })
.setIssuedAt()
.setIssuer(requireEnv("CONVEX_SITE_URL"))
.setAudience("convex")
.setExpirationTime(expirationTime)
.sign(privateKey);
}

function getLatestJwk() {
try {
const jwksString = requireEnv("JWKS");
const jwks = JSON.parse(jwksString);
// assume the latest JWK is the first one
const latestJwk = jwks["keys"][0];
if (!latestJwk) {
throw new Error("No JWK found");
}
return latestJwk as JWK;
} catch (error) {
throw new Error("Error getting latest JWK", { cause: error });
}
}