React+Node/Express如何实现OAuth外部登录重定向并获取回调授权码
根因定位
你之前用Express拉取第三方授权页HTML再返回给前端渲染的实现,从根上就走偏了:
- 第三方登录页里的表单action、接口路径都是基于第三方自身的域名编写的,当你把HTML拿到自己的服务下返回给前端渲染时,浏览器会按照当前页面的域名解析这些相对路径,最终所有提交请求都会指向你自建的服务,根本不会走到第三方的地址上,这就是用户输完用户名提交时打到你自己
/authorize路由的核心原因 - 这种代理HTML的方式还会完全丢失第三方登录流程依赖的用户Session Cookie、CSRF校验上下文,就算你手动把表单提交地址改成第三方的地址,也会因为没有对应的校验参数被第三方拒绝服务
本质上这个实现方式完全不符合OAuth2.0授权码流程的设计逻辑,不要想着在自己的
/authorize路由里补逻辑去适配第三方提交的请求,从实现路径上就错了。
适配React+Express栈的正确实现
OAuth2.0授权码流程天生就是设计成由浏览器直接完成跳转交互的,根本不需要后端代劳拉取登录页HTML,明确拆分前后端职责即可:
前端(React)需要做的事
- 用户点击登录按钮时,直接让浏览器跳转到第三方的授权地址,全程不需要你的Express服务中转这个登录页的请求,核心代码非常简单:
// 登录按钮的点击回调里执行 window.location.href = "https://somelink.com/oauth/authorize?client_id=someclientid&redirect_uri=http://localhost:3000/callback&response_type=code" - 跳转后浏览器会直接加载第三方的用户名输入页,用户提交用户名后,第三方会自动把页面302重定向到
https://dev.login.com的密码输入页,整个交互全在第三方域名的上下文下跑,不会出现表单地址解析错的问题 - 等用户在第三方输完密码、校验通过后,第三方会按照你提前配置的回调地址,把浏览器重定向回你自己的
http://localhost:3000/callback?code=xxx地址。你只需要在这个React路由里做两件事:- 从当前页面的URL参数里把
code值取出来 - 发个内部接口请求把这个
code传给你自己的Express后端
- 从当前页面的URL参数里把
后端(Express)需要做的事
- 新增一个内部接口(比如
/api/oauth/exchange)用来接收前端传过来的授权码,在服务端环境下向第三方的token交换接口发起请求,携带约定好的client_id、client_secret、拿到的code、和授权流程一致的redirect_uri,从第三方那里换回access_token和refresh_token - 这里特别提醒:换token的操作必须在后端完成,因为这个环节需要用到你的应用密钥
client_secret,把密钥放到前端代码里等于直接把应用身份暴露给所有人
避坑提示
- 不要想着用iframe嵌第三方登录页,绝大多数OAuth服务商都会加
X-Frame-Options头禁止页面被iframe嵌入,会直接导致页面加载失败 - 不要尝试用后端axios代理第三方登录页的请求,这种方式拿不到用户浏览器端的Cookie,就算你硬把表单提交地址改成第三方的,也会因为没有有效Session被第三方拒之门外
- 完全不需要为第三方登录流程写自己的
/authorize路由,所有第三方页面的交互全靠浏览器直接和第三方交互完成,你的服务只需要接住最后带授权码的回调、完成换token的步骤就行
内容的提问来源于stack exchange,提问作者tejas
相关产品推荐
相关产品推荐

