OIDC提供商

OIDC提供商插件使您能够构建和管理自己的OpenID Connect (OIDC)提供商,让您完全控制用户身份验证,而无需依赖Okta或Azure AD等第三方服务。它还允许其他服务通过您的OIDC提供商验证用户身份。

主要特性

  • 客户端注册:注册客户端以通过您的OIDC提供商进行身份验证。
  • 动态客户端注册:允许客户端动态注册。
  • 授权码流程:支持授权码流程。
  • JWKS端点:发布JWKS端点,允许客户端验证令牌。(尚未完全实现)
  • 刷新令牌:颁发刷新令牌并使用refresh_token授权处理访问令牌更新。
  • OAuth同意:实现OAuth同意界面用于用户授权,具有为受信任应用程序绕过同意的选项。
  • UserInfo端点:为客户端提供UserInfo端点以获取用户详细信息。

此插件正在积极开发中,可能不适合生产环境使用。请在GitHub上报告任何问题或错误。

安装

挂载插件

将OIDC插件添加到您的auth配置中。请参阅OIDC配置了解如何配置插件。

auth.ts
import { betterAuth } from "better-auth";
import { oidcProvider } from "better-auth/plugins";
 
const auth = betterAuth({
    plugins: [oidcProvider({
        loginPage: "/sign-in", // 登录页面的路径
        // ...其他选项
    })]
})

迁移数据库

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

npx @better-auth/cli migrate

请参阅架构部分手动添加字段。

添加客户端插件

将OIDC客户端插件添加到您的auth客户端配置中。

import { createAuthClient } from "better-auth/client";
import { oidcClient } from "better-auth/client/plugins"
const authClient = createAuthClient({
    plugins: [oidcClient({
        // 您的OIDC配置
    })]
})

使用方法

安装后,您可以使用OIDC提供商来管理应用程序内的身份验证流程。

注册新客户端

要注册新的OIDC客户端,请使用oauth2.register方法。

POST
/oauth2/register
client.ts
const application = await client.oauth2.register({
    name: "My Client",
    redirect_uris: ["https://client.example.com/callback"],
});

创建应用程序后,您将收到可以显示给用户的client_idclient_secret

此端点支持符合RFC7591的客户端注册。

UserInfo端点

OIDC提供商包括一个UserInfo端点,允许客户端检索已认证用户的信息。此端点位于/oauth2/userinfo,需要有效的访问令牌。

GET
/oauth2/userinfo
client-app.ts
// 客户端如何使用UserInfo端点的示例
const response = await fetch('https://your-domain.com/api/auth/oauth2/userinfo', {
  headers: {
    'Authorization': 'Bearer ACCESS_TOKEN'
  }
});
 
const userInfo = await response.json();
// userInfo包含基于授予的作用域的用户详细信息

UserInfo端点根据授权期间授予的作用域返回不同的声明:

  • 使用openid作用域:返回用户的ID(sub声明)
  • 使用profile作用域:返回姓名、头像、名字、姓氏
  • 使用email作用域:返回电子邮件和电子邮件验证状态

getAdditionalUserInfoClaim函数接收用户对象和请求的作用域数组,允许您根据授权期间授予的作用域有条件地包含声明。这些额外的声明将包含在UserInfo端点响应和ID令牌中。

同意界面

当用户被重定向到OIDC提供商进行身份验证时,他们可能会被提示授权应用程序访问他们的数据。这被称为同意界面。默认情况下,Better Auth将显示一个示例同意界面。您可以通过在初始化期间提供consentPage选项来自定义同意界面。

auth.ts
import { betterAuth } from "better-auth";
 
export const auth = betterAuth({
    plugins: [oidcProvider({
        consentPage: "/path/to/consent/page"
    })]
})

插件将把用户重定向到指定的路径,并携带client_idscope查询参数。您可以使用此信息显示自定义同意界面。一旦用户同意,您可以调用oauth2.consent来完成授权。

