Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting up with JavaScript/IOS application needs to be documented #117

Open
4 tasks
wJoenn opened this issue Feb 13, 2025 · 0 comments
Open
4 tasks

Setting up with JavaScript/IOS application needs to be documented #117

wJoenn opened this issue Feb 13, 2025 · 0 comments

Comments

@wJoenn
Copy link

wJoenn commented Feb 13, 2025

Summary

I've passed the last couple of days trying to set up this gem to work with a API-only Rails application and there's been a lot of trouble along the way.
I figured I should share my experience now that I've made it work in case anyone else tries to do the same later.

Context

Our Rails application is a backend only app handling user authentication with devise and omniauth-apple along with other omniauth providers.
This Rails API interacts with multiple Client-side applications combining multiple platforms (Web, Android, IOS and a web extension).

Our initial config for omniauth-apple was as follow and we made sure that every credential was properly defined as per the README's instruction (which were a huge help btw, thank you very much)

  config.omniauth :apple, Rails.application.credentials.apple_app_bundle_id, '',
                  key_id: Rails.application.credentials.apple_key_id,
                  pem: Rails.application.credentials.apple_pem,
                  scope: 'email',
                  team_id: Rails.application.credentials.apple_team_id

When we want to sign in a user using omniauth our flow is as follow (example with the @capacitor-community/apple-sign-in npm package):

  • We authenticate the user through a omniauth provider from our client-side applications.
import { SignInWithApple } from '@capacitor-community/apple-sign-in';

const handleAppleSignin = async () => {
  const { response } = await SignInWithApple.authorize({
    clientId: YOUR_CLIENT_ID,
    redirectURI: YOUR_REDIRECT_URI,
    scopes: 'email'
  });

  ...
};
  • Once the user is authenticated that provider provides us with a authorization code and sometimes a JWT id_token (the latter depends on the provider).
  • We POST those params to our API through the appropriate callback endpoint which lets the omniauth- gem handle the callback and when successfull we return our user object
import { SignInWithApple } from '@capacitor-community/apple-sign-in';

const handleAppleSignin = async () => {
   const { response } = await SignInWithApple.authorize({ ... })

  const user = fetch('your.api.domain/auth/apple/callback', {
    data: JSON.stringify({
      code: response.authorizationCode,
      id_token: response.identityToken,
    }),
    method: 'POST'
  }
};

Issues

I don't really remember the exact order in which I encountered each issues so I'll just list them as I remember them, sorry for not being more specific.

There was a CORS error because we're using Hybrid mode. The solution was describe in #54 (comment) which specified having to add provider_ignores_state: true to the config when skipping to the callback step which is the case in Hybrid mode.

This needs to be documented

  config.omniauth :apple, Rails.application.credentials.apple_app_bundle_id, '',
                  key_id: Rails.application.credentials.apple_key_id,
                  pem: Rails.application.credentials.apple_pem,
                  provider_ignores_state: true, # <- ADD THIS LINE
                  scope: 'email',
                  team_id: Rails.application.credentials.apple_team_id

There was a invalid_credentials error that I took a long time to figure out. The problem was that omniauth-apple only checks the request.params when looking for the code and id_token but never reads the request.body if they are missing. Because with other providers we were sending those credentials in the request's body we did it here again thinking that would work.

To fix this we updated our API request to include the params in the query instead of in the request's body

This needs to be documented

  const user = fetch(`your.api.domain/auth/apple/callback?code=${response.authorizationCode}&id_token=${response.identityToken}`, {
    method: 'POST'
  }

There was an error notifying a mismatch in the redirect_uri. That is because when verifying a authorization code the redirect_uri needs to be the same as when that authorization code was obtained which means the client-side application's redirect_uri needs to be send to the backend too and used in the callback.

This is also the case with other omniauth-<provider> gem and we just forgot to include it this time which we corrected.

This needs to be documented

  const user = fetch(`...&redirect_uri=${YOUR_REDIRECT_URI}`, {
    method: 'POST'
  }

The problem is that this still didn't work and that is because omniauth-apple does not allow using a redirect_uri coming from the POST request.

options[:redirect_uri] || (full_host + callback_path)

This needs to be fixed with a pull request
In the meantime this issue can be monkey patched by initializing a custom class that will override this gem

# config/initializers/omniauth-apple-monkey-patch.rb
module OmniAuth
  module Strategies
    class Apple
      def callback_url
        # options[:redirect_uri] || (full_host + callback_path)
        request.params['redirect_uri'] || options[:redirect_uri] || (full_host + callback_path)
      end
    end
  end
end

There was an error saying the client_id provided to the middleware was incorrect.
This issue is describe in #68 and a solution is provided in #68 (comment) which is to add a authorized_client_ids option to the middleware and give it the apple client id

This option needs to be documented

  config.omniauth :apple, Rails.application.credentials.apple_app_bundle_id, '',
                  authorized_client_ids: [Rails.application.credentials.apple_app_bundle_id], # <- ADD THIS LINE
                  key_id: Rails.application.credentials.apple_key_id,
                  pem: Rails.application.credentials.apple_pem,
                  provider_ignores_state: true,
                  scope: 'email',
                  team_id: Rails.application.credentials.apple_team_id

While this solution worked, it ended up being a monkey patch

We ended up discovering that the real reason why the provided client_id was not working as intended while providing it again in the authorized_client_ids worked was because when defining the @client_id omniauth-apple omits the options.client_id from its validation

id_info[:aud] if options.authorized_client_ids.include? id_info[:aud]

This needs to be fixed with a pull request
In the meantime this issue can be monkey patched by initializing a custom class that will override this gem

# config/initializers/omniauth-apple-monkey-patch.rb
module OmniAuth
  module Strategies
    class Apple

    private

      def client_id
        @client_id ||= if id_info.nil?
                         options.client_id
                       else
                         # id_info[:aud] if options.authorized_client_ids.include? id_info[:aud]
                         id_info[:aud] if [options.client_id].concat(options.authorized_client_ids).include?(id_info[:aud])
                       end
      end
    end
  end
end

By doing it is not necessary to add a authorized_client_ids option to the middleware.


There was an error with missing nonce in the id_token.
This issue is being discussed in #102 already.

Basically whether id_token[:nonce_supported] is true and whether id_token[:nonce] is present is not related but omniauth-apple consider them paired which in our case meant we had nonce_supported: true and nonce: nil which broke.

verify_nonce!(id_token) if id_token[:nonce_supported]

A solution as been proposed in #111 should be considered for approval.
In the meantime this issue can be monkey patched by initializing a custom class that will override this gem

# config/initializers/omniauth-apple-monkey-patch.rb
module OmniAuth
  module Strategies
    class Apple

    private

      def verify_nonce!(id_token)
        # invalid_claim! :nonce unless id_token[:nonce] && id_token[:nonce] == stored_nonce
        invalid_claim! :nonce unless id_token[:nonce] == stored_nonce
      end
    end
  end
end

Tasks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant