Blitz 尚在 beat 阶段! 🎉 预计会在今年的 Q3 季度发布 1.0
Back to Documentation Menu

通过 Passport.js 来第三方登录

Topics

Jump to a Topic

Blitz 提供一个可以让你使用已有的 Passport.js 身份验证策略 的适配器。

目前仅支持使用 verify 回调的 passport 策略。在下面的 Twitter 示例中 ,TwitterStrategy() 的第二个参数是 verify 回调。

设置

1. 增加 Passport.js API 路由

app/api/auth/[...auth].ts 中添加一个新的 api 路由,内容如下:

// app/api/auth/[...auth].ts
import { passportAuth } from "blitz"
import db from "db"

export default passportAuth({
  successRedirectUrl: "/",
  errorRedirectUrl: "/",
  strategies: [
    {
      strategy: new PassportStrategy(), // 提供初始化的 passport 策略
    },
  ],
})

如果你需要,你可以将 api 路由替换为一个不同的路径,但是文件名必须为 [...auth].js[...auth].ts

URLs

passportAuth 适配器为每个安装的策略添加两个 API 端口。

使用位于 app/api/auth/[...auth].ts 的处理程序,它添加了以下内容:

  1. /api/auth/[strategyName] - 启动登录的 URL
  2. /api/auth/[strategyName]/callback - 完成登录的回调 URL

例如通过 passport-twitter 策略,Twitter 的 URLs 将会为:

  1. /api/auth/twitter - 启动登录的 URL
  2. /api/auth/twitter/callback - 完成登录的回调 URL

你可以通过查找以下内容来确定策略文档中的 strategyNamepassport.authenticate('github')。所以在这种情况下 ,strategyNamegithub

SSL 代理配置

你可能需要将 secureProxy 选项设置为 true,以防你的应用位于 SSL 代理 (Nginx)后面。代理应正确地设置来管理 x-forwarded-proto header。

// app/api/auth/[...auth].ts
import { passportAuth } from "blitz"
import db from "db"

export default passportAuth({
  successRedirectUrl: "/",
  errorRedirectUrl: "/",
secureProxy: true,
strategies: [ /*...*/ ], })

访问中间件内容

你可以通过给 passportAuth 适配器提供一个回调来访问中间件上下文。回调的第 一个参数是 ctx。你可以通过 ctx.session 来访问这个会话上下文。

// app/api/auth/[...auth].ts
import { passportAuth } from "blitz"
import db from "db"

export default passportAuth((ctx) => ({
  successRedirectUrl: "/",
  errorRedirectUrl: "/",
  strategies: [
    {
      strategy: new TwitterStrategy({
        consumerKey: process.env.TWITTER_CONSUMER_KEY as string,
        consumerSecret: process.env.TWITTER_CONSUMER_SECRET as string,
        /*...*/
      }),
    },
  ],
}))

注意:如果你的环境变量还没类型化,你必须在使用回调时为每个环境变量添加类型 断言(如上所示)。

2. 增加一个 Passport 策略

在 API 路由中为 strategies 数组参数添加一个策略,然后按照该策略的文档进 行设置。

例如如下添加 passport-twitter 的示例:

