Sign in with Apple

@David, the author of the PR has responded to my comment, what’s your opinion on this?:

Me:

This PR appears to solve the issue with the lack of name and email?

PR Author:

No, unfortunately not. Apple needs to provide a REST API endpoint to retrieve name/email as they currently only pass this information upon successful authentication once and not on subsequent authentications.

Isn’t one-time at initial Authorisation good enough for us?

It’s better than nothing, but it’s still not good. For example, if you “sign in with apple”, but then click cancel on the “create account” screen. Or if you connect an existing account with apple, then decide to create a new account instead. Hopefully Apple will resolve that before the end of the beta :crossed_fingers:

4 Likes

Is there a working implementation of the Sign in with Apple feature at this stage? A site I’m doing marketplace work for plans to have an iOS app however without this option, we can’t enable other auth options unless we want our app to be rejected for violations of app guidelines.

No that I’m aware of. We’re waiting for this issue to be resolved How to retrieve email and name? · Issue #8 · nhosoya/omniauth-apple · GitHub, though the repo owner has unhelpfully closed it.

I’m not too sure but don’t commits like this one and the other commits from August resolve the issue?

Feel free to install the plugin to test but I’m not aware that’s yet resolved. I would not recommend using it in Production though until it’s finished or at least signed off by a developer with some time to confirm things.

I’m a huge advocate for this feature. I’d love to see it be built into vanilla Discourse like the other sign-in options.

I think you cannot retrieve that kind of information on purpose.

Displaying a Sign in with Apple button in your app or website means that people can sign in or sign up with just a tap using the Apple ID they already have, and skip filling out forms, verifying email addresses, and choosing passwords. Sign in with Apple provides a new, more private way to simply and quickly sign into apps and websites while giving people a consistent sign-in experience they can trust and the convenience of not having to remember multiple accounts and passwords. In cases where you choose to ask for a name and email address, people have the option to keep their email address private and share a unique, random email address instead.

See the full developer article by Apple

You’re right, privacy is one of the key parts of Sign In with Apple, but the key part of your quote is:

Assuming people choose to give us name/email, then we would expect to receive it from the provider every time the user logs in. In the current implementation, this doesn’t happen. After the first authentication, we get no user information.

I don’t think this is something the gem author can fix - it’s something Apple would need to change. I can’t see that happening any time soon, so maybe we just need to ask users to enter their name/email in Discourse manually :cry:

3 Likes

Yes, but what about the people who end up choosing not to give their information?

Oh, and here’s a crude concept. :sweat_smile:

Good news:

I partially got a fork of Robert/merefield’s plugin working (the fork only involves switching to a copy of the omniauth gem that I built from the latest source on GitHub). However, on my test Discourse (which has HTTPS end-to-end via ngrok), I had to set the same site cookies Site Setting to (none) in order for auth to work. With the setting disabled, I can create an account (even if I closed the signup form) and can log back in however I’m unable to do so if the setting is enabled.

Backtrace from a failed login attempt is below:

Failed login backtrace
/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/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>'

Does anyone here have any suggestions on if/how I can re-write the plugin to no longer require disabling this site setting for its core functions to work? My plugin code lives at GitHub - sau226dev/discourse-sign-in-with-apple: Login to Discourse with your Apple ID and the latest code I used for rebuilding the omniauth gem should be in the same GitHub organization.

Thanks for any help you can provide in advance,

sau226

3 Likes

The plugin originally worked to an extent but has not received attention recently because of the issue described by David above.

Until we receive positive news that Apple has resolved that fundamental blocker (sending name and email on every login attempt) it’s not worth maintaining this code imho. I don’t believe there is anything you can do to get around it? That is why I haven’t even attempted to update the dependencies. The plugin would fail testing in any case.

