Blitz 提供一个可以让你使用已有的 Passport.js 身份验证策略 的适配器。
目前仅支持使用 verify
回调的 passport 策略。在下面的 Twitter 示例中
,TwitterStrategy()
的第二个参数是 verify
回调。
在 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
。
passportAuth
适配器为每个安装的策略添加两个 API 端口。
使用位于 app/api/auth/[...auth].ts
的处理程序,它添加了以下内容:
/api/auth/[strategyName]
- 启动登录的 URL/api/auth/[strategyName]/callback
- 完成登录的回调 URL例如通过 passport-twitter
策略,Twitter 的 URLs 将会为:
/api/auth/twitter
- 启动登录的 URL/api/auth/twitter/callback
- 完成登录的回调 URL你可以通过查找以下内容来确定策略文档中的
strategyName
:passport.authenticate('github')
。所以在这种情况下
,strategyName
是 github
。
你可能需要将 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,
/*...*/
}),
},
],
}))
注意:如果你的环境变量还没类型化,你必须在使用回调时为每个环境变量添加类型 断言(如上所示)。
在 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 @unique
和 name String
。
使用 URL 格式 /api/auth/[strategyName]
添加指向你的应用的链接。
对于上述的 Twitter 示例,相关的链接会像这样:
<a href="/api/auth/twitter">通过 Twitter 登录</a>
与第三方成功认证后,用户将被重定向回上述权限 API 路由上。发生这种情况时,
将调用 verify
回调。
当调用 verify
回调时,用户已通过第三方的身份验证,但尚未为你的 Blitz
应用创建会话。
要创建一个新的 Blitz 会话,你需要从你的 verify
回调中调用 done()
函数。
done(undefined, result)
其中 result
是 VerifyCallbackResult
类型的对象。
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。
redirectUrl
给 verify
回调结果done(undefined, {publicData, redirectUrl: '/'})
redirectUrl
查询参数给“初始化登录” URLexample.com/api/auth/twitter?redirectUrl=/dashboard
example.com/api/auth/twitter?redirectUrl=${router.pathname}
passportAuth
的配置config.successRedirectUrl
config.errorRedirectUrl
/
注意:如果有错误,方法 #1 和 #2 将会覆盖 config.errorRedirectUrl
。
这应该给你最大的灵活性来做你需要的任何事情。如果这不能满足你的需求,请在 Github 上开启 issue!
authenticateOptions
一些策略需要在 passport.authenticate()
方法中调用一个选项,如 scope
或
successMessage
。增加类似如下的选项给 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 })
}
),
},
],
})
注意:没有 authenticateOptions
,verify
函数中的 profile
参数将不会包
含任何值。