为你的 Next Auth 应用添加认证 (Authentication)
本指南将向你展示如何将 Logto 集成到你的 Next.js 应用中,使用 Next Auth。
- 在本指南中,我们假设你已经在你的 Next.js 项目中设置了 Next Auth。如果没有,请查看 Next Auth 文档 以开始使用。
前提条件
- 一个 Logto Cloud 账户或一个 自托管 Logto。
- 一个已创建的 Logto 传统应用。
- 一个带有 Next Auth 的 Next.js 项目,查看 Next Auth 文档。
集成
配置 Next Auth 提供者
在我们深入细节之前,这里是终端用户体验的快速概述。登录过程可以简化如下:
- 你的应用调用登录方法。
- 用户被重定向到 Logto 登录页面。对于原生应用,将打开系统浏览器。
- 用户登录并被重定向回你的应用(配置为重定向 URI)。
关于基于重定向的登录
- 此认证 (Authentication) 过程遵循 OpenID Connect (OIDC) 协议,Logto 强制执行严格的安全措施以保护用户登录。
- 如果你有多个应用程序,可以使用相同的身份提供商 (IdP)(日志 (Logto))。一旦用户登录到一个应用程序,当用户访问另一个应用程序时,Logto 将自动完成登录过程。
要了解有关基于重定向的登录的原理和好处的更多信息,请参阅 Logto 登录体验解释。
在以下代码片段中,我们假设你的应用程序运行在 http://localhost:3000/
。
配置登录重定向 URI
让我们切换到 Logto Console 的应用详情页面。添加一个重定向 URI http://localhost:3000/api/auth/callback/logto
并点击“保存更改”。
设置 Next Auth 提供商
你可以在管理控制台的应用详情页面找到并复制“应用密钥”:
修改你的 Next Auth 的 API 路由配置,如果你使用的是 Pages Router,文件位于 pages/api/auth/[...nextauth].js
,如果你使用的是 App Router,文件位于 app/api/auth/[...nextauth]/route.ts
。
以下是 App Router 的示例:
- Next Auth v5
- Next Auth v4
import NextAuth from 'next-auth';
export const {
handlers: { GET, POST },
signIn,
signOut,
auth,
} = NextAuth({
providers: [
{
id: 'logto',
name: 'Logto',
type: 'oidc',
// 你可以从 Logto 应用详情页面的 "Issuer endpoint" 字段获取发行者 (Issuer) 值
issuer: 'https://xxxx.logto.app/oidc',
clientId: '<logto-app-id>',
clientSecret: '<logto-app-secret>',
authorization: {
params: { scope: 'openid offline_access profile email' },
},
profile(profile) {
// 你可以在这里自定义用户资料映射
return {
id: profile.sub,
name: profile.name ?? profile.username,
email: profile.email,
image: profile.picture,
};
},
},
],
});
- 将
issuer
URL 替换为你的 Logto 应用的 "Issuer endpoint"。 - 将
clientId
和clientSecret
替换为你的 Logto 应用的 ID 和密钥。 - 自定义
profile
函数以将用户资料映射到 Next Auth 用户对象,示例中显示了默认映射。
import NextAuth from 'next-auth';
const handler = NextAuth({
providers: [
{
id: 'logto',
name: 'Logto',
type: 'oauth',
// 你可以从 Logto 应用详情页面的 "OpenID Provider configuration endpoint" 字段获取 well-known URL
wellKnown: 'https://xxxx.logto.app/oidc/.well-known/openid-configuration',
authorization: { params: { scope: 'openid offline_access profile email' } },
clientId: '<logto-app-id>',
clientSecret: '<logto-app-secret>',
client: {
id_token_signed_response_alg: 'ES384',
},
profile(profile) {
// 你可以在这里自定义用户资料映射
return {
id: profile.sub,
name: profile.name ?? profile.username,
email: profile.email,
image: profile.picture,
};
},
},
],
});
export { handler as GET, handler as POST };
- 将
wellKnown
URL 替换为你的 Logto 应用的 "OpenID Provider configuration endpoint"。 - 将
clientId
和clientSecret
替换为你的 Logto 应用的 ID 和密钥。 - 自定义
profile
函数以将用户资料映射到 Next Auth 用户对象,示例中显示了默认映射。 - 记得将
id_token_signed_response_alg
设置为ES384
。
检查点
现在,你可以测试你的应用,看看认证 (Authentication) 是否按预期工作。
权限 (Scopes) 和声明 (Claims)
Logto 使用 OIDC 权限和声明约定 来定义从 ID 令牌和 OIDC 用户信息端点检索用户信息的权限和声明。“权限”和“声明”都是 OAuth 2.0 和 OpenID Connect (OIDC) 规范中的术语。
简而言之,当你请求一个权限时,你将获得用户信息中的相应声明。例如,如果你请求 `email` 权限,你将获得用户的 `email` 和 `email_verified` 数据。
默认情况下,Logto SDK 总是会请求三个权限:`openid`、`profile` 和 `offline_access`,并且无法移除这些默认权限。但你可以在配置 Logto 时添加更多权限:
const handler = NextAuth({
providers: [
{
id: 'logto',
name: 'Logto',
// ... other options
authorization: { params: { scope: 'openid offline_access profile email' } },
// ... other options
},
],
});
以下是支持的权限(Scopes)及其对应的声明(Claims)列表:
openid
声明名称 | 类型 | 描述 | 需要用户信息吗? |
---|---|---|---|
sub | string | 用户的唯一标识符 | 否 |
profile
声明名称 | 类型 | 描述 | 需要用户信息吗? |
---|---|---|---|
name | string | 用户的全名 | 否 |
username | string | 用户名 | 否 |
picture | string | 终端用户的个人资料图片的 URL。此 URL 必须指向一个图像文件(例如,PNG、JPEG 或 GIF 图像文件),而不是包含图像的网页。请注意,此 URL 应特别引用适合在描述终端用户时显示的终端用户的个人资料照片,而不是终端用户拍摄的任意照片。 | 否 |
created_at | number | 终端用户创建的时间。时间表示为自 Unix 纪元(1970-01-01T00:00:00Z)以来的毫秒数。 | 否 |
updated_at | number | 终端用户信息最后更新的时间。时间表示为自 Unix 纪元(1970-01-01T00:00:00Z)以来的毫秒数。 | 否 |
其他 标准声明 包括 family_name
、given_name
、middle_name
、nickname
、preferred_username
、profile
、website
、gender
、birthdate
、zoneinfo
和 locale
也将包含在 profile
权限中,而无需请求用户信息端点。与上述声明的区别在于,这些声明只有在其值不为空时才会返回,而上述声明在值为空时将返回 null
。
与标准声明不同,created_at
和 updated_at
声明使用毫秒而不是秒。
email
声明名称 | 类型 | 描述 | 需要用户信息吗? |
---|---|---|---|
string | 用户的电子邮件地址 | 否 | |
email_verified | boolean | 电子邮件地址是否已验证 | 否 |
phone
声明名称 | 类型 | 描述 | 需要用户信息吗? |
---|---|---|---|
phone_number | string | 用户的电话号码 | 否 |
phone_number_verified | boolean | 电话号码是否已验证 | 否 |
address
请参阅 OpenID Connect Core 1.0 以获取地址声明的详细信息。
custom_data
声明名称 | 类型 | 描述 | 需要用户信息吗? |
---|---|---|---|
custom_data | object | 用户的自定义数据 | 是 |
identities
声明名称 | 类型 | 描述 | 需要用户信息吗? |
---|---|---|---|
identities | object | 用户的关联身份 | 是 |
sso_identities | array | 用户的关联 SSO 身份 | 是 |
urn:logto:scope:organizations
声明名称 | 类型 | 描述 | 需要用户信息吗? |
---|---|---|---|
organizations | string[] | 用户所属的组织 ID | 否 |
organization_data | object[] | 用户所属的组织数据 | 是 |
urn:logto:scope:organization_roles
声明名称 | 类型 | 描述 | 需要用户信息吗? |
---|---|---|---|
organization_roles | string[] | 用户所属的组织角色,格式为 <organization_id>:<role_name> | 否 |
考虑到性能和数据大小,如果“需要用户信息吗?”为“是”,则表示声明不会显示在 ID 令牌中,而会在 用户信息端点 响应中返回。