JWT

JWT插件提供了获取JWT令牌的端点和用于验证令牌的JWKS端点。

此插件并非用于替代会话。它主要用于那些需要JWT令牌的服务。

安装

将插件添加到您的auth配置中

auth.ts
import { betterAuth } from "better-auth"
import { jwt } from "better-auth/plugins"
 
export const auth = betterAuth({
    plugins: [ 
        jwt(), 
    ] 
})

迁移数据库

运行迁移或生成架构,以向数据库添加必要的字段和表。

npx @better-auth/cli migrate

查看架构部分以手动添加字段。

使用方法

安装插件后,您可以开始使用JWT和JWKS插件通过各自的端点获取令牌和JWKS。

JWT

获取令牌

  1. 使用您的会话令牌

要获取令牌,请调用/token,这将返回以下内容:

  { 
    "token": "ey..."
  }

确保在请求的Authorization头中包含令牌。并且在您的auth配置中添加了bearer插件。

await fetch("/api/auth/token", {
  headers: {
    "Authorization": `Bearer ${token}`
  },
})
  1. set-auth-jwt头获取

当您调用getSession方法时,会在set-auth-jwt头中返回一个jwt。您可以直接将其发送到您的服务。

await authClient.getSession({
  fetchOptions: {
    onSuccess: (ctx)=>{
      const jwt = ctx.response.headers.get("set-auth-jwt")
    }
  }
})

验证令牌

令牌可以在您自己的服务中验证,无需额外的验证调用或数据库检查。 为此使用JWKS,可以从/api/auth/jwks端点获取公钥。

由于此密钥不会频繁更改,因此可以无限期缓存。 用于签署JWT的密钥ID(kid)包含在令牌的头部。 如果收到具有不同kid的JWT,建议再次获取JWKS。

  {
    "keys": [
        {
            "crv": "Ed25519",
            "x": "bDHiLTt7u-VIU7rfmcltcFhaHKLVvWFy-_csKZARUEU",
            "kty": "OKP",
            "kid": "c5c7995d-0037-4553-8aee-b5b620b89b23"
        }
    ]
  }

使用远程JWKS的jose示例

import { jwtVerify, createRemoteJWKSet } from 'jose'
 
async function validateToken(token: string) {
  try {
    const JWKS = createRemoteJWKSet(
      new URL('http://localhost:3000/api/auth/jwks')
    )
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: 'http://localhost:3000', // 应该匹配您的JWT颁发者,即BASE_URL
      audience: 'http://localhost:3000', // 应该匹配您的JWT受众,默认为BASE_URL
    })
    return payload
  } catch (error) {
    console.error('Token validation failed:', error)
    throw error
  }
}
 
// 使用示例
const token = 'your.jwt.token' // 这是您从/api/auth/token端点获取的令牌
const payload = await validateToken(token)

使用本地JWKS的示例

import { jwtVerify, createLocalJWKSet } from 'jose'
 
 
async function validateToken(token: string) {
  try {
    /**
     * 这是您从/api/auth/jwks端点获取的JWKS
     */
    const storedJWKS = {
      keys: [{
        //...
      }]
    };
    const JWKS = createLocalJWKSet({
      keys: storedJWKS.data?.keys!,
    })
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: 'http://localhost:3000', // 应该匹配您的JWT颁发者,即BASE_URL
      audience: 'http://localhost:3000', // 应该匹配您的JWT受众,默认为BASE_URL
    })
    return payload
  } catch (error) {
    console.error('Token validation failed:', error)
    throw error
  }
}
 
// 使用示例
const token = 'your.jwt.token' // 这是您从/api/auth/token端点获取的令牌
const payload = await validateToken(token)

架构

JWT插件向数据库添加了以下表:

JWKS

表名:jwks

Field NameTypeKeyDescription
idstring每个网络密钥的唯一标识符
publicKeystring-网络密钥的公共部分
privateKeystring-网络密钥的私有部分
createdAtDate-创建网络密钥的时间戳

选项

密钥对算法

用于生成密钥对的算法。默认为具有Ed25519曲线的EdDSA。以下是可用选项:

auth.ts
jwt({
  jwks: {
    keyPairConfig: {
      alg: "EdDSA",
      crv: "Ed25519"
    }
  }
})

EdDSA

  • 默认曲线Ed25519
  • 可选属性crv
    • 可用选项:Ed25519Ed448
    • 默认值:Ed25519

ES256

  • 无其他属性

RSA256

  • 可选属性modulusLength
    • 期望一个数字
    • 默认值:2048

PS256

  • 可选属性modulusLength
    • 期望一个数字
    • 默认值:2048

ECDH-ES

  • 可选属性crv
    • 可用选项:P-256P-384P-521
    • 默认值:P-256

ES512

  • 无其他属性

禁用私钥加密

默认情况下,私钥使用AES256 GCM加密。您可以通过将disablePrivateKeyEncryption选项设置为true来禁用此功能。

出于安全原因,建议保持私钥加密。

auth.ts
jwt({
  jwks: {
    disablePrivateKeyEncryption: true
  }
})

修改JWT载荷

默认情况下,整个用户对象都会添加到JWT载荷中。您可以通过向definePayload选项提供函数来修改载荷。

auth.ts
jwt({
  jwt: {
    definePayload: ({user}) => {
      return {
        id: user.id,
        email: user.email,
        role: user.role
      }
    }
  }
})

修改颁发者、受众、主题或过期时间

如果未提供任何内容,则使用BASE_URL作为颁发者,并将受众设置为BASE_URL。过期时间设置为15分钟。

auth.ts
jwt({
  jwt: {
    issuer: "https://example.com",
    audience: "https://example.com",
    expirationTime: "1h",
    getSubject: (session) => {
      // 默认情况下,主题是用户ID
      return session.user.email
    }
  }
})