omniauth-apple2 1.0.0 → 1.0.2
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 -64
- 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: 82df12438cb2d66c9d326a6f245fe3680e9bb382492dacbbe2f7fecd792b72f1
|
|
4
|
+
data.tar.gz: c4115da4b861f34eec461067b9cd1e69071c9a21b642ad8a96cb98c968c9e78d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 391754c3471100c6f76bc31c3c86479e8d333c32f23bc5d0575a9722e995151d5927810e886d3c6f01a8d3173b700fc3aa08ec8a19c633d499cf3aed76867e50
|
|
7
|
+
data.tar.gz: 9f303209bb4ce58a5b9f833d68d6ec7d93a2ed8b6d567b709b499a1132d69262f43484d23c53b8bc9b2bcaed7408a111c9175a871999dba67c93efb7c1d26e39
|
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,16 @@ module OmniAuth
|
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
def verify_nonce!(payload)
|
|
141
|
-
return unless payload.key?('nonce')
|
|
142
|
-
|
|
143
141
|
expected_nonce = stored_nonce
|
|
144
|
-
return if payload[
|
|
142
|
+
return if blank?(payload["nonce"]) && !true_claim?(payload["nonce_supported"])
|
|
143
|
+
return if payload["nonce"] == expected_nonce
|
|
145
144
|
|
|
146
|
-
raise CallbackError.new(:id_token_nonce_invalid,
|
|
145
|
+
raise CallbackError.new(:id_token_nonce_invalid, "nonce is missing or does not match")
|
|
147
146
|
end
|
|
148
147
|
|
|
149
148
|
def fetch_jwk(expected_kid)
|
|
150
149
|
jwks = fetch_jwks_keys
|
|
151
|
-
matching_key = jwks.find { |key| key[
|
|
150
|
+
matching_key = jwks.find { |key| key["kid"] == expected_kid }
|
|
152
151
|
raise CallbackError.new(:jwks_key_not_found, expected_kid) unless matching_key
|
|
153
152
|
|
|
154
153
|
JWT::JWK.import(matching_key)
|
|
@@ -161,7 +160,7 @@ module OmniAuth
|
|
|
161
160
|
response = Net::HTTP.get_response(uri)
|
|
162
161
|
raise CallbackError.new(:jwks_fetch_failed, response.code) unless response.is_a?(Net::HTTPSuccess)
|
|
163
162
|
|
|
164
|
-
JSON.parse(response.body).fetch(
|
|
163
|
+
JSON.parse(response.body).fetch("keys", [])
|
|
165
164
|
end
|
|
166
165
|
|
|
167
166
|
def valid_audiences
|
|
@@ -169,9 +168,9 @@ module OmniAuth
|
|
|
169
168
|
end
|
|
170
169
|
|
|
171
170
|
def extract_kid(token)
|
|
172
|
-
header_segment = token.split(
|
|
171
|
+
header_segment = token.split(".").first
|
|
173
172
|
decoded_header = Base64.urlsafe_decode64(pad_base64(header_segment))
|
|
174
|
-
JSON.parse(decoded_header)[
|
|
173
|
+
JSON.parse(decoded_header)["kid"]
|
|
175
174
|
end
|
|
176
175
|
|
|
177
176
|
def decode_payload(token, jwk)
|
|
@@ -186,7 +185,7 @@ module OmniAuth
|
|
|
186
185
|
|
|
187
186
|
def decode_options
|
|
188
187
|
{
|
|
189
|
-
algorithms: [
|
|
188
|
+
algorithms: ["RS256"],
|
|
190
189
|
iss: ISSUER,
|
|
191
190
|
verify_iss: true,
|
|
192
191
|
aud: valid_audiences,
|
|
@@ -201,7 +200,7 @@ module OmniAuth
|
|
|
201
200
|
end
|
|
202
201
|
|
|
203
202
|
def client_secret
|
|
204
|
-
JWT.encode(client_secret_claims, private_key,
|
|
203
|
+
JWT.encode(client_secret_claims, private_key, "ES256", client_secret_headers)
|
|
205
204
|
end
|
|
206
205
|
|
|
207
206
|
def private_key
|
|
@@ -226,15 +225,15 @@ module OmniAuth
|
|
|
226
225
|
end
|
|
227
226
|
|
|
228
227
|
def new_nonce
|
|
229
|
-
session[
|
|
228
|
+
session["omniauth.nonce"] = SecureRandom.urlsafe_base64(16)
|
|
230
229
|
end
|
|
231
230
|
|
|
232
231
|
def stored_nonce
|
|
233
|
-
session.delete(
|
|
232
|
+
session.delete("omniauth.nonce")
|
|
234
233
|
end
|
|
235
234
|
|
|
236
235
|
def true_claim?(value)
|
|
237
|
-
[true,
|
|
236
|
+
[true, "true"].include?(value)
|
|
238
237
|
end
|
|
239
238
|
|
|
240
239
|
def blank?(value)
|
|
@@ -242,16 +241,16 @@ module OmniAuth
|
|
|
242
241
|
end
|
|
243
242
|
|
|
244
243
|
def pad_base64(value)
|
|
245
|
-
value + (
|
|
244
|
+
value + ("=" * ((4 - (value.length % 4)) % 4))
|
|
246
245
|
end
|
|
247
246
|
end
|
|
248
247
|
|
|
249
248
|
# Backward-compatible strategy name for existing callback paths.
|
|
250
249
|
class Apple < Apple2
|
|
251
|
-
option :name,
|
|
250
|
+
option :name, "apple"
|
|
252
251
|
end
|
|
253
252
|
end
|
|
254
253
|
end
|
|
255
254
|
|
|
256
|
-
OmniAuth.config.add_camelization
|
|
257
|
-
OmniAuth.config.add_camelization
|
|
255
|
+
OmniAuth.config.add_camelization "apple2", "Apple2"
|
|
256
|
+
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
|