So this not a ‘released’ plugin (otherwise this or a similar topic would live in #plugin) and it’s not likely to receive any support until that happens. I would be delighted to ‘polish it off’ if that issue was resolved and Apple were to provide that info.

There is also another significant issue btw: in order to use it for ones own site you need to pay up for the Apple Developer Programme to get access to the setup on Apple’s systems. This is going to put a lot of low budget sites off I suspect as that is not a free/low price sign up.

5 Likes

I believe @sau226 would seem to suggest the lack of returned email/name is not, in fact, a blocker?

@orenwolf It (the lack of returned email/name on subsequent log ins) did not appear to be an issue. I believe that I was able to close the sign up window and resume sign up with correct details passed in. As I stated before, I was able to log in with Apple afterwards straight away with no issue.

The only issue I faced was the CSRF error unless the site setting I mentioned above was disabled. One potential issue is the lack of a name on the sign in form and that the username is whatever is before the @ sign in the email (however if you ask me, these potential issues are either a non issue or can be easily fixed by the user).

3 Likes

In addition to David’s comments above I found a related topic on Apple’s developer support site which attracted an official response that confirms the issue:

https://forums.developer.apple.com/thread/121496

Official Response:

Hi aslkdjalksdjasdasd,
This behaves correctly, user info is only sent in the ASAuthorizationAppleIDCredential upon initial user sign up. Subsequent logins to your app using Sign In with Apple with the same account do not share any user info and will only return a user identifier in the ASAuthorizationAppleIDCredential. It is recommened that you securely cache the initial ASAuthorizationAppleIDCredential containing the user info until you can validate that an account has succesfully been created on your server.
Patrick

As one developer comments:

So wait… If for some reason the first redirect from Apple gets lost for one of many VERY common reasons then we’ve permanently lost that user since there’s no other way to get their information. There is NO other way to get this info?

and another:

Or if something goes wrong downstream, we would have the customers complaining and support will tell them to go into AppleId website to revoke the permission, so they can properly register again. I believe this will be a poor experience and will make people not using this login mechanism if they start having this sort of problems.

So I don’t think you can safely use this in Production, sadly. This would be a support nightmare.

I suggest we put this to sleep until Apple wakes up to the problem they’ve created: in their attempt to improve security it seems they’ve over-compromised robustness.

10 Likes

That’s a bummer. :cry:

1 Like

Apple has updated their “Sign in with Apple” developer page to include more information about data collection, data management, etc.
https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/data-management/

4 Likes

I have opened a pull request to update the omniauth-apple gem to the latest version, which includes this commit that sounds like it might resolve the issue of not receiving the user’s email on every login.

In order to try this out I followed the recommended blog post to set up the credentials, but there are two things I couldn’t figure out so far:

  • What’s the path for the return URL redirect that is needed to set up the service ID with Apple?
  • How do I get the “verification txt”, or is that not required? Searching around I found mentions of this being downloadable from Apple as part of the domain/email communication setup, but this seems to be no longer the case:
2 Likes

Thanks.

Normally you submit a PR after you’ve successfully tested something :wink:

Please confirm when you have.

Seems like a no-brainer though but I would be more encouraged if we had a statement from Apple that they now send email every time. Changing the gem version will not resolve that if Apple hasn’t addressed the core issue.

I’ll take a look at my setup when I re-subscribe to being an Apple Developer which i’ve not yet done … (this debacle was a bit of a discouragement tbh)

@David

I have tried bumping our fork of the plugin to use the latest omniauth-apple. (Note that there are some other changes required, not just a bump of the version number).

tl;dr: it’s still broken

I managed to hack it into working on my sandbox, but it still has a number of issues:

  1. Apple is using a POST request on the callback. This is not common in OAuth implementations because it means samesite=Lax cookies will not be sent with the request. That means Discourse is unable to read session cookies during the callback, and therefore throws a CSRF error.

    INSECURE workaround is to disable this security by changing Discourse cookies to samesite=None

  2. Using a POST request without a CSRF token also trips another security measure in core.

    INSECURE workaround is to remove this line

  3. I was getting a 403 from apple when the omniauth gem was fetching the JWKs. I suspect that the Accept: header is not being set correctly, but haven’t verified that

    INSECURE workaround was to hard-code the keys

After all that, I finally managed to log in with apple. You can try it out on https://sandbox.dtaylor.uk (will leave it working for a few days, but please don’t enter anything sensitive on there because it is INSECURE)

And then… emails/names are still only included in the first authentication. You can try this out - sign in with apple, cancel the account creation, then try again. The second attempt will be missing your details.

So, assuming apple isn’t going to change things any time soon… how could we make this work?

For (1) and (2) I think we could convert Apple’s POST into a GET without hurting security. When we receive a POST on the callback, we render some javascript which sets window.location='/auth/apple/callback?code=...&state=.... From there, it would work just like any other provider. However, I think intercepting the POST would require some core API changes.

For (3), I think it could probably be fixed with a little work in the omniauth gem.

But we’re still stuck without name/email, so I’m not sure it’s worth fixing these other problems :cry:

7 Likes

Thanks for giving this another try and your analysis! Maybe submitting a technical support incident to Apple could shed some light on the remaining issues? From my experience they will more likely to provide possible solutions/workarounds there than in the Apple developer forums.