standard_id-apple 0.1.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63120ccfea7a05fb35a42c90548f441a7d719aa3d5a46e0bb9d72ec97e1e5129
4
- data.tar.gz: '0857aa7e1bbafae5621ce60aa2c3364b01931a836b8e0adc121b50bcfacf42af'
3
+ metadata.gz: 3c67afb888da7a7a20542d46149eaf86215d02de9c097a757b98223f4c992811
4
+ data.tar.gz: b905edd16718f7fee932b567250a7caa5136d1cf64f82030668b7bcb8dd40d87
5
5
  SHA512:
6
- metadata.gz: da5566c05904516299901bc7cbafb42e97c59c6bfb516959c8a2e77b55afd34f976e07c503ac94781681b972349c82519f4053d97004c7dea6967738ded0fe4a
7
- data.tar.gz: 95da64b4f2c8deee61e18867133aa36d7ddea30985c276c266dcba0e8448712de8807c1040f224783621cb7319a998264c982ed0b96cb1e3b52a91447bfd1e0f
6
+ metadata.gz: fdd51606e4a08272c10bae448cecbf0472e8449a5679229013138b5fed0abef6f2e404ef1ef495731a1744c58538fbfc1c141af974ad02c825212a5a51adffc1
7
+ data.tar.gz: 58e35ebc3804fd52719774db5a63229918e0c89462dca2b7f654a8a57820389549044c1ca42e27cf46155afc6617942084e1ce41b650866c9ba0a049e7599289
@@ -12,40 +12,47 @@ module StandardId
12
12
  JWKS_URI = "#{ISSUER}/auth/keys".freeze
13
13
  DEFAULT_SCOPE = "name email".freeze
14
14
  DEFAULT_RESPONSE_MODE = "form_post".freeze
15
+ AUTHORIZATION_PARAM_DEFAULTS = {
16
+ scope: DEFAULT_SCOPE,
17
+ response_mode: DEFAULT_RESPONSE_MODE
18
+ }.freeze
15
19
 
16
20
  class << self
17
21
  def provider_name
18
22
  "apple"
19
23
  end
20
24
 
21
- def authorization_url(state:, redirect_uri:, **options)
22
- scope = options[:scope] || DEFAULT_SCOPE
23
- response_mode = options[:response_mode] || DEFAULT_RESPONSE_MODE
25
+ def supported_authorization_params
26
+ [:nonce, :scope, :response_mode]
27
+ end
24
28
 
29
+ def authorization_url(state:, redirect_uri:, **options)
25
30
  ensure_basic_credentials!
26
31
 
27
32
  query = {
28
33
  client_id: StandardId.config.apple_client_id,
29
- redirect_uri: redirect_uri,
34
+ redirect_uri:,
30
35
  response_type: "code",
31
- scope: scope,
32
- response_mode: response_mode,
33
- state: state
36
+ state:
34
37
  }
35
38
 
36
- "#{AUTH_ENDPOINT}?#{URI.encode_www_form(query)}"
39
+ supported_authorization_params.each do |param|
40
+ query[param] = options[param] || AUTHORIZATION_PARAM_DEFAULTS[param]
41
+ end
42
+
43
+ "#{AUTH_ENDPOINT}?#{URI.encode_www_form(query.compact)}"
37
44
  end
38
45
 
39
- def get_user_info(code: nil, id_token: nil, access_token: nil, redirect_uri: nil, **options)
46
+ def get_user_info(code: nil, id_token: nil, access_token: nil, redirect_uri: nil, nonce: nil, **options)
40
47
  client_id = options[:client_id] || StandardId.config.apple_client_id
41
48
 
42
49
  if id_token.present?
43
50
  build_response(
44
- verify_id_token(id_token: id_token, client_id: client_id),
51
+ verify_id_token(id_token: id_token, client_id: client_id, nonce: nonce),
45
52
  tokens: { id_token: id_token }
46
53
  )
47
54
  elsif code.present?
48
- exchange_code_for_user_info(code: code, redirect_uri: redirect_uri, client_id: client_id)
55
+ exchange_code_for_user_info(code: code, redirect_uri: redirect_uri, client_id: client_id, nonce: nonce)
49
56
  elsif access_token.present?
50
57
  raise StandardId::InvalidRequestError, "Access token login flow is not supported for Apple"
51
58
  else
@@ -82,7 +89,7 @@ module StandardId
82
89
  params.merge(client_id: client_id)
83
90
  end
84
91
 
85
- def exchange_code_for_user_info(code:, redirect_uri:, client_id: StandardId.config.apple_client_id)
92
+ def exchange_code_for_user_info(code:, redirect_uri:, client_id: StandardId.config.apple_client_id, nonce: nil)
86
93
  ensure_full_credentials!(client_id: client_id)
87
94
  raise StandardId::InvalidRequestError, "Missing authorization code" if code.blank?
88
95
 
@@ -108,7 +115,7 @@ module StandardId
108
115
  raise StandardId::InvalidRequestError, "Apple response missing id_token" if id_token.blank?
109
116
 
110
117
  tokens = extract_token_payload(parsed_token)
111
- user_info = verify_id_token(id_token: id_token, client_id: client_id)
118
+ user_info = verify_id_token(id_token: id_token, client_id: client_id, nonce: nonce)
112
119
 
113
120
  build_response(user_info, tokens: tokens)
114
121
  rescue StandardError => e
@@ -117,7 +124,7 @@ module StandardId
117
124
  raise StandardId::OAuthError, e.message, cause: e
118
125
  end
119
126
 
120
- def verify_id_token(id_token:, client_id: StandardId.config.apple_client_id)
127
+ def verify_id_token(id_token:, client_id: StandardId.config.apple_client_id, nonce: nil)
121
128
  raise StandardId::InvalidRequestError, "Missing id_token" if id_token.blank?
122
129
  raise StandardId::InvalidRequestError, "Apple client_id is not configured" if client_id.blank?
123
130
 
@@ -137,6 +144,15 @@ module StandardId
137
144
  verify_aud: true
138
145
  )
139
146
 
147
+ # Validate nonce if provided (web flow with server-generated nonce)
148
+ if nonce.present?
149
+ token_nonce = verified_payload["nonce"]
150
+ if token_nonce != nonce
151
+ raise StandardId::InvalidRequestError,
152
+ "ID token nonce mismatch. Expected: #{nonce}, got: #{token_nonce}"
153
+ end
154
+ end
155
+
140
156
  {
141
157
  "sub" => verified_payload["sub"],
142
158
  "email" => verified_payload["email"],
@@ -2,6 +2,6 @@
2
2
 
3
3
  module StandardId
4
4
  module Apple
5
- VERSION = "0.1.1"
5
+ VERSION = "0.1.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standard_id-apple
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jaryl Sim