Jim*_*ane 6 oauth cross-domain firebase google-cloud-functions
我正在为Firebase平台的用户实现oAuth登录。
除非用户已 禁用跨域cookie,否则所有方法都工作正常。
这是我所做的。
statemay 函数设置cookie并将用户重定向到oAuth提供者。如果用户已从其浏览器禁用跨域方Cookie,则在上面的步骤3中,该功能无法读取任何Cookie。两种功能都位于同一域中,如下面的屏幕快照所示。
有什么办法可以解决这个问题?我的方法做错了吗?
我不明白为什么这两个函数被视为跨域。
更新以包括更多信息
请求:
Request URL: https://europe-west2-quantified-self-io.cloudfunctions.net/authRedirect
Request Method: GET
Status Code: 302
Remote Address: [2a00:1450:4007:811::200e]:443
Referrer Policy: no-referrer-when-downgrade
Run Code Online (Sandbox Code Playgroud)
请求标题
:authority: europe-west2-quantified-self-io.cloudfunctions.net
:method: GET
:path: /authRedirect
:scheme: https
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
accept-encoding: gzip, deflate, br
accept-language: en-GB,en-US;q=0.9,en;q=0.8
cookie: signInWithService=false; state=877798d3672e7d6fa9588b03f1e26794f4ede3a0
dnt: 1
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Run Code Online (Sandbox Code Playgroud)
响应标题
alt-svc: quic=":443"; ma=2592000; v="46,43,39"
cache-control: private
content-encoding: gzip
content-length: 218
content-type: text/html; charset=utf-8
date: Sat, 03 Aug 2019 08:55:18 GMT
function-execution-id: c8rjc7xnvoy8
location: https://cloudapi-oauth.suunto.com/oauth/authorize?response_type=code&client_id=xxx&redirect_uri=&scope=workout&state=1c8073866d1ffaacf2d4709090ad099872718afa
server: Google Frontend
set-cookie: state=1c8073866d1ffaacf2d4709090ad099872718afa; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
set-cookie: signInWithService=false; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
status: 302
vary: Accept
x-cloud-trace-context: 99a93680a17770f848f200a9e729b122;o=1
x-powered-by: Express
Run Code Online (Sandbox Code Playgroud)
之后,一旦用户从服务中返回,他就针对解析cookie的代码(或处理该cookie的函数)进行了身份验证:
export const authToken = functions.region('europe-west2').https.onRequest(async (req, res) => {
const oauth2 = suuntoAppAuth();
cookieParser()(req, res, async () => {
try {
const currentDate = new Date();
const signInWithService = req.cookies.signInWithService === 'true';
console.log('Should sign in:', signInWithService);
console.log('Received verification state:', req.cookies.state);
console.log('Received state:', req.query.state);
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
console.log('Received auth code:', req.query.code);
const results = await oauth2.authorizationCode.getToken({
code: req.query.code,
redirect_uri: determineRedirectURI(req), // @todo fix,
});
// console.log('Auth code exchange result received:', results);
// We have an access token and the user identity now.
const accessToken = results.access_token;
const suuntoAppUserName = results.user;
// Create a Firebase account and get the Custom Auth Token.
let firebaseToken;
if (signInWithService) {
firebaseToken = await createFirebaseAccount(suuntoAppUserName, accessToken);
}
return res.jsonp({
firebaseAuthToken: firebaseToken,
serviceAuthResponse: <ServiceTokenInterface>{
accessToken: results.access_token,
refreshToken: results.refresh_token,
tokenType: results.token_type,
expiresAt: currentDate.getTime() + (results.expires_in * 1000),
scope: results.scope,
userName: results.user,
dateCreated: currentDate.getTime(),
dateRefreshed: currentDate.getTime(),
},
serviceName: ServiceNames.SuuntoApp
});
} catch (error) {
return res.jsonp({
error: error.toString(),
});
}
});
});
Run Code Online (Sandbox Code Playgroud)
上面的代码找不到名称为的Cookie state
所以这里失败了
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
Run Code Online (Sandbox Code Playgroud)
在这里进行更多搜索是更多信息。
我基于https://github.com/firebase/functions-samples/tree/master/instagram-auth的示例
看起来其他用户也遇到相同的问题https://github.com/firebase/functions-samples/issues/569
我也打开了这个问题https://github.com/firebase/firebase-functions/issues/544
您的响应显示了 Set-Cookie 标头state和signInWithService没有属性的 cookie domain:
set-cookie: state=1c8073866d1ffaacf2d4709090ad099872718afa; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
set-cookie: signInWithService=false; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
Run Code Online (Sandbox Code Playgroud)
没有域的 Set-Cookie 意味着 cookie 在返回服务器的过程中发生的情况取决于浏览器。“默认”、符合规范的行为:浏览器将获取服务 URL 的 FQDN 并将其与 cookie 关联。RFC6265:
除非 cookie 的属性另有指示,否则 cookie 仅返回到源服务器(而不返回到任何子域)...如果服务器省略 Domain 属性,则用户代理将仅将 cookie 返回到源服务器。
当浏览器决定是否接受来自 HTTP 服务的 cookie 时,决策标准之一是 cookie 是第一方还是第三方:
europe-west2-quantified-self-io.cloudfunctions.net/authRedirect位于https://europe-west2-quantified-self-io.cloudfunctions.net/...europe-west2-quantified-self-io.cloudfunctions.net/authRedirect位于https://some.domain.app.com/...在您的情况下,您的“父”应用程序/页面的 FQDN 可能与 不同europe-west2-quantified-self-io.cloudfunctions.net,因此这些 cookie 被标记为第三方。正如您所发现的,用户可以选择阻止第三方 cookie。截至 2019 年 8 月,Firefox 和 Safari 默认阻止第 3 方 cookie。大多数(如果不是全部)广告拦截器和类似的扩展程序也会阻止它们。这将导致浏览器简单地忽略来自 的 HTTP 响应中的 Set-Cookie 标头europe-west2-quantified-self-io.cloudfunctions.net/authRedirect。该 cookie 不会被发送回第二个 Firebase 函数,europe-west2-quantified-self-io.cloudfunctions.net/authToken因为它在客户端上不存在。
您的选择:
/authRedirectFB 函数,解析响应(包括通过 Set-Cookie 标头的 cookie),然后通过 .txt 将响应(包括 cookie)写回浏览器document.cookie。在这种情况下,cookie 是第一方的。cloudapi-oauth.suunto.com因为授权服务器不需要 cookie。您遵循了推荐此流程的Instagram-auth示例单击“使用 Instagram 登录”按钮时,会显示一个弹出窗口,将用户重定向到重定向功能 URL。
然后,重定向功能将用户重定向到 Instagram OAuth 2.0 同意屏幕,用户必须在其中(仅限第一次)授予批准。此外,
statecookie 还使用 URL 查询参数的值在客户端上设置,state以便稍后进行检查。
当授权服务器不支持 PKCE 扩展(不支持)时,对查询参数的检查state基于oAuth 客户端的实现最佳实践:cloudapi-oauth.suunto.com
客户端必须防止 CSRF。一次性使用“state”参数中携带的 CSRF 令牌,该令牌安全地绑定到用户代理,应该用于此目的。如果客户端使用 PKCE [RFC7636],并且授权服务器支持 PKCE,则客户端可以选择不使用“状态”进行 CSRF 保护,因为此类保护是由 PKCE 提供的。在这种情况下,“状态”可以再次用于其原始目的,即传输有关客户端应用程序状态的数据
关键短语被安全地绑定到用户代理。对于网络应用程序来说,cookie 是实现此绑定的一个不错的选择,但它不是唯一的选择。您可以将值粘贴state到本地或会话存储中,单页应用程序在实践中正是这样做的。如果您想生活在云中,您可以使用stateCloud Storage 或同等产品...但您必须创建一个唯一标识您的客户端和此特定 HTTP 请求的密钥。并非不可能,但对于一个简单的场景来说可能有点过分了。
| 归档时间: |
|
| 查看次数: |
228 次 |
| 最近记录: |