init test
This commit is contained in:
83
convex/actions/jwt.ts
Normal file
83
convex/actions/jwt.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
'use node';
|
||||
|
||||
import * as jose from "jose";
|
||||
import type { GenericId } from "convex/values";
|
||||
|
||||
// JWT 验证配置
|
||||
const JWT_CONFIG = {
|
||||
// jwksUri: "https://kevisual.cn/root/convex/jwks.json",
|
||||
jwksUri: "http://convex.kevisual.cn:3211/root/convex/jwks.json",
|
||||
issuer: "https://kevisual.cn",
|
||||
audience: "test-convex-app",
|
||||
algorithms: ["RS256"],
|
||||
};
|
||||
|
||||
// 创建 JWKS 缓存
|
||||
let jwksCache: jose.JWTVerifyGetKey | null = null;
|
||||
|
||||
function getJWKS() {
|
||||
if (!jwksCache) {
|
||||
jwksCache = jose.createRemoteJWKSet(new URL(JWT_CONFIG.jwksUri));
|
||||
}
|
||||
return jwksCache;
|
||||
}
|
||||
|
||||
export const createJwt = async (payload: jose.JWTPayload): Promise<string> => {
|
||||
const secret = new TextEncoder().encode("your-256-bit-secret");
|
||||
const token = await new jose.SignJWT(payload)
|
||||
.setProtectedHeader({ alg: "HS256", typ: "JWT" })
|
||||
.setIssuedAt()
|
||||
.setExpirationTime("2h")
|
||||
.sign(secret);
|
||||
return token;
|
||||
}
|
||||
// 验证 JWT token 并返回 payload
|
||||
export async function verifyToken(token: string): Promise<jose.JWTPayload> {
|
||||
const JWKS = getJWKS();
|
||||
const { payload } = await jose.jwtVerify(token, JWKS, {
|
||||
issuer: JWT_CONFIG.issuer,
|
||||
audience: JWT_CONFIG.audience,
|
||||
algorithms: JWT_CONFIG.algorithms,
|
||||
});
|
||||
return payload;
|
||||
}
|
||||
|
||||
// 从请求 header 中提取 Bearer token
|
||||
export function extractBearerToken(authHeader: string | null): string | null {
|
||||
if (!authHeader) return null;
|
||||
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
// 获取或创建用户(演示用)
|
||||
export async function getOrCreateUser(
|
||||
db: any,
|
||||
tokenPayload: jose.JWTPayload
|
||||
): Promise<GenericId<"users">> {
|
||||
const externalId = tokenPayload.sub as string;
|
||||
|
||||
// 查找已存在的用户
|
||||
const existingUser = await db
|
||||
.query("users")
|
||||
.withIndex("id", (q: any) => q.eq("id", externalId))
|
||||
.first();
|
||||
|
||||
if (existingUser) {
|
||||
// 更新最后登录时间
|
||||
await db
|
||||
.table("users")
|
||||
.doc(existingUser._id)
|
||||
.patch({ lastLoginAt: new Date().toISOString() });
|
||||
return existingUser._id;
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
return await db
|
||||
.table("users")
|
||||
.insert({
|
||||
id: externalId,
|
||||
name: (tokenPayload.name as string) || "Unknown",
|
||||
email: (tokenPayload.email as string) || "",
|
||||
createdAt: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user