注意 callbackURL 使用上述回调端口(/api/auth/twitter/callback

import { passportAuth } from "blitz"
import db from "db"
import { Strategy as TwitterStrategy } from "passport-twitter"

export default passportAuth({
  successRedirectUrl: "/",
  errorRedirectUrl: "/",
  strategies: [
{ strategy: new TwitterStrategy( { consumerKey: process.env.TWITTER_CONSUMER_KEY, consumerSecret: process.env.TWITTER_CONSUMER_SECRET, callbackURL: process.env.NODE_ENV === "production" ? "https://example.com/api/auth/twitter/callback" : "http://localhost:3000/api/auth/twitter/callback", includeEmail: true, }, async function (_token, _tokenSecret, profile, done) { const email = profile.emails && profile.emails[0]?.value if (!email) { // This can happen if you haven't enabled email access in your twitter app permissions return done( new Error("Twitter OAuth response doesn't have email.") ) } const user = await db.user.upsert({ where: { email }, create: { email, name: profile.displayName, }, update: { email }, }) const publicData = { userId: user.id, roles: [user.role], source: "twitter", } done(undefined, { publicData }) } ), },
], })

注意:上述的 passport-twitter 示例要求你的 User prisma 模型具有 email String @uniquename String

3. 通过这个 Passport 策略登录

使用 URL 格式 /api/auth/[strategyName] 添加指向你的应用的链接。

对于上述的 Twitter 示例,相关的链接会像这样:

<a href="/api/auth/twitter">通过 Twitter 登录</a>

详细使用说明

与第三方成功认证后,用户将被重定向回上述权限 API 路由上。发生这种情况时, 将调用 verify 回调。

当调用 verify 回调时,用户已通过第三方的身份验证,但尚未为你的 Blitz 应用创建会话

创建一个会话

要创建一个新的 Blitz 会话,你需要从你的 verify 回调中调用 done() 函数。

done(undefined, result)

其中 resultVerifyCallbackResult 类型的对象。

export type VerifyCallbackResult = {
  publicData: PublicData
  privateData?: Record<string, any>
  redirectUrl?: string
}

Blitz 适配器将接着为你调用 session.$create(),并且将用户重定向到应用中正 确的地方。

返回一个错误

如果相反,由于某些错误,你想要阻止创建会话,则调用 done() 并将错误作为第 一个参数。然后用户将被重定向回正确的位置。

return done(new Error("it broke"))

向用户显示错误

此过程中的任何错误都将作为 authError 查询参数提供。

例如,使用 errorRedirectUrl = '/'done(new Error("it broke")),用户 将被重定向到:

/?authError=it broke

身份验证后重定向

有四种不同的方法可以确定用户在通过身份验证后应该被发送到的重定向 URL。它们 在此处按优先级列出。与方法 #1 一起提供的 URL 将覆盖所有其它 URL。

  1. 增加 redirectUrlverify 回调结果
    • 示例:done(undefined, {publicData, redirectUrl: '/'})
  2. 增加一个 redirectUrl 查询参数给“初始化登录” URL
    • 例如:example.com/api/auth/twitter?redirectUrl=/dashboard
    • 例如:example.com/api/auth/twitter?redirectUrl=${router.pathname}
  3. 通过传递给 passportAuth 的配置
    • 如果成功,将会使用 config.successRedirectUrl
    • 如果失败,将会使用 config.errorRedirectUrl
  4. 如果上述任何没有被提供,将会重定向到 /

注意:如果有错误,方法 #1 和 #2 将会覆盖 config.errorRedirectUrl

这应该给你最大的灵活性来做你需要的任何事情。如果这不能满足你的需求,请在 Github 上开启 issue!

authenticateOptions

一些策略需要在 passport.authenticate() 方法中调用一个选项,如 scopesuccessMessage。增加类似如下的选项给 passportAuth 对象:

import { passportAuth } from "blitz"
import db from "db"
import { Strategy as Auth0Strategy } from "passport-auth0"

export default passportAuth({
  successRedirectUrl: "/",
  errorRedirectUrl: "/",
  strategies: [
    {
authenticateOptions: { scope: "openid email profile" },
strategy: new Auth0Strategy( { domain: process.env.AUTH0_DOMAIN, clientID: process.env.AUTH0_CLIENT_ID, clientSecret: process.env.AUTH0_CLIENT_SECRET, callbackURL: process.env.NODE_ENV === "production" ? "https://example.com/api/auth/auth0/callback" : "http://localhost:3000/api/auth/auth0/callback", }, async function ( _token, _tokenSecret, extraParams, profile, done ) { const email = profile.emails && profile.emails[0]?.value if (!email) { // 如果你没有在你的 Twitter 应用中开启邮箱访问权限后会出现 return done( new Error("GitHub OAuth response doesn't have email.") ) } const user = await db.user.upsert({ where: { email }, create: { email, name: profile.displayName, }, update: { email }, }) const publicData = { userId: user.id, roles: [user.role], source: "auth0", } done(undefined, { publicData }) } ), }, ], })

注意:没有 authenticateOptionsverify 函数中的 profile 参数将不会包 含任何值。


Idea for improving this page? Edit it on GitHub.