standard_id-apple 0.4.0 → 0.5.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 +4 -4
- data/CHANGELOG.md +70 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +61 -7
- data/lib/generators/standard_id/apple/install/install_generator.rb +78 -0
- data/lib/generators/standard_id/apple/install/templates/initializer.rb.erb +42 -0
- data/lib/standard_id/apple/version.rb +1 -1
- metadata +13 -18
- data/.editorconfig +0 -15
- data/.rspec +0 -3
- data/.rubocop.yml +0 -8
- data/.ruby-version +0 -1
- data/AGENTS.md +0 -72
- data/CLAUDE.md +0 -32
- data/CODE_OF_CONDUCT.md +0 -132
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3886bf49958b7ea2704e0491b0e7225c7f82b0d80ca7ab0dcca192b69e7bc1fa
|
|
4
|
+
data.tar.gz: 804f776780574d8457d6037148148c823a1011ffe3e68a8b84362c821f61c623
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f124007a84b444fd05c29af8c990cb878a1f9c63a2947309ca719f7e8bda6c0cdbd78d5d793ad404257dca8115ee8b59e5fdc28e320825db75267266826ed261
|
|
7
|
+
data.tar.gz: f90487e071ce29728dab12aa31cee722828e54056b90541ca606ea3e54e4728cba7d7f8458fbfe966212b3b2ce1f99afdda2ead034d7c959c7c17ee71fd8ceae
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-07-31
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Install generator: `bin/rails g standard_id:apple:install`.** Writes
|
|
15
|
+
`config/initializers/standard_id_apple.rb` with all five `social.apple_*`
|
|
16
|
+
fields wired to ENV, then prints the environment variables the host has to
|
|
17
|
+
set (including the warning that `APPLE_PRIVATE_KEY_PEM` is multi-line and
|
|
18
|
+
that a secret store which flattens it to literal `\n` produces a key that
|
|
19
|
+
parses but fails to sign — surfacing as a JWT error at callback time, not at
|
|
20
|
+
boot). Idempotent: re-running skips an existing initializer; `--force`
|
|
21
|
+
overwrites, `--skip-initializer` writes nothing.
|
|
22
|
+
|
|
23
|
+
It writes a **separate** file rather than editing `standard_id.rb`, so the
|
|
24
|
+
provider can be removed by deleting one file and `standard_id`'s own install
|
|
25
|
+
generator stays free to overwrite its initializer without clobbering these
|
|
26
|
+
credentials. Initializers load alphabetically, so the base config is applied
|
|
27
|
+
first. The generated file uses the `config.social.` form throughout — a spec
|
|
28
|
+
pins that it never emits the unqualified `config.apple_*` form, which works
|
|
29
|
+
today only because the names happen to be unique across scopes.
|
|
30
|
+
|
|
31
|
+
Five of the nine `standard_*` gems shipped an install generator and this was
|
|
32
|
+
not one of them, which left both consumers assembling the block from the
|
|
33
|
+
README by hand.
|
|
34
|
+
|
|
35
|
+
### Documentation
|
|
36
|
+
|
|
37
|
+
- **Consumer list corrected in `CLAUDE.md`: this gem has two consumers, not
|
|
38
|
+
one.** It named `luminality-web` only; `sidekick-web` also consumes it. Both
|
|
39
|
+
live in sibling workspaces rather than beside this repo, which is how the
|
|
40
|
+
second one went unnoticed.
|
|
41
|
+
|
|
42
|
+
- **Corrected the Configuration section, which was wrong in two ways.**
|
|
43
|
+
|
|
44
|
+
It showed the *flat* form (`config.apple_client_id = ...`) rather than the
|
|
45
|
+
`social` scope the fields actually live in. The flat form works — StandardId
|
|
46
|
+
routes an unqualified name to the owning scope when it is unique across scopes
|
|
47
|
+
— but only once the field is declared, and it silently breaks the day another
|
|
48
|
+
scope declares a colliding name. Existing code using it is not broken.
|
|
49
|
+
|
|
50
|
+
It also documented a form that raised on `standard_id` <= 0.32.0. These fields
|
|
51
|
+
are declared by this gem, and until `standard_id` 0.33.0 they were declared
|
|
52
|
+
from this gem's Railtie `after_initialize` — after `config/initializers` — so
|
|
53
|
+
the plain initializer raised `StandardId::ConfigurationError: Unknown field
|
|
54
|
+
'apple_client_id' for scope 'social'`. The README now records the
|
|
55
|
+
`after_initialize` workaround for older `standard_id`, states that 0.33.0
|
|
56
|
+
declares provider fields before `:load_config_initializers` so the plain form
|
|
57
|
+
is correct there, and notes that the fields do not exist at all without this
|
|
58
|
+
gem in the Gemfile — on any `standard_id` version.
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- **`standard_id` dependency tightened from `~> 0.1, >= 0.1.7` to `~> 0.29.0`.**
|
|
63
|
+
The old constraint claimed compatibility with every `0.x` release while this
|
|
64
|
+
plugin reaches into `StandardId::ProviderRegistry` and
|
|
65
|
+
`StandardId::Providers::Apple`, and `standard_id` is pre-1.0 with breaking
|
|
66
|
+
minors. Bundler would happily resolve against an untested minor and fail at
|
|
67
|
+
runtime instead of at resolution. Both current consumers already pin
|
|
68
|
+
`standard_id "~> 0.29.0"`, so nothing existing is affected.
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- Gemspec now uses an allow-list (`Dir["lib/**/*", …]`) rather than a
|
|
73
|
+
`git ls-files` reject-list. A reject-list fails **open** — new files ship
|
|
74
|
+
unless someone remembers to exclude them, which is how `.claude/` reached
|
|
75
|
+
published `0.3.0` of `standard_id-google` (rarebit-one/standard_id-google#69).
|
|
76
|
+
Drops `.editorconfig`, `.pinact.yaml`, `.rspec`, `.rubocop.yml`,
|
|
77
|
+
`.ruby-version`, `AGENTS.md`, `CLAUDE.md`, and `CODE_OF_CONDUCT.md` from the
|
|
78
|
+
package; `lib/` is byte-identical.
|
|
79
|
+
|
|
10
80
|
## [0.4.0] - 2026-05-19
|
|
11
81
|
|
|
12
82
|
### Changed
|
data/{LICENSE.txt → LICENSE}
RENAMED
data/README.md
CHANGED
|
@@ -20,22 +20,76 @@ bundle install
|
|
|
20
20
|
|
|
21
21
|
The gem automatically registers itself with StandardId when it is required.
|
|
22
22
|
|
|
23
|
+
Then run the install generator to drop the credentials block in place:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bin/rails g standard_id:apple:install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This writes `config/initializers/standard_id_apple.rb` — deliberately a
|
|
30
|
+
separate file from `config/initializers/standard_id.rb`, so the provider can be
|
|
31
|
+
removed by deleting one file and `standard_id`'s own install generator stays
|
|
32
|
+
free to overwrite its initializer without clobbering these values. Initializers
|
|
33
|
+
load alphabetically, so the base config is applied first. The generator is
|
|
34
|
+
idempotent; re-running on an existing initializer skips with a clear message
|
|
35
|
+
(pass `--force` to overwrite).
|
|
36
|
+
|
|
23
37
|
## Configuration
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
The generator writes this for you; the block is documented here for hosts
|
|
40
|
+
configuring by hand. Configure Apple credentials via the StandardId
|
|
41
|
+
configuration block, in the `social` scope:
|
|
26
42
|
|
|
27
43
|
```ruby
|
|
44
|
+
# config/initializers/standard_id.rb
|
|
28
45
|
StandardId.configure do |config|
|
|
29
|
-
config.apple_client_id = ENV["APPLE_CLIENT_ID"]
|
|
30
|
-
config.apple_mobile_client_id = ENV["APPLE_MOBILE_CLIENT_ID"] # optional
|
|
31
|
-
config.apple_team_id = ENV["APPLE_TEAM_ID"]
|
|
32
|
-
config.apple_key_id = ENV["APPLE_KEY_ID"]
|
|
33
|
-
config.apple_private_key = ENV["APPLE_PRIVATE_KEY_PEM"]
|
|
46
|
+
config.social.apple_client_id = ENV["APPLE_CLIENT_ID"]
|
|
47
|
+
config.social.apple_mobile_client_id = ENV["APPLE_MOBILE_CLIENT_ID"] # optional
|
|
48
|
+
config.social.apple_team_id = ENV["APPLE_TEAM_ID"]
|
|
49
|
+
config.social.apple_key_id = ENV["APPLE_KEY_ID"]
|
|
50
|
+
config.social.apple_private_key = ENV["APPLE_PRIVATE_KEY_PEM"]
|
|
34
51
|
end
|
|
35
52
|
```
|
|
36
53
|
|
|
37
54
|
With those values in place, StandardId routes such as `/auth/callback/apple` continue to function using this provider gem.
|
|
38
55
|
|
|
56
|
+
### Two corrections to earlier versions of this section
|
|
57
|
+
|
|
58
|
+
**The `social.` prefix.** This section previously showed the flat form
|
|
59
|
+
(`config.apple_client_id = ...`). That happens to work — `StandardId`'s top-level
|
|
60
|
+
config routes an unqualified name to the owning scope when it is unique across
|
|
61
|
+
scopes, and these five are — but only *after* the field has been declared, and it
|
|
62
|
+
is the wrong thing to document: the fields live in the `social` scope, the
|
|
63
|
+
install template writes them there, and the flat form silently stops working the
|
|
64
|
+
day another scope declares a colliding name. Existing code using the flat form is
|
|
65
|
+
not broken and needs no change.
|
|
66
|
+
|
|
67
|
+
**Boot ordering.** These fields are declared by *this gem*, not by
|
|
68
|
+
`standard_id`, and until `standard_id` 0.33.0 they were declared from this gem's
|
|
69
|
+
Railtie `after_initialize` — which runs *after* `config/initializers`. On
|
|
70
|
+
`standard_id` **0.32.0 and earlier**, the block above therefore raised:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
StandardId::ConfigurationError: Unknown field 'apple_client_id' for scope 'social'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The workaround was to wrap the writes:
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
# Only needed on standard_id <= 0.32.0
|
|
80
|
+
Rails.application.config.after_initialize do
|
|
81
|
+
StandardId.configure { |config| config.social.apple_client_id = ENV["APPLE_CLIENT_ID"] }
|
|
82
|
+
end
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`standard_id` **>= 0.33.0** declares every loaded provider's fields before
|
|
86
|
+
`:load_config_initializers`, so a plain initializer is correct. The wrapper is no
|
|
87
|
+
longer needed and existing ones keep working unchanged.
|
|
88
|
+
|
|
89
|
+
Note this is about *ordering*, not just versions: **the fields do not exist
|
|
90
|
+
without this gem in your Gemfile**, on any `standard_id` version. Configuring
|
|
91
|
+
`social.apple_*` with the plugin absent raises the same error, correctly.
|
|
92
|
+
|
|
39
93
|
## Testing
|
|
40
94
|
|
|
41
95
|
Run the spec suite:
|
|
@@ -56,4 +110,4 @@ To release a new version:
|
|
|
56
110
|
|
|
57
111
|
## License
|
|
58
112
|
|
|
59
|
-
MIT — see [LICENSE
|
|
113
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module StandardId
|
|
6
|
+
module Apple
|
|
7
|
+
module Generators
|
|
8
|
+
# Installs the Apple provider's credentials block in a host Rails app.
|
|
9
|
+
#
|
|
10
|
+
# Writes `config/initializers/standard_id_apple.rb` — a separate file from
|
|
11
|
+
# `config/initializers/standard_id.rb` on purpose. The provider is opt-in
|
|
12
|
+
# per app, so its credentials should be removable by deleting one file,
|
|
13
|
+
# and `standard_id`'s own install generator must stay free to overwrite
|
|
14
|
+
# its initializer without clobbering these five values.
|
|
15
|
+
#
|
|
16
|
+
# Initializers load alphabetically, so `standard_id.rb` runs before
|
|
17
|
+
# `standard_id_apple.rb` — the base configuration is already applied by
|
|
18
|
+
# the time this file sets the `social.apple_*` fields.
|
|
19
|
+
#
|
|
20
|
+
# Idempotent: re-running skips an initializer that is already there.
|
|
21
|
+
# `--skip-initializer` opts out; `--force` overwrites.
|
|
22
|
+
class InstallGenerator < Rails::Generators::Base
|
|
23
|
+
source_root File.expand_path("templates", __dir__)
|
|
24
|
+
|
|
25
|
+
INITIALIZER_PATH = "config/initializers/standard_id_apple.rb"
|
|
26
|
+
|
|
27
|
+
desc <<~DESC
|
|
28
|
+
Installs StandardId Apple. This writes #{INITIALIZER_PATH} with the
|
|
29
|
+
five social.apple_* fields wired to ENV, and prints the env vars the
|
|
30
|
+
host needs to set.
|
|
31
|
+
|
|
32
|
+
The generator is idempotent — an existing initializer is skipped with
|
|
33
|
+
a clear message. Pass --force to overwrite.
|
|
34
|
+
DESC
|
|
35
|
+
|
|
36
|
+
class_option :skip_initializer, type: :boolean, default: false,
|
|
37
|
+
desc: "Do not write #{INITIALIZER_PATH}"
|
|
38
|
+
class_option :force, type: :boolean, default: false,
|
|
39
|
+
desc: "Overwrite #{INITIALIZER_PATH} if it already exists"
|
|
40
|
+
|
|
41
|
+
def copy_initializer
|
|
42
|
+
if options[:skip_initializer]
|
|
43
|
+
say_status("skip", "#{INITIALIZER_PATH} (--skip-initializer)", :yellow)
|
|
44
|
+
return
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if File.exist?(File.join(destination_root, INITIALIZER_PATH)) && !options[:force]
|
|
48
|
+
say_status("identical", "#{INITIALIZER_PATH} (already exists; pass --force to overwrite)", :blue)
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
template "initializer.rb.erb", INITIALIZER_PATH, force: options[:force]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def print_env_hint
|
|
56
|
+
return if options[:skip_initializer]
|
|
57
|
+
|
|
58
|
+
say ""
|
|
59
|
+
say "=" * 79
|
|
60
|
+
say "StandardId Apple installed."
|
|
61
|
+
say ""
|
|
62
|
+
say "Set these in the host's environment (1Password / DO app spec / .env):"
|
|
63
|
+
say ""
|
|
64
|
+
say " APPLE_CLIENT_ID the Services ID (web sign-in)"
|
|
65
|
+
say " APPLE_MOBILE_CLIENT_ID the iOS bundle ID — optional, web-only apps skip it"
|
|
66
|
+
say " APPLE_TEAM_ID the 10-character Apple Developer team ID"
|
|
67
|
+
say " APPLE_KEY_ID the key ID of the Sign In with Apple private key"
|
|
68
|
+
say " APPLE_PRIVATE_KEY_PEM the .p8 private key contents, PEM, newlines intact"
|
|
69
|
+
say ""
|
|
70
|
+
say "APPLE_PRIVATE_KEY_PEM is multi-line. Most secret stores flatten it to"
|
|
71
|
+
say "literal \\n — if sign-in fails on a JWT signing error, that is why."
|
|
72
|
+
say "=" * 79
|
|
73
|
+
say ""
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Apple Sign In credentials for StandardId.
|
|
4
|
+
#
|
|
5
|
+
# These five fields are declared by the standard_id-apple gem, NOT by
|
|
6
|
+
# standard_id — they do not exist unless this gem is in the Gemfile, on any
|
|
7
|
+
# standard_id version. Removing the gem and leaving this file behind raises
|
|
8
|
+
# `StandardId::ConfigurationError: Unknown field 'apple_client_id' for scope
|
|
9
|
+
# 'social'`, which is correct behaviour, not a bug.
|
|
10
|
+
#
|
|
11
|
+
# Set them in the `social` scope, as below. The unqualified form
|
|
12
|
+
# (`config.apple_client_id = ...`) happens to work today because StandardId
|
|
13
|
+
# routes a unique unqualified name to its owning scope, but it stops working
|
|
14
|
+
# the day another scope declares a colliding name. Use `config.social.`.
|
|
15
|
+
#
|
|
16
|
+
# On standard_id <= 0.32.0 these fields were declared from this gem's Railtie
|
|
17
|
+
# `after_initialize`, which runs AFTER config/initializers — so the plain block
|
|
18
|
+
# below raised at boot and had to be wrapped:
|
|
19
|
+
#
|
|
20
|
+
# Rails.application.config.after_initialize do
|
|
21
|
+
# StandardId.configure { |config| config.social.apple_client_id = ... }
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# standard_id >= 0.33.0 declares every loaded provider's fields before
|
|
25
|
+
# `:load_config_initializers`, so the plain form is correct. Existing wrappers
|
|
26
|
+
# keep working and need no change.
|
|
27
|
+
StandardId.configure do |config|
|
|
28
|
+
config.social.apple_client_id = ENV["APPLE_CLIENT_ID"]
|
|
29
|
+
|
|
30
|
+
# Optional: only set when a native iOS client signs in against the same
|
|
31
|
+
# backend. Its audience differs from the web Services ID, so leaving it nil
|
|
32
|
+
# on a web-only app is correct — don't point it at APPLE_CLIENT_ID.
|
|
33
|
+
config.social.apple_mobile_client_id = ENV["APPLE_MOBILE_CLIENT_ID"]
|
|
34
|
+
|
|
35
|
+
config.social.apple_team_id = ENV["APPLE_TEAM_ID"]
|
|
36
|
+
config.social.apple_key_id = ENV["APPLE_KEY_ID"]
|
|
37
|
+
|
|
38
|
+
# The .p8 contents in PEM form, newlines intact. Secret stores that flatten
|
|
39
|
+
# multi-line values to literal \n produce a key that parses but fails to
|
|
40
|
+
# sign — surfacing as a JWT error at callback time, not at boot.
|
|
41
|
+
config.social.apple_private_key = ENV["APPLE_PRIVATE_KEY_PEM"]
|
|
42
|
+
end
|
metadata
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: standard_id-apple
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jaryl Sim
|
|
8
|
-
bindir:
|
|
8
|
+
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
@@ -47,22 +47,22 @@ dependencies:
|
|
|
47
47
|
name: standard_id
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
|
49
49
|
requirements:
|
|
50
|
-
- - "~>"
|
|
51
|
-
- !ruby/object:Gem::Version
|
|
52
|
-
version: '0.1'
|
|
53
50
|
- - ">="
|
|
54
51
|
- !ruby/object:Gem::Version
|
|
55
|
-
version: 0.
|
|
52
|
+
version: '0.29'
|
|
53
|
+
- - "<"
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '1.0'
|
|
56
56
|
type: :runtime
|
|
57
57
|
prerelease: false
|
|
58
58
|
version_requirements: !ruby/object:Gem::Requirement
|
|
59
59
|
requirements:
|
|
60
|
-
- - "~>"
|
|
61
|
-
- !ruby/object:Gem::Version
|
|
62
|
-
version: '0.1'
|
|
63
60
|
- - ">="
|
|
64
61
|
- !ruby/object:Gem::Version
|
|
65
|
-
version: 0.
|
|
62
|
+
version: '0.29'
|
|
63
|
+
- - "<"
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '1.0'
|
|
66
66
|
description: Extracted StandardId::Providers::Apple implementation packaged as a standalone
|
|
67
67
|
gem so StandardId installations can opt into Sign in with Apple independently.
|
|
68
68
|
email:
|
|
@@ -71,17 +71,12 @@ executables: []
|
|
|
71
71
|
extensions: []
|
|
72
72
|
extra_rdoc_files: []
|
|
73
73
|
files:
|
|
74
|
-
- ".editorconfig"
|
|
75
|
-
- ".rspec"
|
|
76
|
-
- ".rubocop.yml"
|
|
77
|
-
- ".ruby-version"
|
|
78
|
-
- AGENTS.md
|
|
79
74
|
- CHANGELOG.md
|
|
80
|
-
-
|
|
81
|
-
- CODE_OF_CONDUCT.md
|
|
82
|
-
- LICENSE.txt
|
|
75
|
+
- LICENSE
|
|
83
76
|
- README.md
|
|
84
77
|
- Rakefile
|
|
78
|
+
- lib/generators/standard_id/apple/install/install_generator.rb
|
|
79
|
+
- lib/generators/standard_id/apple/install/templates/initializer.rb.erb
|
|
85
80
|
- lib/standard_id/apple.rb
|
|
86
81
|
- lib/standard_id/apple/providers/apple.rb
|
|
87
82
|
- lib/standard_id/apple/railtie.rb
|
data/.editorconfig
DELETED
data/.rspec
DELETED
data/.rubocop.yml
DELETED
data/.ruby-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
4.0.1
|
data/AGENTS.md
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# AGENTS.md - AI Agent Guide for standard_id-apple
|
|
2
|
-
|
|
3
|
-
`standard_id-apple` is a provider plugin for the [StandardId](https://github.com/rarebit-one/standard_id) authentication engine. It packages a `StandardId::Providers::Apple` implementation for Sign in with Apple, and auto-registers itself with the host StandardId installation via a `Rails::Railtie` so apps that bundle the gem don't need an explicit initializer.
|
|
4
|
-
|
|
5
|
-
## Quick Reference
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Run tests
|
|
9
|
-
bundle exec rspec
|
|
10
|
-
|
|
11
|
-
# Run a single spec file
|
|
12
|
-
bundle exec rspec spec/standard_id/apple/providers/apple_spec.rb
|
|
13
|
-
|
|
14
|
-
# Run linting (note: --config flag is required on Ruby 4.0)
|
|
15
|
-
bundle exec rubocop --config .rubocop.yml
|
|
16
|
-
|
|
17
|
-
# Auto-fix lint issues
|
|
18
|
-
bundle exec rubocop --config .rubocop.yml -A
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Project Structure
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
standard_id-apple/
|
|
25
|
-
├── lib/standard_id/
|
|
26
|
-
│ ├── apple.rb # Top-level require entrypoint
|
|
27
|
-
│ └── apple/
|
|
28
|
-
│ ├── version.rb # Gem version constant
|
|
29
|
-
│ ├── railtie.rb # Auto-registers provider on after_initialize
|
|
30
|
-
│ └── providers/apple.rb # StandardId::Providers::Apple implementation
|
|
31
|
-
└── spec/
|
|
32
|
-
├── spec_helper.rb # Boots a minimal Rails app so the Railtie fires
|
|
33
|
-
└── standard_id/ # Provider specs
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Key Patterns
|
|
37
|
-
|
|
38
|
-
### Provider class
|
|
39
|
-
|
|
40
|
-
`StandardId::Providers::Apple` inherits from `StandardId::Providers::Base` (defined in the parent `standard_id` gem) and implements the provider contract: `provider_name`, `authorization_url`, `get_user_info`, `config_schema`, plus Apple-specific helpers (`verify_id_token`, `generate_client_secret`, JWKS fetching).
|
|
41
|
-
|
|
42
|
-
### Railtie auto-registration
|
|
43
|
-
|
|
44
|
-
`StandardId::Apple::Railtie` runs on `config.after_initialize` and calls `StandardId::ProviderRegistry.register(:apple, StandardId::Providers::Apple)`. Host apps just need the gem in their Gemfile — no initializer required.
|
|
45
|
-
|
|
46
|
-
### Spec bootstrapping
|
|
47
|
-
|
|
48
|
-
`spec/spec_helper.rb` defines a tiny `Rails::Application` and calls `Rails.application.initialize!` so the Railtie's `after_initialize` hook fires during the spec run; without this the provider would not appear in the registry.
|
|
49
|
-
|
|
50
|
-
## Key Files
|
|
51
|
-
|
|
52
|
-
| File | Purpose |
|
|
53
|
-
|------|---------|
|
|
54
|
-
| `lib/standard_id/apple.rb` | Top-level require entrypoint |
|
|
55
|
-
| `lib/standard_id/apple/railtie.rb` | Provider registration on Rails boot |
|
|
56
|
-
| `lib/standard_id/apple/providers/apple.rb` | Apple provider implementation |
|
|
57
|
-
| `lib/standard_id/apple/version.rb` | Gem version constant |
|
|
58
|
-
| `standard_id-apple.gemspec` | Gem metadata + runtime deps |
|
|
59
|
-
|
|
60
|
-
## Dependencies
|
|
61
|
-
|
|
62
|
-
- **standard_id** `~> 0.1`, `>= 0.1.7` (parent engine — provides `Providers::Base`, `ProviderRegistry`, errors)
|
|
63
|
-
- **activesupport** `>= 8.0` (`Time.current`, `present?`/`blank?`, indifferent access)
|
|
64
|
-
- **jwt** `~> 2.7` (id_token decoding, client_secret signing)
|
|
65
|
-
|
|
66
|
-
Dev: rspec, rubocop, webmock, lefthook.
|
|
67
|
-
|
|
68
|
-
## Testing
|
|
69
|
-
|
|
70
|
-
- WebMock stubs Apple's JWKS and token endpoints — never make real network calls in specs.
|
|
71
|
-
- The dummy Rails app in `spec_helper.rb` is intentionally minimal; add config via `StandardId.config.apple_*` setters in individual specs rather than expanding the dummy app.
|
|
72
|
-
- CI runs the full Ruby 4.0.x patch matrix via the shared `rarebit-one/.github` reusable workflow.
|
data/CLAUDE.md
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
## Worktree-Only Workflow (Enforced)
|
|
4
|
-
|
|
5
|
-
**All file modifications are blocked in the main checkout.** A PreToolUse hook (`enforce-worktree.sh`) rejects Edit, Write, and NotebookEdit operations targeting files outside a worktree. There are no opt-outs. Do not use Bash to write files in the main checkout either (e.g., `echo >`, `sed -i`, `tee`, `cp`) — the hook cannot intercept shell commands, so this rule is instruction-enforced.
|
|
6
|
-
|
|
7
|
-
Before writing any code, create a worktree:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@refs/remotes/origin/@@')
|
|
11
|
-
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main}
|
|
12
|
-
git fetch origin "$DEFAULT_BRANCH"
|
|
13
|
-
git worktree add .worktrees/<name> -b <branch-name> "origin/$DEFAULT_BRANCH"
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Then work inside `.worktrees/<name>/` for the rest of the session.
|
|
17
|
-
|
|
18
|
-
**Naming:** Use the Linear issue identifier if available (e.g., `.worktrees/<identifier>`), a task slug (e.g., `.worktrees/fix-auth-timeout`), or today's date (e.g., `.worktrees/2026-04-01`) as fallback.
|
|
19
|
-
|
|
20
|
-
**The hook allows modifications only when:**
|
|
21
|
-
|
|
22
|
-
1. The file is inside a git worktree (detected via `git rev-parse --git-dir` returning a path under `.git/worktrees/`)
|
|
23
|
-
2. Running in a CI/automated context where the checkout is already isolated
|
|
24
|
-
**Why this matters:** Working directly on the main checkout causes cross-contamination between sessions — uncommitted changes, wrong branches, and dirty state leak into unrelated work. Worktrees eliminate this entirely.
|
|
25
|
-
|
|
26
|
-
See the `/worktree` and `/start` skills for full conventions and flags.
|
|
27
|
-
|
|
28
|
-
## Consumers
|
|
29
|
-
|
|
30
|
-
`standard_id-apple` is currently consumed by `luminality-web` only in the rarebit-one workspace. Apple Sign In is a Luminality-only feature; `fundbright-web` and `nutripod-web` do not consume this gem.
|
|
31
|
-
|
|
32
|
-
After publishing a new version via `/publish-gem`, roll it out with the workspace-level `/rollout-gem standard_id-apple [<version>]` skill (defined at the rarebit-one workspace root, one directory above this repo). The canonical consumer matrix — including version constraints — lives in that skill's `SKILL.md`.
|
data/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
|
2
|
-
|
|
3
|
-
## Our Pledge
|
|
4
|
-
|
|
5
|
-
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
-
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
-
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
-
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
-
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
-
identity and orientation.
|
|
11
|
-
|
|
12
|
-
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
-
diverse, inclusive, and healthy community.
|
|
14
|
-
|
|
15
|
-
## Our Standards
|
|
16
|
-
|
|
17
|
-
Examples of behavior that contributes to a positive environment for our
|
|
18
|
-
community include:
|
|
19
|
-
|
|
20
|
-
* Demonstrating empathy and kindness toward other people
|
|
21
|
-
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
-
* Giving and gracefully accepting constructive feedback
|
|
23
|
-
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
-
and learning from the experience
|
|
25
|
-
* Focusing on what is best not just for us as individuals, but for the overall
|
|
26
|
-
community
|
|
27
|
-
|
|
28
|
-
Examples of unacceptable behavior include:
|
|
29
|
-
|
|
30
|
-
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
31
|
-
any kind
|
|
32
|
-
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
-
* Public or private harassment
|
|
34
|
-
* Publishing others' private information, such as a physical or email address,
|
|
35
|
-
without their explicit permission
|
|
36
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
-
professional setting
|
|
38
|
-
|
|
39
|
-
## Enforcement Responsibilities
|
|
40
|
-
|
|
41
|
-
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
-
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
-
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
-
or harmful.
|
|
45
|
-
|
|
46
|
-
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
-
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
-
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
-
decisions when appropriate.
|
|
50
|
-
|
|
51
|
-
## Scope
|
|
52
|
-
|
|
53
|
-
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
-
an individual is officially representing the community in public spaces.
|
|
55
|
-
Examples of representing our community include using an official email address,
|
|
56
|
-
posting via an official social media account, or acting as an appointed
|
|
57
|
-
representative at an online or offline event.
|
|
58
|
-
|
|
59
|
-
## Enforcement
|
|
60
|
-
|
|
61
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
62
|
-
reported to the community leaders responsible for enforcement at
|
|
63
|
-
[INSERT CONTACT METHOD].
|
|
64
|
-
All complaints will be reviewed and investigated promptly and fairly.
|
|
65
|
-
|
|
66
|
-
All community leaders are obligated to respect the privacy and security of the
|
|
67
|
-
reporter of any incident.
|
|
68
|
-
|
|
69
|
-
## Enforcement Guidelines
|
|
70
|
-
|
|
71
|
-
Community leaders will follow these Community Impact Guidelines in determining
|
|
72
|
-
the consequences for any action they deem in violation of this Code of Conduct:
|
|
73
|
-
|
|
74
|
-
### 1. Correction
|
|
75
|
-
|
|
76
|
-
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
77
|
-
unprofessional or unwelcome in the community.
|
|
78
|
-
|
|
79
|
-
**Consequence**: A private, written warning from community leaders, providing
|
|
80
|
-
clarity around the nature of the violation and an explanation of why the
|
|
81
|
-
behavior was inappropriate. A public apology may be requested.
|
|
82
|
-
|
|
83
|
-
### 2. Warning
|
|
84
|
-
|
|
85
|
-
**Community Impact**: A violation through a single incident or series of
|
|
86
|
-
actions.
|
|
87
|
-
|
|
88
|
-
**Consequence**: A warning with consequences for continued behavior. No
|
|
89
|
-
interaction with the people involved, including unsolicited interaction with
|
|
90
|
-
those enforcing the Code of Conduct, for a specified period of time. This
|
|
91
|
-
includes avoiding interactions in community spaces as well as external channels
|
|
92
|
-
like social media. Violating these terms may lead to a temporary or permanent
|
|
93
|
-
ban.
|
|
94
|
-
|
|
95
|
-
### 3. Temporary Ban
|
|
96
|
-
|
|
97
|
-
**Community Impact**: A serious violation of community standards, including
|
|
98
|
-
sustained inappropriate behavior.
|
|
99
|
-
|
|
100
|
-
**Consequence**: A temporary ban from any sort of interaction or public
|
|
101
|
-
communication with the community for a specified period of time. No public or
|
|
102
|
-
private interaction with the people involved, including unsolicited interaction
|
|
103
|
-
with those enforcing the Code of Conduct, is allowed during this period.
|
|
104
|
-
Violating these terms may lead to a permanent ban.
|
|
105
|
-
|
|
106
|
-
### 4. Permanent Ban
|
|
107
|
-
|
|
108
|
-
**Community Impact**: Demonstrating a pattern of violation of community
|
|
109
|
-
standards, including sustained inappropriate behavior, harassment of an
|
|
110
|
-
individual, or aggression toward or disparagement of classes of individuals.
|
|
111
|
-
|
|
112
|
-
**Consequence**: A permanent ban from any sort of public interaction within the
|
|
113
|
-
community.
|
|
114
|
-
|
|
115
|
-
## Attribution
|
|
116
|
-
|
|
117
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
118
|
-
version 2.1, available at
|
|
119
|
-
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
120
|
-
|
|
121
|
-
Community Impact Guidelines were inspired by
|
|
122
|
-
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
123
|
-
|
|
124
|
-
For answers to common questions about this code of conduct, see the FAQ at
|
|
125
|
-
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
|
126
|
-
[https://www.contributor-covenant.org/translations][translations].
|
|
127
|
-
|
|
128
|
-
[homepage]: https://www.contributor-covenant.org
|
|
129
|
-
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
130
|
-
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
131
|
-
[FAQ]: https://www.contributor-covenant.org/faq
|
|
132
|
-
[translations]: https://www.contributor-covenant.org/translations
|