@ fastify/oaut2 (法语)
环绕 simple-oauth2 图书馆.
本模块的 v4. x 支持快取 v3. x 本模块的 v3. x 支持 Fastify v2.x
安装
npm i @fastify/oauth2
使用量
在使用快取-oauth2模块时需要创建两个独立的端点,一个用于OAuth2服务提供商的回调(如Facebook或Discord),另一个用于初始化OAuth2登录流量.
const fastify = require('fastify')({ logger: { level: 'trace' } })
const oauthPlugin = require('@fastify/oauth2')
fastify.register(oauthPlugin, {
name: 'facebookOAuth2',
credentials: {
client: {
id: '<CLIENT_ID>',
secret: '<CLIENT_SECRET>'
},
auth: oauthPlugin.FACEBOOK_CONFIGURATION
},
// register a fastify url to start the redirect flow to the service provider's OAuth2 login
startRedirectPath: '/login/facebook',
// service provider redirects here after user login
callbackUri: 'http://localhost:3000/login/facebook/callback'
// You can also define callbackUri as a function that takes a FastifyRequest and returns a string
// callbackUri: req => `${req.protocol}://${req.hostname}/login/facebook/callback`,
})
// This is the new endpoint that initializes the OAuth2 login flow
// This endpoint is only required if startRedirectPath has not been provided
fastify.get('/login/facebook', {}, (req, reply) => {
fastify.facebookOAuth2.generateAuthorizationUri(
req,
reply,
(err, authorizationEndpoint) => {
if (err) console.error(err)
reply.redirect(authorizationEndpoint)
}
);
});
// The service provider redirect the user here after successful login
fastify.get('/login/facebook/callback', async function (request, reply) {
const { token } = await this.facebookOAuth2.getAccessTokenFromAuthorizationCodeFlow(request, reply)
console.log(token.access_token)
// if later need to refresh the token this can be used
// const { token: newToken } = await this.getNewAccessTokenUsingRefreshToken(token)
reply.send({ access_token: token.access_token })
})
简言之,必须首先航行到 /login/facebook 在网页浏览器中手动结束点 。 这将重定向到 OAuth2 服务提供商的登录屏幕 。 从那里,服务商会自动重定向回 /login/facebook/callback 用于获取和使用访问符的终点。 这个 CLIENT_ID 财务报告和审定财务报表 CLIENT_SECRET 需要用服务提供商提供的替代.
提供了完整的示例 固定- 分解- oaut2 实例
用法为 @fastify/cookie
从v7.2.0起, @fastify/oauth2 需要使用 cookie 来安全地执行 OAuth2 交换。 因此,如果你需要的话 @fastify/cookie 你自己来,
您需要注册 在此之前 @fastify/oauth2。 。 。 .
const fastify = require('fastify')({ logger: { level: 'trace' } })
const oauthPlugin = require('@fastify/oauth2')
fastify.register(require('@fastify/cookie'), cookieOptions)
fastify.register(oauthPlugin, oauthOptions)
饼干是默认的 httpOnly, (中文(简体) ), sameSite: Lax, (中文(简体) ), path: '/'如果这不适合您的使用大小写,则可以通过在配置对象中提供选项来覆盖默认的 cookie 设置,例如
fastify.register(oauthPlugin, {
...,
cookie: {
sameSite: 'none'
}
})
由旧版本创建但没有明确内容的饼干 path 在浏览器会话结束之前,可能仍停留在更窄的浏览器衍生路径下.
以默认饼干名称注册的多家OAuth提供商共享相同的状态和验证器饼干. 如果用户可以并行运行不止一个提供者的登录流量, 设置区分 redirectStateCookieName 财务报告和审定财务报表 verifierCookieName 每个提供者的值.
这些 cookie 的默认值是 oauth2-code-verifier 页:1 verifierCookieName 财务报告和审定财务报表 oauth2-redirect-state 页:1 redirectStateCookieName。 。 。 .
fastify.register(oauthPlugin, {
...,
redirectStateCookieName: 'custom-redirect-state',
verifierCookieName: 'custom-code-verifier'
})
已签名的饼干
cookie: { signed: true } 支持: 状态和验证 cookie 未签名
在比较状态和将核查者送到符号终点之前。 A 类
其签名无法验证的饼干被丢弃, 而不是作为实际使用 .
签名只有在请求通过 options.cookie。如果
注册 @fastify/cookie {\fn黑体\fs20\shad2\2ah82\3ah20\4ah33\fscx95\3ch592001\be1}你对你的爱 parseOptions: { signed: true },该设置
已经 没有 沉默地延伸至这两个饼干,因为插件必须知道是否
{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}去签它所写的.
请注意,签名不是保护这些饼干的手段. 无法阻止攻击者 从植入状态,因为一个有效的签名状态可以简单地从自己的状态中获取 登录端点。 如果符合常规,就使用它,而不是作为安全控制.
hostPrefixedCookies
hostPrefixedCookies: true 将两个默认的饼干名称切换到它们的
__Host- 前缀表格, __Host-oauth2-redirect-state 财务报告和审定财务报表
__Host-oauth2-code-verifier数字 :
fastify.register(oauthPlugin, {
...,
hostPrefixedCookies: true
})
默认为 false,因为前缀名称需要为应用程序服务
超过HTTPS并默认允许它们会中断非部署. 你个
应该打开它. 它只选择默认值——一个明确的
redirectStateCookieName 或者说 verifierCookieName 总是赢.
当你应该打开它
随时打开 在您可注册的域下的任何主机都位于您的完整之外
控制。饼干是按主机而不是原产地设定的:
blog.example.com 与 Domain=example.com 发送到 app.example.com因此,一个XSS上
一个营销子域,一个子域收购,一个正在移动的CNAME,一个合伙人运营
子域, 或一个被遗忘的中框, 足以让人写
oauth2-redirect-state 财务报告和审定财务报表 oauth2-code-verifier 您的应用程序主机.
这很重要,因为默认状态检查比较 state 查询参数为
州饼干。 一个人谁能写那两个饼干 可以种植一个 state 常设专家委员会
他们自己的核查员,然后让受害人的浏览器完成一个带有
授权代码 攻击者 身份——将受害人记录在
攻击者的账户,在那里他们后来所做的一切记录。 这是登录CSRF;
它不会暴露受害人自己的账户或信物.
cookie: { secure: true } 并不会阻止它。 服务器只接收 cookie 名称和
值,无法分辨在存储饼干时存在哪些属性,所以
写在非安全上下文的饼干 和你设定的饼干是分不开的.
A 类 __Host- 饼干只能由安全的源头编写,只由主机编写,所以没有
兄妹子域 或响应 HTTP 请求的政党可以创建您的主机
一个 如果您运行一个完全没有子域的单一主机名, 则旗是防御深度
而不是解决一个可以解决的问题,但促成这种问题没有代价.
这需要什么
浏览器 必须在 HTTPS 上找到您的申请。 重要的是 URL 在其中
地址栏,而不是节点本身服务的内容: Secure 由服务器发射,但
由浏览器执行, 因此 在反向代理中终止 TLS 并运行快取
plain HTTP 完全一样。无需 https 选项
实例,您不需要 trustProxy。 。 。 .
浏览器也处理 http://localhost 作为安全的环境, 所以地方发展工作
不变.
旗帜只有在浏览器真正到达应用程序时无法使用
在非浏览主机名上的 HTTP 。 这里的饼干完全无法存储, 每次登录
失败 Invalid state; 保留旗帜,或明确设置未前缀名称.
一般前缀处理
当饼干名称开始 __Host- 或者说 __Secure- - 无论是从这个旗帜还是从
你给自己定了一个名字.. @fastify/oauth2 生成前缀需要的属性
从名字,压倒任何 options.cookie 说: __Host- 部队 secure: true
财务报告和审定财务报表 path: '/' 并删除 domain, (中文(简体) ), __Secure- 部队 secure: true。前缀是
匹配不敏感大小写, 因为浏览器匹配它们 。 浏览器静悄悄地放下一个
属性与它的前缀相矛盾,所以推导它们就是使流更能运转的
而不是失败 Invalid state。 。 。 .
预设配置
您可以选择一些默认设置来指定 auth 选项.
APPLE_CONFIGURATIONFACEBOOK_CONFIGURATIONGITHUB_CONFIGURATIONGITLAB_CONFIGURATIONLINKEDIN_CONFIGURATIONGOOGLE_CONFIGURATIONMICROSOFT_CONFIGURATIONVKONTAKTE_CONFIGURATIONSPOTIFY_CONFIGURATIONDISCORD_CONFIGURATIONTWITCH_CONFIGURATIONVATSIM_CONFIGURATIONVATSIM_DEV_CONFIGURATIONEPIC_GAMES_CONFIGURATIONYANDEX_CONFIGURATIONX_CONFIGURATION
自定义配置
当然,如果一个预设不在我们的模块中,您可以自己设定ODATH的终点:
fastify.register(oauthPlugin, {
name: 'customOauth2',
credentials: {
client: {
id: '<CLIENT_ID>',
secret: '<CLIENT_SECRET>'
},
auth: {
authorizeHost: 'https://my-site.com',
authorizePath: '/authorize',
tokenHost: 'https://token.my-site.com',
tokenPath: '/api/token'
}
},
startRedirectPath: '/login',
callbackUri: 'http://localhost:3000/login/callback',
callbackUriParams: {
exampleParam: 'example param value'
}
})
使用自动发现终点
当您的提供者支持 OpenID 连接发现时, 您想要自动配置授权、 令牌和撤销端点,
然后你可以使用发现选项.
discovery 是一个简单的对象,需要 issuer 属性.
发行者预计为字符串 URL 或元数据 URL. 支持有或无有后端斜线的备选案文.
你可以看见更多 示例。 。 。 .
fastify.register(oauthPlugin, {
name: 'customOAuth2',
scope: ['profile', 'email'],
credentials: {
client: {
id: '<CLIENT_ID>',
secret: '<CLIENT_SECRET>',
},
// Note how "auth" is not needed anymore when discovery is used.
},
startRedirectPath: '/login',
callbackUri: 'http://localhost:3000/callback',
discovery: { issuer: 'https://identity.mycustomdomain.com' }
// pkce: 'S256', you can still do this explicitly, but since discovery is used,
// it's BEST to let plugin do it itself
// based on what Authorization Server Metadata response
});
重要发现说明:
- 你不应该设置
credentials.auth当发现力学被使用时. - 当您的提供者支持时, 插件也会在授权代码授权中选择适当的 PKCE 方法
- 如果你仍然想要自己选择方法,并确切地知道自己在做什么;你仍然可以明确地这样做.
Schema 配置
您可以指定您自己的计划 startRedirectPath 结束点。 它允许您在使用 @fastify/swagger 一起来.
说明: schema 选项将覆盖 tags 选项而不合并它们.
fastify.register(oauthPlugin, {
name: 'facebookOAuth2',
credentials: {
client: {
id: '<CLIENT_ID>',
secret: '<CLIENT_SECRET>'
},
auth: oauthPlugin.FACEBOOK_CONFIGURATION
},
// register a fastify url to start the redirect flow
startRedirectPath: '/login/facebook',
// facebook redirect here after the user login
callbackUri: 'http://localhost:3000/login/facebook/callback',
// add tags for the schema
tags: ['facebook', 'oauth2'],
// add schema
schema: {
tags: ['facebook', 'oauth2'] // this will take the precedence
}
})
设置自定义状态
默认 @fastify/oauth2 生成随机 state把它储存在饼干里,然后在
callback 在 state 查询参数等于饼干值。 这个
是一个双向提交 cookie: 它证明了两个值匹配, 而不是同一浏览器启动
流动。 阻止另一个派对 写这些饼干的第一是
hostPrefixedCookies,你应当启用,除非你被钉住
到普通的 HTTP; 一旦调用, 状态和核查 cookie 也会被清除
已处理, 因此无法重放状态 .
为了更强有力的保证, 将状态绑定在启动流量的浏览器的会话中
在召回时消耗它. generateStateFunction 接受一个函数生成
state aWATH 流的参数。 此函数收到快取实例 request
作为参数的对象。 这个 state 参数也将被设置为 httpOnly, (中文(简体) ),
sameSite: Lax 饼干.
当设置时,它需要提供功能 checkStateFunction 为了验证生成的状态.
fastify.register(oauthPlugin, {
name: 'facebookOAuth2',
credentials: {
client: {
id: '<CLIENT_ID>',
secret: '<CLIENT_SECRET>'
},
auth: oauthPlugin.FACEBOOK_CONFIGURATION
},
// register a fastify url to start the redirect flow
startRedirectPath: '/login/facebook',
// facebook redirect here after the user login
callbackUri: 'http://localhost:3000/login/facebook/callback',
// custom function to generate the state
generateStateFunction: (request) => {
const state = request.query.customCode
request.session.state = state
return state
},
// custom function to check the state is valid
checkStateFunction: (request, callback) => {
if (request.query.state === request.session.state) {
callback()
return
}
callback(new Error('Invalid state'))
}
})
这里支持Async函数,并可以通过 this。 。 。 .
fastify.register(oauthPlugin, {
name: 'facebookOAuth2',
credentials: {
client: {
id: '<CLIENT_ID>',
secret: '<CLIENT_SECRET>'
},
auth: oauthPlugin.FACEBOOK_CONFIGURATION
},
// register a fastify url to start the redirect flow
startRedirectPath: '/login/facebook',
// facebook redirect here after the user login
callbackUri: 'http://localhost:3000/login/facebook/callback',
// custom function to generate the state and store it into the redis
generateStateFunction: async function (request) {
const state = request.query.customCode
await this.redis.set(stateKey, state)
return state
},
// custom function to check the state is valid
checkStateFunction: async function (request, callback) {
if (request.query.state !== request.session.state) {
throw new Error('Invalid state')
}
return true
}
})
设置自定义调用参数
这个 callbackUriParams 接受一个对象,该对象将被翻译为查询参数,用于调回的 OWATH 流量。 默认值为QQ.
fastify.register(oauthPlugin, {
name: 'googleOAuth2',
scope: ['profile', 'email'],
credentials: {
client: {
id: '<CLIENT_ID>',
secret: '<CLIENT_SECRET>',
},
auth: oauthPlugin.GOOGLE_CONFIGURATION,
},
startRedirectPath: '/login/google',
callbackUri: 'http://localhost:3000/login/google/callback',
callbackUriParams: {
// custom query param that will be passed to callbackUri
access_type: 'offline', // will tell Google to send a refreshToken too
},
pkce: 'S256'
// check if your provider supports PKCE,
// in case they do,
// use of this parameter is highly encouraged
// in order to prevent authorization code interception attacks
});
设置自定义符号请求正文参数
这个 tokenRequestParams 参数接受将翻译为 POST 正文中的额外参数的对象
在通过服务标志端点请求访问符时.
实例
见 example/ 更多示例的文件夹.
参考文献
此快速化插件将自定义实例与 simple-oauth2
实例 命名空间 由财产指定 name 有和没有 oauth2 前缀.
例如,用于 name: 'customOauth2',则 simple-oauth2 实例将像这样可以访问 :
fastify.oauth2CustomOauth2.oauth2 财务报告和审定财务报表 fastify.customOauth2.oauth2
这样,我们可以注册多个 OAuth 供应商和每个 OAuth 供应商 simple-oauth2 将活在自己 命名空间。 。 。 .
举个例子.
fastify.oauth2Facebook.oauth2fastify.oauth2Github.oauth2fastify.oauth2Spotify.oauth2fastify.oauth2Vkontakte.oauth2
假设我们已经注册了多个 OAuth 供应商:
fastify.register(oauthPlugin, { name: 'facebook', { ... } // facebooks credentials, startRedirectPath, callbackUri etc )fastify.register(oauthPlugin, { name: 'github', { ... } // githubs credentials, startRedirectPath, callbackUri etc )fastify.register(oauthPlugin, { name: 'spotify', { ... } // spotifys credentials, startRedirectPath, callbackUri etc )fastify.register(oauthPlugin, { name: 'vkontakte', { ... } // vkontaktes credentials, startRedirectPath, callbackUri etc )
公用事业
此加固插件为您的加固实例添加了 6 个功能装饰器 命名空间数字 :
-
getAccessTokenFromAuthorizationCodeFlow(request, callback): 使用授权代码流来取取 OAuth2 令牌的函数,它使用该流的最后一个请求中的数据. 如果召回没有通过,它会返回一个承诺。 回调或承诺解析返回 访问脚本 对象,该对象具有AccessToken.token带有下列密钥的属性:access_tokenrefresh_token(可选,只有在offline scope最初请求,如回调UriParams的例子所示)token_type(一般为:'Bearer'(中文(简体) )expires_in(令牌过期的秒数,例如.240000(中文(简体) )
-
或
getAccessTokenFromAuthorizationCodeFlow(request, reply, callback)有3个参数的变体,这是PKCE扩展使用时建议的形式和要求. 通过reply允许快取-oaut2删除状态和 PKCE 代码 验证器 cookie,这样,如果服务器在取出令牌时有问题,那么它们就不会留在浏览器中,这样已经到达回调状态就不能被重放. 见 以 PKCE 为例 Google 更多。 。 。 .必须指出: 如果您的提供者支持
S256作为代码-挑战 方法,总是更喜欢这样. 仅使用plain当你的提供者不支持S256。 。 。 . -
getNewAccessTokenUsingRefreshToken(token, params, callback):取去平面符号对象(即token财产AccessToken对象,或带有refresh_token获取新的AccessToken- 目标 这对背景处理人员来说,在之前的 AccessToken 过期时重新发布新的 AccessToken 非常有用. 这个params参数是可选的,它是一个可以用来在附加参数中传递到刷新请求(例如更严格的一组范围)的对象。 如果回调未通过, 此函数将返回一个承诺 。 由召回或解约产生的对象是新的AccessToken对象(见上文)。 你如何使用它的例子name:googleOAuth2数字 :
fastify.googleOAuth2.getNewAccessTokenUsingRefreshToken(currentAccessToken.token, {}, (err, newAccessToken) => {
// Handle the new accessToken, e.g. newAccessToken.token.access_token
});
generateAuthorizationUri(requestObject, replyObject, callback): 生成授权 uri 的函数。 如果回调未通过, 此函数将返回一个承诺 。 由回调或解约产生的字符串是ur授权. 当您想要在特定的路线中处理方向调整时, 这一般是有用的 。 这个requestObject参数将请求对象传递到generateStateFunction) (中文(简体) ). 你个 别 需要声明startRedirectPath如果你使用这个方法。 你如何使用它的例子:
fastify.get('/external', { /* Hooks can be used here */ }, (req, reply) => {
fastify.oauth2CustomOAuth2.generateAuthorizationUri(req, reply, (err, authorizationEndpoint) => {
reply.redirect(authorizationEndpoint)
});
});
revokeToken(Token, tokenType, params, callback): 在授权服务器上取消当前访问 ken 或刷新 token 的功能 。 如果召回没有通过,它会返回一个承诺。 回拨或承诺解析返回void
fastify.googleOAuth2.revokeToken(currentAccessToken, 'access_token', undefined, (err) => {
// Handle the reply here
});
revokeAllToken(Token, params, callback): 取消授权服务器上当前访问的功能 token并刷新 token 。 如果召回没有通过,它会返回一个承诺。 回拨或承诺解析返回void
fastify.googleOAuth2.revokeAllToken(currentAccessToken, undefined, (err) => {
// Handle the reply here
});
userinfo(tokenOrTokenSet): 从授权提供者获取用户信息数据的函数 。 两种符号(作为对象)或access_token字符串值可以传递 .
重要说明:
用户信息只在 discovery 选项被使用,身份提供者则发布此类终点广告.
对于静态配置的插件,您需要让 HTTP 给自己打电话 .
详见OIDC标准定义 用户信息端点
更多见 userinfo_endpoint 属性在 OIDC 发现元数据 标准定义.
fastify.googleOAuth2.userinfo(currentAccessToken, (err, userinfo) => {
// do something with userinfo
});
// with custom params
fastify.googleOAuth2.userinfo(currentAccessToken, { method: 'GET', params: { /* add your custom key value pairs here to be appended to request */ } }, (err, userinfo) => {
// do something with userinfo
});
// or promise version
const userinfo = await fastify.googleOAuth2.userinfo(currentAccessToken);
// use custom params
const userinfo = await fastify.googleOAuth2.userinfo(currentAccessToken, { method: 'GET', params: { /* ... */ } });
有一些变体带有召回和许诺. 自定义参数可以作为选项传递. 见 类型 使用模式 实例。 。 。 .
说明:
我们支持HTTP GET 财务报告和审定财务报表 POST 请求使用 userinfo 端点发送访问令牌 Bearer 头部有计划.
您可以通过设置来做到这一点( C)via: "header" 参数),但因其为默认值而并非强制.
我们也支持 POST 发送 access_token 在一个请求机构。 您可以明确提供 via: "body" 参数.
例如,用于 name: 'customOauth2',帮助者 getAccessTokenFromAuthorizationCodeFlow 财务报告和审定财务报表 getNewAccessTokenUsingRefreshToken 将像这样可以访问:
fastify.oauth2CustomOauth2.getAccessTokenFromAuthorizationCodeFlowfastify.oauth2CustomOauth2.getNewAccessTokenUsingRefreshToken
使用类型脚本
类型定义随包而提供. 装饰在运行期间被应用并基于认证配置名. 一个解决方案是利用TypeScript声明合并来添加类型安全命名空间. 确保你有 @types/node 已安装以正确工作 .
在项目申报文件中
import { OAuth2Namespace } from '@fastify/oauth2';
declare module 'fastify' {
interface FastifyInstance {
facebookOAuth2: OAuth2Namespace;
myCustomOAuth2: OAuth2Namespace;
}
}
所有认证配置可用 oauth2 键入到 OAuth2Namespace | undefined比如说, eg. fastify.oauth2CustomOauth2 页:1 customOauth2。 。 。 .
提供方
下列提供者要求正确安排额外工作.
抖动
Twitch 要求在 oauth2 流中求取一个符号必须包含 client_id 财务报告和审定财务报表 client_secret 属性在 tokenRequestParams数字 :
fastify.register(oauthPlugin, {
name: 'twitchOauth2',
credentials: {
client: {
id: '<CLIENT_ID>',
secret: '<CLIENT_SECRET>'
},
auth: oauthPlugin.TWITCH_CONFIGURATION
},
tokenRequestParams: {
client_id: '<CLIENT_ID>',
client_secret: '<CLIENT_SECRET>',
},
// register a fastify url to start the redirect flow
startRedirectPath: '/login/twitch',
// twitch redirect here after the user login
callbackUri: 'http://localhost:3000/login/twitch/callback'
})
链接
链接信息也要求 client_id 财务报告和审定财务报表 client_secret 在符号请求正文中,否则符号交换失败 400 Bad Request通过 tokenRequestParams 如上文Twitch所示。 见 实例/链接.js。 。 。 .
许可证
许可证根据 麻省理工学院。 。 。 .
编号 见 simple-oauth2 驾照也是