POST
/oauth2/consent
server.ts
const res = await client.oauth2.consent({
	accept: true, // 或false表示拒绝
});

client_id和其他必要信息存储在浏览器cookie中,因此您不需要在请求中传递它们。如果它们在cookie中不存在,consent方法将返回错误。

处理登录

当用户被重定向到OIDC提供商进行身份验证时,如果他们尚未登录,将被重定向到登录页面。您可以通过在初始化期间提供loginPage选项来自定义登录页面。

auth.ts
import { betterAuth } from "better-auth";
 
export const auth = betterAuth({
    plugins: [oidcProvider({
        loginPage: "/sign-in"
    })]
})

您不需要从您的一方处理任何事情;当创建新会话时,插件将处理继续授权流程。

配置

OIDC元数据

通过在初始化期间提供配置对象来自定义OIDC元数据。

auth.ts
import { betterAuth } from "better-auth";
import { oidcProvider } from "better-auth/plugins";
 
export const auth = betterAuth({
    plugins: [oidcProvider({
        metadata: {
            issuer: "https://your-domain.com",
            authorization_endpoint: "/custom/oauth2/authorize",
            token_endpoint: "/custom/oauth2/token",
            // ...其他自定义元数据
        }
    })]
})

JWKS端点(尚未完全实现)

对于JWKS支持,您需要使用jwt插件。它公开/jwks端点以提供公钥。

目前,令牌是用应用程序的密钥签名的。JWKS端点尚未完全实现。

动态客户端注册

如果您想允许客户端动态注册,可以通过将allowDynamicClientRegistration选项设置为true来启用此功能。

auth.ts
const auth = betterAuth({
    plugins: [oidcProvider({
        allowDynamicClientRegistration: true,
    })]
})

这将允许/register端点公开可用,客户端可以使用它进行注册。

架构

OIDC提供商插件向数据库添加了以下表:

OAuth应用程序

表名:oauthApplication

Field NameTypeKeyDescription
idstringOAuth客户端的数据库ID
clientIdstring每个OAuth客户端的唯一标识符
clientSecretstring-OAuth客户端的密钥
namestring-OAuth客户端的名称
redirectURLsstring-重定向URL的逗号分隔列表
metadatastringOAuth客户端的附加元数据
typestring-OAuth客户端的类型(例如,web,移动)
disabledboolean-指示客户端是否已禁用
userIdstring拥有该客户端的用户的ID。(可选)
createdAtDate-创建OAuth客户端的时间戳
updatedAtDate-上次更新OAuth客户端的时间戳

OAuth访问令牌

表名:oauthAccessToken

Field NameTypeKeyDescription
idstring访问令牌的数据库ID
accessTokenstring-颁发给客户端的访问令牌
refreshTokenstring-颁发给客户端的刷新令牌
accessTokenExpiresAtDate-访问令牌的过期日期
refreshTokenExpiresAtDate-刷新令牌的过期日期
clientIdstringOAuth客户端的ID
userIdstring与令牌关联的用户的ID
scopesstring-授予的作用域的逗号分隔列表
createdAtDate-创建访问令牌的时间戳
updatedAtDate-上次更新访问令牌的时间戳

OAuth同意

表名:oauthConsent

Field NameTypeKeyDescription
idstring同意的数据库ID
userIdstring给予同意的用户的ID
clientIdstringOAuth客户端的ID
scopesstring-同意的作用域的逗号分隔列表
consentGivenboolean-指示是否给予了同意
createdAtDate-给予同意的时间戳
updatedAtDate-上次更新同意的时间戳

选项

allowDynamicClientRegistration: boolean - 启用或禁用动态客户端注册。

metadata: OIDCMetadata - 自定义OIDC提供商元数据。

loginPage: string - 自定义登录页面的路径。

consentPage: string - 自定义同意页面的路径。

getAdditionalUserInfoClaim: (user: User, scopes: string[]) => Record<string, any> - 获取额外用户信息声明的函数。

On this page