@David,PR 的作者已经回复了我的评论,对此你有什么看法?:
我:
这个 PR 似乎解决了缺少姓名和邮箱的问题?
PR 作者:
不,很遗憾不行。Apple 需要提供 REST API 端点来获取姓名和邮箱,因为他们目前仅在首次成功认证时传递这些信息,而在后续认证中不再传递。
仅在初次授权时获取一次,对我们来说难道不够吗?
@David,PR 的作者已经回复了我的评论,对此你有什么看法?:
我:
这个 PR 似乎解决了缺少姓名和邮箱的问题?
PR 作者:
不,很遗憾不行。Apple 需要提供 REST API 端点来获取姓名和邮箱,因为他们目前仅在首次成功认证时传递这些信息,而在后续认证中不再传递。
仅在初次授权时获取一次,对我们来说难道不够吗?
这比没有好,但依然不够理想。例如,如果您点击“使用 Apple 登录”,但在“创建账户”界面取消了操作;或者您先连接了现有的 Apple 账户,随后又决定创建一个新账户。希望 Apple 能在测试版结束前解决这个问题:![]()
目前是否有“使用 Apple 登录”功能的可用实现?我为客户的电商平台开发的项目计划推出 iOS 应用,但若没有此选项,我们就无法启用其他身份验证方式,否则应用可能因违反 App Store 审核指南而被拒绝。
据我所知没有。我们正在等待这个问题得到解决 https://github.com/nhosoya/omniauth-apple/issues/8,不过仓库所有者不恰当地关闭了它。
请随意安装插件进行测试,但我尚不清楚该问题是否已解决。不过,在开发完成或至少由开发人员确认之前,我不建议在正式环境中使用。
我强烈支持这项功能。我希望它能像其他登录选项一样,直接内置到原生 Discourse 中。
我认为这是故意无法检索此类信息的。
在您的应用或网站中显示“使用 Apple 登录”按钮,意味着用户只需轻点一下,即可使用他们已有的 Apple ID 登录或注册,无需填写表单、验证电子邮件地址或设置密码。“使用 Apple 登录”提供了一种全新的、更私密的方式,让用户能够简单快速地登录应用和网站,同时提供他们可信赖的一致登录体验,并免去记忆多个账户和密码的麻烦。在您需要请求姓名和电子邮件地址的情况下,用户可以选择隐藏其真实电子邮件地址,转而分享一个唯一的随机电子邮件地址。
你说得对,隐私确实是“使用 Apple 登录”的核心部分之一,但你引用的关键部分是:
假设用户选择向我们提供姓名和电子邮件,那么我们期望每次用户登录时都能从提供商处收到这些信息。但在当前实现中,情况并非如此。首次认证后,我们再也无法获取用户信息。
我认为这并非该 gem 的作者能够修复的问题——这需要 Apple 进行更改。我看不到这种情况短期内会发生,因此我们或许只能要求用户在 Discourse 中手动输入他们的姓名和电子邮件 ![]()
好消息:
我部分让 Robert/merefield 的插件的分支版本运行起来了(该分支仅涉及切换到我从 GitHub 最新源码构建的 omniauth gem 副本)。不过,在我的测试 Discourse 实例上(通过 ngrok 实现了端到端 HTTPS),我必须将“站点 Cookie”站点设置设为“(无)”,身份验证才能正常工作。禁用该设置后,我可以创建账户(即使关闭了注册表单),也能重新登录;但如果启用该设置,则无法做到。
以下是登录失败的回溯信息:
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger.rb:112:in `report_to_store'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger.rb:103:in `add_with_opts'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger.rb:54:in `add'
/usr/local/lib/ruby/2.6.0/logger.rb:543:in `error'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:163:in `log'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:486:in `fail!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-oauth2-1.6.0/lib/omniauth/strategies/oauth2.rb:71:in `callback_phase'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:238:in `callback_call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:189:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/builder.rb:64:in `call'
/var/www/discourse/lib/middleware/omniauth_bypass_middleware.rb:47:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/tempfile_reaper.rb:15:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/conditional_get.rb:38:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/head.rb:12:in `call'
/var/www/discourse/lib/content_security_policy/middleware.rb:12:in `call'
/var/www/discourse/lib/middleware/anonymous_cache.rb:318:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/session/abstract/id.rb:259:in `context'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/session/abstract/id.rb:253:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/cookies.rb:648:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:101:in `run_callbacks'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/actionable_exceptions.rb:17:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/debug_exceptions.rb:32:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/middleware/reporter.rb:43:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/rack/logger.rb:38:in `call_app'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/rack/logger.rb:28:in `call'
/var/www/discourse/config/initializers/100-quiet_logger.rb:18:in `call'
/var/www/discourse/config/initializers/100-silence_logger.rb:31:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/request_id.rb:27:in `call'
/var/www/discourse/lib/middleware/enforce_hostname.rb:17:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/method_override.rb:22:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/executor.rb:14:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/sendfile.rb:111:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-6.0.1/lib/action_dispatch/middleware/host_authorization.rb:77:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-1.1.4/lib/mini_profiler/profiler.rb:184:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/message_bus-2.2.3/lib/message_bus/rack/middleware.rb:57:in `call'
/var/www/discourse/lib/middleware/request_tracker.rb:181:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/engine.rb:526:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/railtie.rb:190:in `public_send'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.1/lib/rails/railtie.rb:190:in `method_missing'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/urlmap.rb:68:in `block in call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/urlmap.rb:53:in `each'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.8/lib/rack/urlmap.rb:53:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/lib/unicorn/http_server.rb:605:in `process_client'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/lib/unicorn/http_server.rb:700:in `worker_loop'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/lib/unicorn/http_server.rb:548:in `spawn_missing_workers'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/lib/unicorn/http_server.rb:144:in `start'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.2/bin/unicorn:128:in `<top (required)>'
/var/www/discourse/vendor/bundle/ruby/2.6.0/bin/unicorn:23:in `load'
/var/www/discourse/vendor/bundle/ruby/2.6.0/bin/unicorn:23:in `<main>'
有人有什么建议吗?我该如何重写该插件,使其核心功能不再依赖禁用此站点设置?我的插件代码位于 https://github.com/sau226dev/discourse-sign-in-with-apple,用于重新构建 omniauth gem 的最新代码应位于同一 GitHub 组织中。
提前感谢大家提供的任何帮助,
sau226
该插件最初在一定程度上可以工作,但由于上述 David 描述的问题,近期并未得到关注。
在我们收到苹果已解决这一根本性障碍(即每次登录尝试时发送姓名和电子邮件)的积极消息之前,依我看,维护这段代码并不值得。我不认为您有什么办法可以绕过它?这也是我甚至没有尝试更新依赖项的原因。无论如何,该插件在测试中都会失败。
因此,这并非一个“已发布”的插件(否则此类或类似话题会出现在 #plugin 频道中),在问题解决之前,它不太可能获得任何支持。如果该问题得到解决,且苹果能够提供相关信息,我将非常乐意将其完善。
顺便提一下,还存在另一个严重问题:要将其用于自己的网站,您需要付费加入苹果开发者计划,以获取在苹果系统上进行设置的权限。我怀疑这会让许多预算有限的网站望而却步,因为注册并非免费或低价。
我认为 @sau226 似乎暗示未返回电子邮件/姓名实际上并不是一个阻碍?
@orenwolf 后续登录时未返回邮箱/姓名这一情况似乎并未构成问题。我认为我能够关闭注册窗口,并重新使用正确的信息进行注册。正如我之前所说,我随后可以立即使用 Apple 账号登录,没有任何问题。
我遇到的唯一问题是 CSRF 错误,除非禁用我之前提到的站点设置。一个潜在的问题是注册表单中缺少姓名输入,且用户名默认为邮箱中 @ 符号前的部分(不过在我看来,这些潜在问题要么根本不算问题,要么用户可以轻松解决)。
除了大卫上面的评论外,我在苹果开发者支持网站上发现了一个相关主题,该主题获得了官方回复并确认了该问题:
官方回复:
您好,aslkdjalksdjasdasd,
此行为符合预期。用户信息仅在首次用户注册时通过 ASAuthorizationAppleIDCredential 发送。之后使用“通过 Apple 登录”以同一账户登录您的应用时,不会共享任何用户信息,ASAuthorizationAppleIDCredential 中仅会返回用户标识符。建议您安全地缓存包含用户信息的初始 ASAuthorizationAppleIDCredential,直到您能在服务器上验证账户已成功创建。
Patrick
正如一位开发者评论的那样:
等等……如果由于某些原因,来自苹果的首次重定向因多种非常常见的原因而丢失,那么我们就永久性地失去了该用户,因为没有任何其他方式可以获取他们的信息。真的没有其他方法可以获取这些信息吗?
另一位开发者则评论道:
或者,如果下游出现问题,客户会投诉,而支持人员会让他们前往 Apple ID 网站撤销权限,以便重新正确注册。我认为这将带来糟糕的体验,并导致人们在遇到此类问题时不再使用此登录机制。
因此,遗憾的是,我认为在生产环境中无法安全地使用此功能。这将会成为支持方面的噩梦。
我建议暂时搁置此问题,直到苹果意识到他们所制造的问题:在试图提高安全性的过程中,他们似乎过度牺牲了系统的健壮性。
真让人失望。![]()
Apple 已更新其“使用 Apple 登录”开发者页面,增加了关于数据收集、数据管理等方面的更多信息。
我已提交一个 拉取请求,用于将 omniauth-apple gem 更新至最新版本。该版本包含 此提交,似乎可能解决每次登录时无法获取用户邮箱的问题。
为了尝试此方案,我按照 推荐的博文 配置了凭据,但截至目前仍有两点未能弄清楚:
谢谢。
通常,你在成功测试某项功能后提交 PR 即可 ![]()
请在完成后确认。
这看起来似乎显而易见,但如果我们能收到苹果方面的声明,确认他们现在每次都会发送邮件,我会更有信心。如果苹果没有解决核心问题,仅仅更改 gem 版本是无法解决这一情况的。
等我重新订阅苹果开发者计划时(我目前尚未完成),我会检查一下我的设置……(说实话,这次事件确实让人有些泄气)
我已经尝试了 升级我们插件的分支 以使用最新的 omniauth-apple。(注意:除了升级版本号外,还需要进行其他一些更改)。
tl;dr:问题依然存在
我成功地在沙盒环境中通过一些“黑客”手段使其运行,但它仍然存在一些问题:
Apple 在回调时使用 POST 请求。这在 OAuth 实现中并不常见,因为这意味着带有 samesite=Lax 属性的 Cookie 将不会随请求发送。这导致 Discourse 在回调期间无法读取会话 Cookie,从而引发 CSRF 错误。
不安全的临时解决方案是将 Discourse 的 Cookie 设置为 samesite=None 来禁用此安全机制。
在没有 CSRF 令牌的情况下使用 POST 请求还会触发核心中的另一项安全措施。
不安全的临时解决方案是删除 这一行。
当 omniauth gem 获取 JWKs 时,我收到了来自 Apple 的 403 错误。我怀疑 Accept: 头未正确设置,但尚未验证这一点。
不安全的临时解决方案是硬编码密钥。
经过所有这些操作后,我终于成功使用 Apple 登录了。您可以在 https://sandbox.dtaylor.uk 上尝试(我会让它保持运行几天,但请勿在其中输入任何敏感信息,因为它是不安全的)。
然后……电子邮件和姓名仍然只在首次认证时包含。您可以尝试一下:使用 Apple 登录,取消账户创建,然后再次尝试。第二次尝试将缺少您的详细信息。
因此,假设 Apple 短期内不会改变现状……我们该如何解决这个问题?
对于问题 (1) 和 (2),我认为可以将 Apple 的 POST 请求转换为 GET 请求,而不会影响安全性。当我们在回调中收到 POST 请求时,可以渲染一些 JavaScript 代码,将 window.location 设置为 /auth/apple/callback?code=...&state=...。此后,它将像其他任何提供商一样正常工作。不过,我认为拦截 POST 请求需要对核心 API 进行一些更改。
对于问题 (3),我认为通过一些在 omniauth gem 中的小改动可能就能解决。
但我们仍然无法获取姓名和电子邮件,所以我不确定是否值得修复这些其他问题 ![]()
感谢您再次尝试并提供分析!或许可以向苹果提交一个技术支持工单,以厘清剩余的问题?根据我的经验,在那里他们更有可能提供可行的解决方案或变通方法,而在苹果开发者论坛上则未必如此。