Flutter应用OAuth2认证对接自有Flask API的实现方案咨询
经过数小时无果的调研,我仍对如何实现以下需求存在困惑:
我开发了一款Flutter应用,目前已通过OAuth2实现Google(使用google_sign_in插件)、Facebook(使用flutter_facebook_auth插件)第三方登录,其中Facebook登录的实现代码如下:
final LoginResult loginResult = await FacebookAuth.instance.login(); final userData = await FacebookAuth.instance.getUserData(); print(userData);
代码执行后打印的用户数据格式为:{email: john.doe@email.com, id: 123456, name: John Doe}
我此前已使用Flask/Python搭建了支持OAuth2认证的网页端,现在需要实现网页端与App端用户偏好、业务数据等信息的互通。
我最初的实现思路为:
- 将App端从OAuth流程获取的认证信息发送至自有API,若对应用户不存在则在数据库中创建;
- API返回带有效期(TTL)的访问令牌;
- 后续校验App发起请求携带的令牌有效性。
但这套方案需要编写大量自定义样板代码,我确信已有成熟的现成实现可供参考。同时我存在安全层面的疑问:如何防范他人通过反编译应用、代理抓包或直接伪造请求调用API、冒充其他用户?
我的应用安全要求为中等:后续会上线消息功能,但不涉及资金交易类场景。
目前我考虑过以下几种实现方向,但均存在顾虑:
- 采用PKCE模式:该模式需要OAuth2流程经过我的Flask API,实现复杂度较高(我此前仅在Flutter端单独实现OAuth2就已花费较多精力);
- 采用Resource Owner Password Credentials Grant模式:该模式似乎支持将第三方OAuth2认证结果传递给自有API换取访问令牌用于后续请求,但该协议版本看起来较为老旧(搜索靠前的结果多为Oracle早年的文档);
- 参考Firebase实现逻辑:其采用先完成第三方OAuth2认证、再将凭证传递至服务端API的流程,首次传递凭证时会自动在数据库创建对应用户,但我无法通过逆向工程摸清其具体实现逻辑;
- 采用WebView嵌入Flask网页端的OAuth2认证流程:该方案移动端体验较差,且我不清楚如何读取、存储返回的认证凭证。
你最初的思路本身就是行业通用的标准实现,也就是OAuth2的社交登录令牌校验换自有令牌流程,不需要硬套复杂的PKCE全流程或者老旧的密码模式,具体落地直接按下面的步骤做即可,不需要写太多冗余样板代码,安全等级也完全匹配你的中等安全要求:
核心实现逻辑
你不需要让整个OAuth流程走自己的Flask服务,App端继续沿用现在已经写好的Google、Facebook端侧登录逻辑即可,只需要补3个环节的逻辑:
- App端完成第三方登录后,不要只拿基础用户信息,要从插件里拿到第三方颁发的身份令牌(id_token)/访问令牌(access_token),而不是只传解析后的邮箱、用户名字段。
- 对Facebook登录来说,
loginResult.accessToken?.token就是你要传给后端的令牌,不要直接传getUserData()返回的明文用户信息 - 对Google登录来说,
googleSignIn.authentication返回对象里的idToken就是要传的凭证
- 对Facebook登录来说,
- 你的Flask后端新增一个登录接口,接收App传过来的第三方令牌+第三方平台标识(Google/Facebook),在服务端做校验:
- 拿收到的令牌去对应第三方平台的官方校验接口验证有效性,确认令牌是对应平台颁发的、没有过期、对应的应用id和你自己申请的应用id一致
- 校验通过后,从令牌解析出用户的唯一标识、邮箱等信息,查本地数据库,不存在就新建用户记录
- 生成你自己的带TTL的JWT访问令牌(可选搭配刷新令牌)返回给App端
- App端后续所有业务请求都在请求头里带这个自有JWT令牌,后端统一加中间件校验令牌签名、有效期,通过后再放行业务逻辑。
安全防护措施
针对你担心的反编译、抓包、伪造请求问题,做下面几个配置就足够覆盖中等安全等级场景:
- 所有API请求强制走HTTPS,不要留HTTP明文接口,从传输层防抓包篡改
- JWT签名用非对称加密算法(比如RS256),私钥只存在服务端,绝对不要硬编码在App里,就算App被反编译也拿不到签名密钥,无法伪造有效令牌
- 服务端校验第三方令牌的时候,一定要校验令牌对应的受众(aud)字段是你自己的第三方应用id,防止攻击者拿其他应用的有效令牌来冒充你的用户
- 可以给JWT加设备标识绑定,生成令牌的时候把App端传的设备唯一标识存在令牌payload里,后续请求校验的时候对比当前请求的设备标识和令牌里的是否一致,就算令牌泄露其他人拿到也没法在别的设备上用
- 接口加基础的频率限制,防止暴力枚举请求。
各备选方案的问题说明
- PKCE模式适合你自己作为OAuth授权方的场景,你现在是对接第三方社交登录,完全没必要把整个授权流程拉到自己服务端,徒增复杂度
- 密码授权模式已经被OAuth2.1正式废弃,不要用,完全不适合第三方登录场景
- Firebase的社交登录逻辑本质就是上面说的「端侧拿第三方令牌→传给Firebase后端校验→返回Firebase自有令牌」的流程,没有什么特殊黑科技,你自己按上面的逻辑写和它的实现逻辑完全一致
- WebView嵌入网页登录的方案体验差,还容易有WebView安全漏洞,完全没必要选。
减少样板代码的实现方式
Flask端不需要从零写所有逻辑,直接用成熟的现成依赖就能减少90%的重复代码:
- JWT生成和校验直接用对应Flask生态的JWT扩展,自带令牌过期、签名校验中间件,不需要自己写逻辑
- Google、Facebook的令牌校验直接用对应平台官方提供的服务端SDK,不需要自己手动写HTTP请求调用校验接口、自己解析字段,SDK已经封装好了有效性、受众匹配的校验逻辑。
内容的提问来源于stack exchange,提问作者hansaplast

