Skip to content

Commit

Permalink
Add support for Apple authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
tomhughes committed Feb 9, 2023
1 parent 63d77a4 commit 376fe79
Show file tree
Hide file tree
Showing 13 changed files with 352 additions and 1 deletion.
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -68,6 +68,7 @@ gem "rack-uri_sanitizer"

# Omniauth for authentication
gem "omniauth", "~> 2.0.2"
gem "omniauth-apple"
gem "omniauth-facebook"
gem "omniauth-github"
gem "omniauth-google-oauth2", ">= 0.6.0"
Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Expand Up @@ -322,6 +322,9 @@ GEM
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
rack-protection
omniauth-apple (1.0.2)
jwt
omniauth-oauth2
omniauth-facebook (9.0.0)
omniauth-oauth2 (~> 1.2)
omniauth-github (2.0.1)
Expand Down Expand Up @@ -565,6 +568,7 @@ DEPENDENCIES
minitest (~> 5.1)
oauth-plugin (>= 0.5.1)
omniauth (~> 2.0.2)
omniauth-apple
omniauth-facebook
omniauth-github
omniauth-google-oauth2 (>= 0.6.0)
Expand Down
Binary file added app/assets/images/apple.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions app/assets/images/apple.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion app/controllers/users_controller.rb
Expand Up @@ -250,7 +250,7 @@ def auth_success
when "openid"
uid.match(%r{https://www.google.com/accounts/o8/id?(.*)}) ||
uid.match(%r{https://me.yahoo.com/(.*)})
when "google", "facebook"
when "google", "apple", "facebook"
true
else
false
Expand Down
3 changes: 3 additions & 0 deletions app/views/sessions/new.html.erb
Expand Up @@ -32,6 +32,9 @@
<% if Settings.key?(:google_auth_id) -%>
<li><%= auth_button "google", "google" %></li>
<% end -%>
<% if Settings.key?(:apple_auth_id) -%>
<li><%= auth_button "apple", "apple" %></li>
<% end -%>
<% if Settings.key?(:facebook_auth_id) -%>
<li><%= auth_button "facebook", "facebook" %></li>
<% end -%>
Expand Down
6 changes: 6 additions & 0 deletions config/initializers/omniauth.rb
Expand Up @@ -23,16 +23,22 @@

openid_options = { :name => "openid", :store => openid_store }
google_options = { :name => "google", :scope => "email", :access_type => "online" }
apple_options = { :name => "apple", :scope => "email name" }
facebook_options = { :name => "facebook", :scope => "email", :client_options => { :site => "https://graph.facebook.com/v4.0", :authorize_url => "https://www.facebook.com/v4.0/dialog/oauth" } }
windowslive_options = { :name => "windowslive", :scope => "wl.signin,wl.emails" }
github_options = { :name => "github", :scope => "user:email" }
wikipedia_options = { :name => "wikipedia", :client_options => { :site => "https://meta.wikimedia.org" } }

google_options[:openid_realm] = Settings.google_openid_realm if Settings.key?(:google_openid_realm)

apple_options[:team_id] = Settings.apple_team_id if Settings.key?(:apple_team_id)
apple_options[:key_id] = Settings.apple_key_id if Settings.key?(:apple_key_id)
apple_options[:pem] = Settings.apple_private_key if Settings.key?(:apple_private_key)

Rails.application.config.middleware.use OmniAuth::Builder do
provider :openid, openid_options
provider :google_oauth2, Settings.google_auth_id, Settings.google_auth_secret, google_options if Settings.key?(:google_auth_id)
provider :apple, Settings.apple_auth_id, "", apple_options if Settings.key?(:apple_auth_id)
provider :facebook, Settings.facebook_auth_id, Settings.facebook_auth_secret, facebook_options if Settings.key?(:facebook_auth_id)
provider :windowslive, Settings.windowslive_auth_id, Settings.windowslive_auth_secret, windowslive_options if Settings.key?(:windowslive_auth_id)
provider :github, Settings.github_auth_id, Settings.github_auth_secret, github_options if Settings.key?(:github_auth_id)
Expand Down
4 changes: 4 additions & 0 deletions config/locales/en.yml
Expand Up @@ -213,6 +213,7 @@ en:
none: None
openid: OpenID
google: Google
apple: Apple
facebook: Facebook
windowslive: Windows Live
github: GitHub
Expand Down Expand Up @@ -1789,6 +1790,9 @@ en:
google:
title: Login with Google
alt: Login with a Google OpenID
apple:
title: Login with Apple
alt: Login with an Apple ID
facebook:
title: Login with Facebook
alt: Login with a Facebook Account
Expand Down
4 changes: 4 additions & 0 deletions config/settings.yml
Expand Up @@ -108,6 +108,10 @@ fossgis_valhalla_url: "https://valhalla1.openstreetmap.de/route"
#github_auth_secret: ""
#wikipedia_auth_id: ""
#wikipedia_auth_secret: ""
#apple_auth_id: ""
#apple_team_id: ""
#apple_key_id: ""
#apple_private_key: ""
# Thunderforest authentication details
#thunderforest_key: ""
# Key for generating TOTP tokens
Expand Down
4 changes: 4 additions & 0 deletions config/settings/test.yml
Expand Up @@ -12,6 +12,10 @@ github_auth_id: "dummy"
github_auth_secret: "dummy"
wikipedia_auth_id: "dummy"
wikipedia_auth_secret: "dummy"
apple_auth_id: "dummy"
apple_team_id: "dummy"
apple_key_id: "dummy"
apple_private_key: "dummy"
# Server URL for testing
server_url: "test.host"
# Storage services for testing
Expand Down
1 change: 1 addition & 0 deletions lib/auth.rb
Expand Up @@ -7,6 +7,7 @@ def self.providers
I18n.t("auth.providers.openid") => "openid"
}.tap do |providers|
providers[I18n.t("auth.providers.google")] = "google" if Settings.key?(:google_auth_id)
providers[I18n.t("auth.providers.apple")] = "apple" if Settings.key?(:apple_auth_id)
providers[I18n.t("auth.providers.facebook")] = "facebook" if Settings.key?(:facebook_auth_id)
providers[I18n.t("auth.providers.windowslive")] = "windowslive" if Settings.key?(:windowslive_auth_id)
providers[I18n.t("auth.providers.github")] = "github" if Settings.key?(:github_auth_id)
Expand Down
155 changes: 155 additions & 0 deletions test/integration/user_creation_test.rb
Expand Up @@ -10,6 +10,7 @@ def setup
def teardown
OmniAuth.config.mock_auth[:openid] = nil
OmniAuth.config.mock_auth[:google] = nil
OmniAuth.config.mock_auth[:apple] = nil
OmniAuth.config.mock_auth[:facebook] = nil
OmniAuth.config.mock_auth[:windowslive] = nil
OmniAuth.config.mock_auth[:github] = nil
Expand Down Expand Up @@ -537,6 +538,160 @@ def test_user_create_google_redirect
assert_template "site/welcome"
end

def test_user_create_apple_success
new_email = "newtester-apple@osm.org"
display_name = "new_tester-apple"
password = "testtest"

OmniAuth.config.add_mock(:apple, :uid => "123454321", :info => { "email" => new_email })

assert_difference("User.count") do
assert_no_difference("ActionMailer::Base.deliveries.size") do
perform_enqueued_jobs do
post "/user/new",
:params => { :user => { :email => new_email,
:email_confirmation => new_email,
:display_name => display_name,
:auth_provider => "apple",
:pass_crypt => "",
:pass_crypt_confirmation => "" } }
assert_response :redirect
assert_redirected_to auth_path(:provider => "apple", :origin => "/user/new")
post response.location
assert_response :redirect
assert_redirected_to auth_success_path(:provider => "apple")
follow_redirect!
assert_response :redirect
assert_redirected_to "/user/terms"
post "/user/save",
:params => { :user => { :email => new_email,
:email_confirmation => new_email,
:display_name => display_name,
:auth_provider => "apple",
:auth_uid => "123454321",
:pass_crypt => password,
:pass_crypt_confirmation => password },
:read_ct => 1, :read_tou => 1 }
assert_response :redirect
assert_redirected_to welcome_path
follow_redirect!
end
end
end

# Check the page
assert_response :success
assert_template "site/welcome"

ActionMailer::Base.deliveries.clear
end

def test_user_create_apple_failure
OmniAuth.config.mock_auth[:apple] = :connection_failed

new_email = "newtester-apple2@osm.org"
display_name = "new_tester-apple2"
assert_difference("User.count", 0) do
assert_difference("ActionMailer::Base.deliveries.size", 0) do
perform_enqueued_jobs do
post "/user/new",
:params => { :user => { :email => new_email,
:email_confirmation => new_email,
:display_name => display_name,
:auth_provider => "apple",
:pass_crypt => "",
:pass_crypt_confirmation => "" } }
assert_response :redirect
assert_redirected_to auth_path(:provider => "apple", :origin => "/user/new")
post response.location
assert_response :redirect
assert_redirected_to auth_success_path(:provider => "apple")
follow_redirect!
assert_response :redirect
assert_redirected_to auth_failure_path(:strategy => "apple", :message => "connection_failed", :origin => "/user/new")
follow_redirect!
assert_response :redirect
follow_redirect!
assert_response :success
assert_template "users/new"
end
end
end

ActionMailer::Base.deliveries.clear
end

def test_user_create_apple_redirect
OmniAuth.config.add_mock(:apple, :uid => "123454321", :extra => {
:id_info => { "openid_id" => "http://localhost:1123/new.tester" }
})

new_email = "redirect_tester_apple@osm.org"
display_name = "redirect_tester_apple"
# nothing special about this page, just need a protected page to redirect back to.
referer = "/traces/mine"
assert_difference("User.count") do
assert_difference("ActionMailer::Base.deliveries.size", 1) do
perform_enqueued_jobs do
post "/user/new",
:params => { :user => { :email => new_email,
:email_confirmation => new_email,
:display_name => display_name,
:auth_provider => "apple",
:pass_crypt => "",
:pass_crypt_confirmation => "" },
:referer => referer }
assert_response :redirect
assert_redirected_to auth_path(:provider => "apple", :origin => "/user/new")
post response.location
assert_response :redirect
assert_redirected_to auth_success_path(:provider => "apple")
follow_redirect!
assert_response :redirect
assert_redirected_to "/user/terms"
post "/user/save",
:params => { :user => { :email => new_email,
:email_confirmation => new_email,
:display_name => display_name,
:auth_provider => "apple",
:auth_uid => "http://localhost:1123/new.tester",
:pass_crypt => "testtest",
:pass_crypt_confirmation => "testtest" },
:read_ct => 1, :read_tou => 1 }
follow_redirect!
end
end
end

# Check the e-mail
register_email = ActionMailer::Base.deliveries.first

assert_equal register_email.to.first, new_email
# Check that the confirm account url is correct
confirm_regex = Regexp.new("/user/redirect_tester_apple/confirm\\?confirm_string=([a-zA-Z0-9]*)")
email_text_parts(register_email).each do |part|
assert_match confirm_regex, part.body.to_s
end
confirm_string = email_text_parts(register_email).first.body.match(confirm_regex)[1]

# Check the page
assert_response :success
assert_template "confirmations/confirm"

ActionMailer::Base.deliveries.clear

# Go to the confirmation page
get "/user/#{display_name}/confirm", :params => { :confirm_string => confirm_string }
assert_response :success
assert_template "confirmations/confirm"

post "/user/#{display_name}/confirm", :params => { :confirm_string => confirm_string }
assert_response :redirect
follow_redirect!
assert_response :success
assert_template "site/welcome"
end

def test_user_create_facebook_success
new_email = "newtester-facebook@osm.org"
display_name = "new_tester-facebook"
Expand Down

0 comments on commit 376fe79

Please sign in to comment.