standard_id-apple 0.7.0 → 0.8.0
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5bb7427c0d928fdf21192e6a48c70431a51a2e3c05d3b96dd192eab760247f0
|
|
4
|
+
data.tar.gz: ef547da4fd6b44facb4cafca70b80a98b65cec3859a6715b1c72160a51769aa1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f49035c18090743a5513da37b8eb3e66eef03a430a050aa8643492e668f8cff2cb26d7b0247a21b0854ea086023115b0b62a2fc85f613bb965f8dacecf8335ea
|
|
7
|
+
data.tar.gz: 4e23288305fcf8b7dc59fd203ad62b40c3f85dd0983b931a0c948a4d1c75c3712905e46efa4f4c63ae2e37eec3bae7317e7ca09e92aa17564035c21dcb5a47d2
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.0] - 2026-10-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`apple_redirect_uri`** (optional, ENV fallback `APPLE_REDIRECT_URI`): the
|
|
15
|
+
return URL web-flow codes are authorized against when the caller exchanges
|
|
16
|
+
them without one — normally `https://<host>/auth/callback_mobile/apple`,
|
|
17
|
+
registered on the Services ID.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **`flow=web` code exchanges through the API callback** (e.g. Android signing
|
|
22
|
+
in through Apple's web flow) sent no `redirect_uri` to Apple's token
|
|
23
|
+
endpoint, so Apple rejected them with `invalid_grant`. `resolve_params` now
|
|
24
|
+
fills the configured `apple_redirect_uri` for the web flow when the caller
|
|
25
|
+
passed none. A caller's `redirect_uri` (the browser callback's) still wins,
|
|
26
|
+
and the native flow is unchanged. No-op until `apple_redirect_uri` is set.
|
|
27
|
+
|
|
10
28
|
## [0.7.0] - 2026-10-06
|
|
11
29
|
|
|
12
30
|
### Added
|
data/README.md
CHANGED
|
@@ -45,6 +45,7 @@ configuration block, in the `social` scope:
|
|
|
45
45
|
StandardId.configure do |config|
|
|
46
46
|
config.social.apple_client_id = ENV["APPLE_CLIENT_ID"]
|
|
47
47
|
config.social.apple_mobile_client_id = ENV["APPLE_MOBILE_CLIENT_ID"] # optional
|
|
48
|
+
config.social.apple_redirect_uri = ENV["APPLE_REDIRECT_URI"] # optional, see Flows
|
|
48
49
|
config.social.apple_team_id = ENV["APPLE_TEAM_ID"]
|
|
49
50
|
config.social.apple_key_id = ENV["APPLE_KEY_ID"]
|
|
50
51
|
config.social.apple_private_key = ENV["APPLE_PRIVATE_KEY"]
|
|
@@ -62,6 +63,7 @@ variable named after it, upper-cased:
|
|
|
62
63
|
|---|---|
|
|
63
64
|
| `apple_client_id` | `APPLE_CLIENT_ID` |
|
|
64
65
|
| `apple_mobile_client_id` | `APPLE_MOBILE_CLIENT_ID` |
|
|
66
|
+
| `apple_redirect_uri` | `APPLE_REDIRECT_URI` |
|
|
65
67
|
| `apple_team_id` | `APPLE_TEAM_ID` |
|
|
66
68
|
| `apple_key_id` | `APPLE_KEY_ID` |
|
|
67
69
|
| `apple_private_key` | `APPLE_PRIVATE_KEY` (deprecated fallback: `APPLE_PRIVATE_KEY_PEM`) |
|
|
@@ -103,6 +105,14 @@ UI while its native sign-in keeps working.
|
|
|
103
105
|
| Native `id_token` (`/api/oauth/callback/apple`) | `apple_mobile_client_id` | no |
|
|
104
106
|
| Web flow on Android etc. (`flow=web` on the API callback, redirects back into the app) | `apple_client_id` | yes |
|
|
105
107
|
|
|
108
|
+
Apple only exchanges a web-flow code when the token request carries the same
|
|
109
|
+
`redirect_uri` the authorization used. The browser callback passes its own
|
|
110
|
+
URL. The API callback passes none, so for `flow=web` there set
|
|
111
|
+
`apple_redirect_uri` to the return URL those codes are authorized against,
|
|
112
|
+
normally the mobile bounce `https://<host>/auth/callback_mobile/apple`
|
|
113
|
+
(registered on the Services ID). A `redirect_uri` the caller passes always
|
|
114
|
+
wins, and the native flow never sends one.
|
|
115
|
+
|
|
106
116
|
Apple's signing keys (JWKS) are fetched through `StandardId::HttpClient`
|
|
107
117
|
(timeouts, private-address guard) and cached in-process for an hour; a token
|
|
108
118
|
signed with an unknown key triggers one early refetch, at most once a minute.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Apple Sign In credentials for StandardId.
|
|
4
4
|
#
|
|
5
|
-
# These
|
|
5
|
+
# These six fields are declared by the standard_id-apple gem, NOT by
|
|
6
6
|
# standard_id — they do not exist unless this gem is in the Gemfile, on any
|
|
7
7
|
# standard_id version. Removing the gem and leaving this file behind raises
|
|
8
8
|
# `StandardId::ConfigurationError: Unknown field 'apple_client_id' for scope
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
#
|
|
16
16
|
# ENV: on standard_id >= 0.42 a field this file does not assign falls back to
|
|
17
17
|
# the ENV variable named after it, upper-cased — APPLE_CLIENT_ID,
|
|
18
|
-
# APPLE_MOBILE_CLIENT_ID, APPLE_TEAM_ID, APPLE_KEY_ID,
|
|
19
|
-
# are the names used below. The assignments are kept
|
|
20
|
-
# delete any you prefer to leave to the fallback, or
|
|
21
|
-
# source (e.g. Rails credentials).
|
|
18
|
+
# APPLE_MOBILE_CLIENT_ID, APPLE_REDIRECT_URI, APPLE_TEAM_ID, APPLE_KEY_ID,
|
|
19
|
+
# APPLE_PRIVATE_KEY — which are the names used below. The assignments are kept
|
|
20
|
+
# so the wiring is visible; delete any you prefer to leave to the fallback, or
|
|
21
|
+
# point one at another source (e.g. Rails credentials).
|
|
22
22
|
#
|
|
23
23
|
# apple_client_id switches the web flow on. While it is set, StandardId's boot
|
|
24
24
|
# check requires apple_team_id, apple_key_id and apple_private_key (warns by
|
|
@@ -32,6 +32,12 @@ StandardId.configure do |config|
|
|
|
32
32
|
# on a web-only app is correct — don't point it at APPLE_CLIENT_ID.
|
|
33
33
|
config.social.apple_mobile_client_id = ENV["APPLE_MOBILE_CLIENT_ID"]
|
|
34
34
|
|
|
35
|
+
# Optional: the return URL web-flow codes exchanged through the API callback
|
|
36
|
+
# (`flow=web`, e.g. Android) were authorized against — normally
|
|
37
|
+
# https://<host>/auth/callback_mobile/apple, registered on the Services ID.
|
|
38
|
+
# Apple rejects the exchange (invalid_grant) without it.
|
|
39
|
+
config.social.apple_redirect_uri = ENV["APPLE_REDIRECT_URI"]
|
|
40
|
+
|
|
35
41
|
config.social.apple_team_id = ENV["APPLE_TEAM_ID"]
|
|
36
42
|
config.social.apple_key_id = ENV["APPLE_KEY_ID"]
|
|
37
43
|
|
|
@@ -85,11 +85,16 @@ module StandardId
|
|
|
85
85
|
#
|
|
86
86
|
# ENV fallbacks (standard_id >= 0.42) use the upper-cased field names:
|
|
87
87
|
# APPLE_CLIENT_ID, APPLE_MOBILE_CLIENT_ID, APPLE_PRIVATE_KEY,
|
|
88
|
-
# APPLE_KEY_ID, APPLE_TEAM_ID.
|
|
88
|
+
# APPLE_KEY_ID, APPLE_TEAM_ID, APPLE_REDIRECT_URI.
|
|
89
|
+
#
|
|
90
|
+
# `apple_redirect_uri` is optional: the return URL a web-flow code
|
|
91
|
+
# was issued against, for callers that exchange the code without
|
|
92
|
+
# passing one (see #resolve_params).
|
|
89
93
|
def config_schema
|
|
90
94
|
{
|
|
91
95
|
apple_client_id: { type: :string, default: nil },
|
|
92
96
|
apple_mobile_client_id: { type: :string, default: nil },
|
|
97
|
+
apple_redirect_uri: { type: :string, default: nil },
|
|
93
98
|
apple_private_key: { type: :string, default: -> { legacy_private_key_from_env }, required: true },
|
|
94
99
|
apple_key_id: { type: :string, default: nil, required: true },
|
|
95
100
|
apple_team_id: { type: :string, default: nil, required: true }
|
|
@@ -113,11 +118,24 @@ module StandardId
|
|
|
113
118
|
|
|
114
119
|
# The web flow authenticates against the Services ID, the native flow
|
|
115
120
|
# against the app's bundle ID.
|
|
121
|
+
#
|
|
122
|
+
# A web-flow code must be exchanged with the redirect_uri it was
|
|
123
|
+
# authorized against, or Apple answers invalid_grant. The browser
|
|
124
|
+
# callback passes its own URL. The API callback (`flow=web`, e.g.
|
|
125
|
+
# Android signing in through Apple's web flow and bouncing back via
|
|
126
|
+
# `/auth/callback_mobile/apple`) passes none, so the configured
|
|
127
|
+
# `apple_redirect_uri` fills the gap. A redirect_uri the caller did
|
|
128
|
+
# pass always wins; the native flow never gets one.
|
|
116
129
|
def resolve_params(params, context: {})
|
|
117
130
|
flow = context[:flow] || :web
|
|
118
|
-
client_id
|
|
131
|
+
return params.merge(client_id: StandardId.config.apple_mobile_client_id) if flow == :mobile
|
|
119
132
|
|
|
120
|
-
params.merge(client_id:
|
|
133
|
+
resolved = params.merge(client_id: StandardId.config.apple_client_id)
|
|
134
|
+
configured_redirect_uri = StandardId.config.apple_redirect_uri
|
|
135
|
+
if resolved[:redirect_uri].blank? && configured_redirect_uri.present?
|
|
136
|
+
resolved[:redirect_uri] = configured_redirect_uri
|
|
137
|
+
end
|
|
138
|
+
resolved
|
|
121
139
|
end
|
|
122
140
|
|
|
123
141
|
def exchange_code_for_user_info(code:, redirect_uri:, client_id: StandardId.config.apple_client_id, nonce: nil)
|