omniauth-apple2 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -2
- data/lib/omniauth/apple2/version.rb +1 -1
- data/lib/omniauth/apple2.rb +2 -2
- data/lib/omniauth/strategies/apple2.rb +63 -63
- data/lib/omniauth-apple2.rb +1 -1
- data/omniauth-apple2.gemspec +22 -22
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 43cb0d220327e9f7fb7dcfe341ec34c079dbb4b21c5578d415c6e224c2462d37
|
|
4
|
+
data.tar.gz: 31f0f2eb78846e54026b86fb8b26ad8f25729f6fd406b35087dfb3690a581339
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b8eb303c3f9ef98820f7b003ff6fd5d7e3468779f21a1839d6c9a57721ceadaf5063d96e77d65bfa35ea33444fa27108907be03d3eade5069a4da856ef933f3
|
|
7
|
+
data.tar.gz: 55b220301197a16fffdb1c42e5a7da48ba9617686993b908adcee3ed2be26f553b0e50975370e67cf6e06217bf45e1e9137368cd7b7ee7c737425b8c10fcf67d
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# OmniAuth
|
|
1
|
+
# OmniAuth Apple Strategy
|
|
2
2
|
|
|
3
3
|
[](https://github.com/icoretech/omniauth-apple2/actions/workflows/test.yml?query=branch%3Amain)
|
|
4
4
|
[](https://badge.fury.io/rb/omniauth-apple2)
|
|
@@ -120,7 +120,8 @@ Example payload from `request.env['omniauth.auth']` (real flow shape, anonymized
|
|
|
120
120
|
|
|
121
121
|
```bash
|
|
122
122
|
bundle install
|
|
123
|
-
bundle exec
|
|
123
|
+
bundle exec standardrb --fix
|
|
124
|
+
bundle exec rake test_unit
|
|
124
125
|
RAILS_VERSION='~> 8.1.0' bundle exec rake test_rails_integration
|
|
125
126
|
```
|
|
126
127
|
|
data/lib/omniauth/apple2.rb
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
3
|
+
require "base64"
|
|
4
|
+
require "jwt"
|
|
5
|
+
require "net/http"
|
|
6
|
+
require "omniauth-oauth2"
|
|
7
|
+
require "openssl"
|
|
8
|
+
require "securerandom"
|
|
9
|
+
require "uri"
|
|
10
10
|
|
|
11
11
|
module OmniAuth
|
|
12
12
|
module Strategies
|
|
13
13
|
# OmniAuth strategy for Sign in with Apple.
|
|
14
14
|
class Apple2 < OmniAuth::Strategies::OAuth2
|
|
15
|
-
ISSUER =
|
|
16
|
-
JWKS_URL =
|
|
15
|
+
ISSUER = "https://appleid.apple.com"
|
|
16
|
+
JWKS_URL = "https://appleid.apple.com/auth/keys"
|
|
17
17
|
|
|
18
|
-
option :name,
|
|
18
|
+
option :name, "apple2"
|
|
19
19
|
option :authorize_options, %i[scope state response_mode response_type nonce]
|
|
20
|
-
option :scope,
|
|
21
|
-
option :response_mode,
|
|
22
|
-
option :response_type,
|
|
20
|
+
option :scope, "email name"
|
|
21
|
+
option :response_mode, "form_post"
|
|
22
|
+
option :response_type, "code"
|
|
23
23
|
option :authorized_client_ids, []
|
|
24
24
|
|
|
25
25
|
option :client_options,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
uid { id_info[
|
|
26
|
+
site: ISSUER,
|
|
27
|
+
authorize_url: "/auth/authorize",
|
|
28
|
+
token_url: "/auth/token",
|
|
29
|
+
auth_scheme: :request_body,
|
|
30
|
+
connection_opts: {
|
|
31
|
+
headers: {
|
|
32
|
+
user_agent: "icoretech-omniauth-apple2 gem",
|
|
33
|
+
accept: "application/json"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
uid { id_info["sub"] }
|
|
38
38
|
|
|
39
39
|
info do
|
|
40
40
|
{
|
|
41
41
|
name: full_name,
|
|
42
|
-
email: id_info[
|
|
43
|
-
first_name: user_info.dig(
|
|
44
|
-
last_name: user_info.dig(
|
|
45
|
-
email_verified: true_claim?(id_info[
|
|
46
|
-
is_private_email: true_claim?(id_info[
|
|
42
|
+
email: id_info["email"],
|
|
43
|
+
first_name: user_info.dig("name", "firstName"),
|
|
44
|
+
last_name: user_info.dig("name", "lastName"),
|
|
45
|
+
email_verified: true_claim?(id_info["email_verified"]),
|
|
46
|
+
is_private_email: true_claim?(id_info["is_private_email"])
|
|
47
47
|
}.compact
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
credentials do
|
|
51
51
|
{
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
"token" => access_token.token,
|
|
53
|
+
"refresh_token" => access_token.refresh_token,
|
|
54
|
+
"expires_at" => access_token.expires_at,
|
|
55
|
+
"expires" => access_token.expires?,
|
|
56
|
+
"scope" => token_scope
|
|
57
57
|
}.compact
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
extra do
|
|
61
61
|
{
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
"raw_info" => {
|
|
63
|
+
"id_info" => id_info,
|
|
64
|
+
"user_info" => user_info,
|
|
65
|
+
"id_token" => raw_id_token
|
|
66
66
|
}.compact
|
|
67
67
|
}
|
|
68
68
|
end
|
|
@@ -81,7 +81,7 @@ module OmniAuth
|
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def query_string
|
|
84
|
-
return
|
|
84
|
+
return "" if request.params["code"]
|
|
85
85
|
|
|
86
86
|
super
|
|
87
87
|
end
|
|
@@ -95,14 +95,14 @@ module OmniAuth
|
|
|
95
95
|
def id_info
|
|
96
96
|
@id_info ||= begin
|
|
97
97
|
token = raw_id_token
|
|
98
|
-
raise CallbackError.new(:id_token_missing,
|
|
98
|
+
raise CallbackError.new(:id_token_missing, "id_token is missing") if blank?(token)
|
|
99
99
|
|
|
100
100
|
decode_and_verify_id_token(token)
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def user_info
|
|
105
|
-
raw_user = request.params[
|
|
105
|
+
raw_user = request.params["user"]
|
|
106
106
|
return {} if blank?(raw_user)
|
|
107
107
|
|
|
108
108
|
@user_info ||= JSON.parse(raw_user)
|
|
@@ -111,19 +111,19 @@ module OmniAuth
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
def raw_id_token
|
|
114
|
-
request.params[
|
|
114
|
+
request.params["id_token"] || access_token&.params&.dig("id_token")
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
def full_name
|
|
118
|
-
parts = [user_info.dig(
|
|
119
|
-
return parts.join(
|
|
118
|
+
parts = [user_info.dig("name", "firstName"), user_info.dig("name", "lastName")].compact
|
|
119
|
+
return parts.join(" ") unless parts.empty?
|
|
120
120
|
|
|
121
|
-
id_info[
|
|
121
|
+
id_info["email"]
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
def token_scope
|
|
125
125
|
token_params = access_token.respond_to?(:params) ? access_token.params : {}
|
|
126
|
-
token_params[
|
|
126
|
+
token_params["scope"] || (access_token["scope"] if access_token.respond_to?(:[]))
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
def decode_and_verify_id_token(token)
|
|
@@ -138,17 +138,17 @@ module OmniAuth
|
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
def verify_nonce!(payload)
|
|
141
|
-
return unless payload.key?(
|
|
141
|
+
return unless payload.key?("nonce")
|
|
142
142
|
|
|
143
143
|
expected_nonce = stored_nonce
|
|
144
|
-
return if payload[
|
|
144
|
+
return if payload["nonce"] == expected_nonce
|
|
145
145
|
|
|
146
|
-
raise CallbackError.new(:id_token_nonce_invalid,
|
|
146
|
+
raise CallbackError.new(:id_token_nonce_invalid, "nonce does not match")
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
def fetch_jwk(expected_kid)
|
|
150
150
|
jwks = fetch_jwks_keys
|
|
151
|
-
matching_key = jwks.find { |key| key[
|
|
151
|
+
matching_key = jwks.find { |key| key["kid"] == expected_kid }
|
|
152
152
|
raise CallbackError.new(:jwks_key_not_found, expected_kid) unless matching_key
|
|
153
153
|
|
|
154
154
|
JWT::JWK.import(matching_key)
|
|
@@ -161,7 +161,7 @@ module OmniAuth
|
|
|
161
161
|
response = Net::HTTP.get_response(uri)
|
|
162
162
|
raise CallbackError.new(:jwks_fetch_failed, response.code) unless response.is_a?(Net::HTTPSuccess)
|
|
163
163
|
|
|
164
|
-
JSON.parse(response.body).fetch(
|
|
164
|
+
JSON.parse(response.body).fetch("keys", [])
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
def valid_audiences
|
|
@@ -169,9 +169,9 @@ module OmniAuth
|
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
def extract_kid(token)
|
|
172
|
-
header_segment = token.split(
|
|
172
|
+
header_segment = token.split(".").first
|
|
173
173
|
decoded_header = Base64.urlsafe_decode64(pad_base64(header_segment))
|
|
174
|
-
JSON.parse(decoded_header)[
|
|
174
|
+
JSON.parse(decoded_header)["kid"]
|
|
175
175
|
end
|
|
176
176
|
|
|
177
177
|
def decode_payload(token, jwk)
|
|
@@ -186,7 +186,7 @@ module OmniAuth
|
|
|
186
186
|
|
|
187
187
|
def decode_options
|
|
188
188
|
{
|
|
189
|
-
algorithms: [
|
|
189
|
+
algorithms: ["RS256"],
|
|
190
190
|
iss: ISSUER,
|
|
191
191
|
verify_iss: true,
|
|
192
192
|
aud: valid_audiences,
|
|
@@ -201,7 +201,7 @@ module OmniAuth
|
|
|
201
201
|
end
|
|
202
202
|
|
|
203
203
|
def client_secret
|
|
204
|
-
JWT.encode(client_secret_claims, private_key,
|
|
204
|
+
JWT.encode(client_secret_claims, private_key, "ES256", client_secret_headers)
|
|
205
205
|
end
|
|
206
206
|
|
|
207
207
|
def private_key
|
|
@@ -226,15 +226,15 @@ module OmniAuth
|
|
|
226
226
|
end
|
|
227
227
|
|
|
228
228
|
def new_nonce
|
|
229
|
-
session[
|
|
229
|
+
session["omniauth.nonce"] = SecureRandom.urlsafe_base64(16)
|
|
230
230
|
end
|
|
231
231
|
|
|
232
232
|
def stored_nonce
|
|
233
|
-
session.delete(
|
|
233
|
+
session.delete("omniauth.nonce")
|
|
234
234
|
end
|
|
235
235
|
|
|
236
236
|
def true_claim?(value)
|
|
237
|
-
[true,
|
|
237
|
+
[true, "true"].include?(value)
|
|
238
238
|
end
|
|
239
239
|
|
|
240
240
|
def blank?(value)
|
|
@@ -242,16 +242,16 @@ module OmniAuth
|
|
|
242
242
|
end
|
|
243
243
|
|
|
244
244
|
def pad_base64(value)
|
|
245
|
-
value + (
|
|
245
|
+
value + ("=" * ((4 - (value.length % 4)) % 4))
|
|
246
246
|
end
|
|
247
247
|
end
|
|
248
248
|
|
|
249
249
|
# Backward-compatible strategy name for existing callback paths.
|
|
250
250
|
class Apple < Apple2
|
|
251
|
-
option :name,
|
|
251
|
+
option :name, "apple"
|
|
252
252
|
end
|
|
253
253
|
end
|
|
254
254
|
end
|
|
255
255
|
|
|
256
|
-
OmniAuth.config.add_camelization
|
|
257
|
-
OmniAuth.config.add_camelization
|
|
256
|
+
OmniAuth.config.add_camelization "apple2", "Apple2"
|
|
257
|
+
OmniAuth.config.add_camelization "apple", "Apple"
|
data/lib/omniauth-apple2.rb
CHANGED
data/omniauth-apple2.gemspec
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
lib = File.expand_path(
|
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
-
require
|
|
5
|
+
require "omniauth/apple2/version"
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |spec|
|
|
8
|
-
spec.name =
|
|
8
|
+
spec.name = "omniauth-apple2"
|
|
9
9
|
spec.version = OmniAuth::Apple2::VERSION
|
|
10
|
-
spec.authors = [
|
|
11
|
-
spec.email = [
|
|
10
|
+
spec.authors = ["Claudio Poli"]
|
|
11
|
+
spec.email = ["masterkain@gmail.com"]
|
|
12
12
|
|
|
13
|
-
spec.summary =
|
|
14
|
-
spec.description =
|
|
15
|
-
spec.homepage =
|
|
16
|
-
spec.license =
|
|
17
|
-
spec.required_ruby_version =
|
|
13
|
+
spec.summary = "OmniAuth strategy for Sign in with Apple authentication."
|
|
14
|
+
spec.description = "OAuth2 strategy for OmniAuth that authenticates users with Sign in with Apple."
|
|
15
|
+
spec.homepage = "https://github.com/icoretech/omniauth-apple2"
|
|
16
|
+
spec.license = "MIT"
|
|
17
|
+
spec.required_ruby_version = ">= 3.2"
|
|
18
18
|
|
|
19
|
-
spec.metadata[
|
|
20
|
-
spec.metadata[
|
|
21
|
-
spec.metadata[
|
|
22
|
-
spec.metadata[
|
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/icoretech/omniauth-apple2"
|
|
20
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/icoretech/omniauth-apple2/issues"
|
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/icoretech/omniauth-apple2/releases"
|
|
22
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
23
23
|
|
|
24
24
|
spec.files = Dir[
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
"lib/**/*.rb",
|
|
26
|
+
"README*",
|
|
27
|
+
"LICENSE*",
|
|
28
|
+
"*.gemspec"
|
|
29
29
|
]
|
|
30
|
-
spec.require_paths = [
|
|
30
|
+
spec.require_paths = ["lib"]
|
|
31
31
|
|
|
32
|
-
spec.add_dependency
|
|
33
|
-
spec.add_dependency
|
|
34
|
-
spec.add_dependency
|
|
32
|
+
spec.add_dependency "cgi", ">= 0.3.6"
|
|
33
|
+
spec.add_dependency "jwt", ">= 2.8"
|
|
34
|
+
spec.add_dependency "omniauth-oauth2", ">= 1.8", "< 2.0"
|
|
35
35
|
end
|