Implement discourse Apple login using API

Hi, I am working on building an API for Discourse login using Apple. I have already installed the plugin and followed the instructions from Discourse Apple Authentication. It works fine when logging into Discourse using an Apple account in website.

Upon checking the flow of Apple login from the web, I noticed that it redirects to the Apple page for login, obtains a code generated from Apple upon successful login, and then redirects to:

POST {{discourse_host}}/auth/apple/callback with parameters state:{state}, code:{code from Apple}

After that, it redirects to GET {{discourse_host}}/auth/apple/callback?code={{code from Apple}}&state={state}, generating a new cookie that will be used in the Discourse API.

from this flow I expect it will generate a new cookie after the call /auth/apple/callback

But when I try to call the API it will show the error Authorization timed out, or you have switched browsers. Please try again.

So I tried again with a new code generated by Apple but still got the same error

And when I check the log it show

Started GET "/auth/apple/callback?code=c32f105b5084d42b8bd3e7051873ddb55.0.rrzxz.pChPU9zGlXIHHhgEXLiA5g&state=fbi3bbboud" for>
(apple) Setup endpoint detected, running now.
(apple) Callback phase initiated.
(apple) Authentication failure! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected
Started GET "/auth/failure?message=csrf_detected&strategy=apple" for 123.253.233.16 at 2023-11-27 06:10:28 +0000
Processing by Users::OmniauthCallbacksController#failure as HTML
  Parameters: {"message"=>"csrf_detected", "strategy"=>"apple"}
  Rendered users/omniauth_callbacks/failure.html.erb within layouts/no_ember (Duration: 4.1ms | Allocations: 17)
  Rendered layout layouts/no_ember.html.erb (Duration: 57.5ms | Allocations: 2882)

Message (47 copies reported)

(apple) Authentication failure! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected

Backtrace

/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/omniauth-1.9.2/lib/omniauth/strategy.rb:163:in `log'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/omniauth-1.9.2/lib/omniauth/strategy.rb:486:in `fail!'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/omniauth-oauth2-1.7.3/lib/omniauth/strategies/oauth2.rb:87:in `callback_phase'
/var/www/discourse/plugins/discourse-apple-auth/lib/omniauth_apple.rb:60:in `callback_phase'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/omniauth-1.9.2/lib/omniauth/strategy.rb:238:in `callback_call'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/omniauth-1.9.2/lib/omniauth/strategy.rb:189:in `call!'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/omniauth-1.9.2/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/omniauth-1.9.2/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/omniauth-1.9.2/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/omniauth-1.9.2/lib/omniauth/strategy.rb:192:in `call!'

So I want to ask if this direction to using discourse apple sign-in using API is already correct and how I solve the problem with csrf_detected error

The Apple authentication (and indeed, all other auth methods in DIscourse) are not designed to be used via the API. They should be used directly be end-users in a supported browser.

What problem are you trying to solve here? Perhaps we can work out an alternative solution?

1 Like

Hi @david, thanks for your reply. What I want to achieve here is to create an API for Apple login that will be used by the mobile app to access the Discourse API.

For using the Discourse API to log in with email and password, it works fine using these two Discourse APIs:

  1. {{discourse_host}}/session/csrf.json: This API returns a cookie and CSRF code, which will be used for login using the next API.
  2. {{discourse_host}}/session: This API returns a new cookie that can be used to access other Discourse APIs.
Csrf API

Session

I expect it to be similar to Apple login in Discourse, where I make a request to the Discourse API to generate the _t cookie.

Hi @david, do you have a solution for my issue?

I’m afraid there’s no easy answer. Authentication APIs like this are specifically designed to avoid them being triggered without real user interaction on the web.

For your use case, the more common solution would be to use the user API keys system. That will allow Discourse to handle 100% of the authentication logic, and will give your app some per-user API keys. That strategy should be much more robust than trying to ‘fake’ a user session.

1 Like