apple_auth 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
- data/.github/pull_request_template.md +12 -0
- data/CONTRIBUTING.md +41 -0
- data/README.md +52 -7
- data/lib/apple_auth/base/version.rb +1 -1
- data/lib/apple_auth/helpers/jwt_decoder.rb +37 -0
- data/lib/apple_auth/helpers/jwt_server_conditions.rb +34 -0
- data/lib/apple_auth/server_identity.rb +19 -0
- data/lib/apple_auth/user_identity.rb +1 -23
- data/lib/apple_auth.rb +3 -0
- metadata +14 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81230a2cf77dadea66d5149d533545217931f531e6d1855d7f12d1dcf9dd8957
|
4
|
+
data.tar.gz: 8048602dd34d1b32b1ffb9cb6888b73e4ec912d94e41dc543137fc03a880c4af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd52622624be456077bc356bef447461d4d4638be8a38c29b0cc21f4a9c1e67cd494be8766aaf736918981067cd9c19eafcc5c248dea0d3b0b4e97b968c9ed0c
|
7
|
+
data.tar.gz: a82e5a11a1f4854b7a1e6000a7cfc15b3edc1c3f509f90a83b297388e571ad47acc6151817521fb8e3744c31b4feb4d07934c69bbae3cdb7755a14ecf92eb669
|
@@ -0,0 +1,20 @@
|
|
1
|
+
---
|
2
|
+
name: Bug report
|
3
|
+
about: Create a report to help us improve
|
4
|
+
title: "[Bug] "
|
5
|
+
labels: bug
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
## Bug report:
|
11
|
+
* **Expected Behavior**:
|
12
|
+
* **Actual Behavior**:
|
13
|
+
* **Steps to Reproduce**:
|
14
|
+
1.
|
15
|
+
2.
|
16
|
+
3.
|
17
|
+
|
18
|
+
* **Version of the repo**:
|
19
|
+
* **Ruby and Rails Version**:
|
20
|
+
* **Rails Stacktrace**: this can be found in the `log/development.log` or `log/test.log`, if this is applicable.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
name: Feature request
|
3
|
+
about: Suggest an idea for this project
|
4
|
+
title: '[FEATURE]'
|
5
|
+
labels: enhancement
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
<!-- Please note by far the quickest way to get a new feature is to file a Pull Request.
|
10
|
+
We will consider your request but it may be closed if it's something we're not actively planning to work on. -->
|
11
|
+
|
12
|
+
**Is your feature request related to a problem? Please describe.**
|
13
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
14
|
+
|
15
|
+
**Describe the solution you'd like**
|
16
|
+
A clear and concise description of what you want to happen.
|
17
|
+
|
18
|
+
**Describe alternatives you've considered**
|
19
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
20
|
+
|
21
|
+
**Additional context**
|
22
|
+
Add any other context or screenshots about the feature request here.
|
@@ -0,0 +1,12 @@
|
|
1
|
+
### Summary
|
2
|
+
|
3
|
+
<!-- Provide a general description of the code changes in your pull
|
4
|
+
request... were there any bugs you had fixed? If so, mention them. If
|
5
|
+
these bugs have open GitHub issues, be sure to tag them here as well,
|
6
|
+
to keep the conversation linked together. -->
|
7
|
+
|
8
|
+
### Other Information
|
9
|
+
|
10
|
+
<!-- If there's anything else that's important and relevant to your pull
|
11
|
+
request, mention that information here.
|
12
|
+
Thanks for contributing to this project! -->
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
## Contributing ##
|
2
|
+
|
3
|
+
You can contribute to this repo if you have an issue, found a bug or think there's some functionality required that would add value to the gem. To do so, please check if there's not already an [issue](https://github.com/rootstrap/apple_auth/issues) for that, if you find there's not, create a new one with as much detail as possible.
|
4
|
+
|
5
|
+
If you want to contribute with code as well, please follow the next steps:
|
6
|
+
|
7
|
+
1. Read, understand and agree to our [code of conduct](https://github.com/rootstrap/apple_auth/blob/master/CODE_OF_CONDUCT.md)
|
8
|
+
2. [Fork the repo](https://help.github.com/articles/about-forks/)
|
9
|
+
3. Clone the project into your machine:
|
10
|
+
`$ git clone git@github.com:rootstrap/apple_auth.git`
|
11
|
+
4. Access the repo:
|
12
|
+
`$ cd apple_auth`
|
13
|
+
5. Create your feature/bugfix branch:
|
14
|
+
`$ git checkout -b your_new_feature`
|
15
|
+
or
|
16
|
+
`$ git checkout -b fix/your_fix` in case of a bug fix
|
17
|
+
(if your PR is to address an existing issue, it would be good to name the branch after the issue, for example: if you are trying to solve issue 182, then a good idea for the branch name would be `182_your_new_feature`)
|
18
|
+
6. Write tests for your changes (feature/bug)
|
19
|
+
7. Code your (feature/bugfix)
|
20
|
+
8. Run the code analysis tool by doing:
|
21
|
+
`$ rake code_analysis`
|
22
|
+
9. Run the tests:
|
23
|
+
`$ bundle exec rspec`
|
24
|
+
All tests must pass. If all tests (both code analysis and rspec) do pass, then you are ready to go to the next step:
|
25
|
+
10. Commit your changes:
|
26
|
+
`$ git commit -m 'Your feature or bugfix title'`
|
27
|
+
11. Push to the branch `$ git push origin your_new_feature`
|
28
|
+
12. Create a new [pull request](https://help.github.com/articles/creating-a-pull-request/)
|
29
|
+
|
30
|
+
Some helpful guides that will help you know how we work:
|
31
|
+
1. [Code review](https://github.com/rootstrap/tech-guides/tree/master/code-review)
|
32
|
+
2. [GIT workflow](https://github.com/rootstrap/tech-guides/tree/master/git)
|
33
|
+
3. [Ruby style guide](https://github.com/rootstrap/tech-guides/tree/master/ruby)
|
34
|
+
4. [Rails style guide](https://github.com/rootstrap/tech-guides/blob/master/ruby/rails.md)
|
35
|
+
5. [RSpec style guide](https://github.com/rootstrap/tech-guides/blob/master/ruby/rspec/README.md)
|
36
|
+
|
37
|
+
For more information or guides like the ones mentioned above, please check our [tech guides](https://github.com/rootstrap/tech-guides). Keep in mind that the more you know about these guides, the easier it will be for your code to get approved and merged.
|
38
|
+
|
39
|
+
Note: You can push as many commits as you want when working on a pull request, we just ask that they are descriptive and tell a story. Try to open a pull request with just one commit but if you think you need to divide what you did into more commits to convey what you are trying to do go for it.
|
40
|
+
|
41
|
+
Thank you very much for your time and for considering helping in this project.
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# AppleAuth
|
2
2
|
|
3
|
-
[![CI](https://api.travis-ci.
|
3
|
+
[![CI](https://api.travis-ci.com/rootstrap/apple_auth.svg?branch=master)](https://travis-ci.com/github/rootstrap/apple_auth)
|
4
4
|
[![Maintainability](https://api.codeclimate.com/v1/badges/78453501221a76e3806e/maintainability)](https://codeclimate.com/github/rootstrap/apple_sign_in/maintainability)
|
5
5
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/78453501221a76e3806e/test_coverage)](https://codeclimate.com/github/rootstrap/apple_sign_in/test_coverage)
|
6
6
|
|
@@ -33,7 +33,7 @@ AppleAuth.configure do |config|
|
|
33
33
|
# config.apple_private_key = <Your private key provided by Apple>
|
34
34
|
# config.apple_key_id = <Your kid provided by Apple>
|
35
35
|
# config.apple_team_id = <Your team id provided by Apple>
|
36
|
-
# config.redirect_uri = <Your app
|
36
|
+
# config.redirect_uri = <Your app redirect url>
|
37
37
|
end
|
38
38
|
```
|
39
39
|
Set your different credentials in the file by uncommenting the lines and adding your keys.
|
@@ -56,13 +56,13 @@ end
|
|
56
56
|
|
57
57
|
We strongly recommend to use environment variables for these values.
|
58
58
|
|
59
|
-
Apple sign-in workflow:
|
59
|
+
### Apple sign-in workflow:
|
60
60
|
|
61
61
|
![alt text](https://docs-assets.developer.apple.com/published/360d59b776/rendered2x-1592224731.png)
|
62
62
|
|
63
63
|
For more information, check the [Apple oficial documentation](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api).
|
64
64
|
|
65
|
-
Validate JWT token and get user information:
|
65
|
+
### Validate JWT token and get user information:
|
66
66
|
|
67
67
|
```ruby
|
68
68
|
# with a valid JWT
|
@@ -79,7 +79,7 @@ AppleAuth::UserIdentity.new(user_id, invalid_jwt_token).validate!
|
|
79
79
|
>> AppleAuth::Conditions::JWTValidationError
|
80
80
|
```
|
81
81
|
|
82
|
-
Verify user identity and get access and refresh tokens:
|
82
|
+
### Verify user identity and get access and refresh tokens:
|
83
83
|
|
84
84
|
```ruby
|
85
85
|
code = 'cfb77c21ecd444390a2c214cd33decdfb.0.mr...'
|
@@ -87,14 +87,59 @@ AppleAuth::Token.new(code).authenticate!
|
|
87
87
|
>> { access_token: "a7058d...", expires_at: 1595894672, refresh_token: "r8f1ce..." }
|
88
88
|
```
|
89
89
|
|
90
|
+
### Handle server to server notifications
|
91
|
+
|
92
|
+
from the request parameter :payload
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
# with a valid JWT
|
96
|
+
params[:payload] = "eyJraWQiOiJZ......"
|
97
|
+
AppleAuth::ServerIdentity.new(params[:payload]).validate!
|
98
|
+
>> {iss: "https://appleid.apple.com", exp: 1632224024, iat: 1632137624, jti: "yctpp1ZHaGCzaNB9PWB4DA",...}
|
99
|
+
|
100
|
+
# with an invalid JWT
|
101
|
+
params[:payload] = "asdasdasdasd......"
|
102
|
+
AppleAuth::ServerIdentity.new(params[:payload]).validate!
|
103
|
+
>> JWT::VerificationError: Signature verification raised
|
104
|
+
```
|
105
|
+
|
106
|
+
Implementation in a controller would look like this:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
class Hooks::AuthController < ApplicationController
|
110
|
+
|
111
|
+
skip_before_action :verify_authenticity_token
|
112
|
+
|
113
|
+
# https://developer.apple.com/documentation/sign_in_with_apple/processing_changes_for_sign_in_with_apple_accounts
|
114
|
+
# NOTE: The Apple documentation states the events attribute as an array but is in fact a stringified json object
|
115
|
+
def apple
|
116
|
+
# will raise an error when the signature is invalid
|
117
|
+
payload = AppleAuth::ServerIdentity.new(params[:payload]).validate!
|
118
|
+
event = JSON.parse(payload[:events]).symbolize_keys
|
119
|
+
uid = event["sub"]
|
120
|
+
user = User.find_by!(provider: 'apple', uid: uid)
|
121
|
+
|
122
|
+
case event[:type]
|
123
|
+
when "email-enabled", "email-disabled"
|
124
|
+
# Here we should update the user with the relay state
|
125
|
+
when "consent-revoked", "account-delete"
|
126
|
+
user.destroy!
|
127
|
+
else
|
128
|
+
throw event
|
129
|
+
end
|
130
|
+
render plain: "200 OK", status: :ok
|
131
|
+
end
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
90
135
|
## Using with Devise
|
91
136
|
|
92
137
|
If you are using devise_token_auth gem, run this generator.
|
93
138
|
|
94
|
-
$ rails g apple_sign_in:
|
139
|
+
$ rails g apple_sign_in:apple_auth_controller [scope]
|
95
140
|
|
96
141
|
In the scope you need to write your path from controllers to your existent devise controllers.
|
97
|
-
An example `$ rails g apple_auth:
|
142
|
+
An example `$ rails g apple_auth:apple_auth_controller api/v1/`
|
98
143
|
This will generate a new controller: `controllers/api/v1/apple_auth_controller.rb`.
|
99
144
|
|
100
145
|
You should configure the route, you can wrap it in the devise_scope block like:
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module AppleAuth
|
4
|
+
class JWTDecoder
|
5
|
+
APPLE_KEY_URL = 'https://appleid.apple.com/auth/keys'.freeze
|
6
|
+
|
7
|
+
attr_reader :jwt
|
8
|
+
|
9
|
+
def initialize(jwt)
|
10
|
+
@jwt = jwt
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
decoded.first
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def decoded
|
20
|
+
key_hash = apple_key_hash(jwt)
|
21
|
+
apple_jwk = JWT::JWK.import(key_hash)
|
22
|
+
JWT.decode(jwt, apple_jwk.public_key, true, algorithm: key_hash['alg'])
|
23
|
+
end
|
24
|
+
|
25
|
+
def apple_key_hash(jwt)
|
26
|
+
response = Net::HTTP.get(URI.parse(APPLE_KEY_URL))
|
27
|
+
certificate = JSON.parse(response)
|
28
|
+
matching_key = certificate['keys'].select { |key| key['kid'] == jwt_kid(jwt) }
|
29
|
+
ActiveSupport::HashWithIndifferentAccess.new(matching_key.first)
|
30
|
+
end
|
31
|
+
|
32
|
+
def jwt_kid(jwt)
|
33
|
+
header = JSON.parse(Base64.decode64(jwt.split('.').first))
|
34
|
+
header['kid']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module AppleAuth
|
4
|
+
class JWTServerConditions
|
5
|
+
include Conditions
|
6
|
+
|
7
|
+
CONDITIONS = [
|
8
|
+
AudCondition,
|
9
|
+
IatCondition,
|
10
|
+
IssCondition
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
attr_reader :decoded_jwt
|
14
|
+
|
15
|
+
def initialize(decoded_jwt)
|
16
|
+
@decoded_jwt = decoded_jwt
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate!
|
20
|
+
JWT::ClaimsValidator.new(decoded_jwt).validate! && jwt_conditions_validate!
|
21
|
+
rescue JWT::InvalidPayload => e
|
22
|
+
raise JWTValidationError, e.message
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def jwt_conditions_validate!
|
28
|
+
conditions_results = CONDITIONS.map do |condition|
|
29
|
+
condition.new(decoded_jwt).validate!
|
30
|
+
end
|
31
|
+
conditions_results.all? { |value| value == true }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppleAuth
|
4
|
+
class ServerIdentity
|
5
|
+
attr_reader :jwt
|
6
|
+
|
7
|
+
def initialize(jwt)
|
8
|
+
@jwt = jwt
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate!
|
12
|
+
token_data = JWTDecoder.new(jwt).call
|
13
|
+
|
14
|
+
JWTServerConditions.new(token_data).validate!
|
15
|
+
|
16
|
+
token_data.symbolize_keys
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
module AppleAuth
|
4
4
|
class UserIdentity
|
5
|
-
APPLE_KEY_URL = 'https://appleid.apple.com/auth/keys'
|
6
|
-
|
7
5
|
attr_reader :user_identity, :jwt
|
8
6
|
|
9
7
|
def initialize(user_identity, jwt)
|
@@ -12,31 +10,11 @@ module AppleAuth
|
|
12
10
|
end
|
13
11
|
|
14
12
|
def validate!
|
15
|
-
token_data =
|
13
|
+
token_data = JWTDecoder.new(jwt).call
|
16
14
|
|
17
15
|
JWTConditions.new(user_identity, token_data).validate!
|
18
16
|
|
19
17
|
token_data.symbolize_keys
|
20
18
|
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def decoded_jwt
|
25
|
-
key_hash = apple_key_hash
|
26
|
-
apple_jwk = JWT::JWK.import(key_hash)
|
27
|
-
JWT.decode(jwt, apple_jwk.public_key, true, algorithm: key_hash['alg']).first
|
28
|
-
end
|
29
|
-
|
30
|
-
def apple_key_hash
|
31
|
-
response = Net::HTTP.get(URI.parse(APPLE_KEY_URL))
|
32
|
-
certificate = JSON.parse(response)
|
33
|
-
matching_key = certificate['keys'].select { |key| key['kid'] == jwt_kid }
|
34
|
-
ActiveSupport::HashWithIndifferentAccess.new(matching_key.first)
|
35
|
-
end
|
36
|
-
|
37
|
-
def jwt_kid
|
38
|
-
header = JSON.parse(Base64.decode64(jwt.split('.').first))
|
39
|
-
header['kid']
|
40
|
-
end
|
41
19
|
end
|
42
20
|
end
|
data/lib/apple_auth.rb
CHANGED
@@ -22,7 +22,10 @@ require 'apple_auth/helpers/conditions/exp_condition'
|
|
22
22
|
require 'apple_auth/helpers/conditions/iat_condition'
|
23
23
|
require 'apple_auth/helpers/conditions/iss_condition'
|
24
24
|
require 'apple_auth/helpers/jwt_conditions'
|
25
|
+
require 'apple_auth/helpers/jwt_decoder'
|
26
|
+
require 'apple_auth/helpers/jwt_server_conditions'
|
25
27
|
|
28
|
+
require 'apple_auth/server_identity'
|
26
29
|
require 'apple_auth/user_identity'
|
27
30
|
require 'apple_auth/token'
|
28
31
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apple_auth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Timothy Peraza, Antonieta Alvarez, Martín Morón
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
@@ -178,13 +178,16 @@ dependencies:
|
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '3.8'
|
181
|
-
description:
|
181
|
+
description:
|
182
182
|
email:
|
183
183
|
- timothy@rootstrap.com, antonieta.alvarez@rootstrap.com, martin.jaime@rootstrap.com
|
184
184
|
executables: []
|
185
185
|
extensions: []
|
186
186
|
extra_rdoc_files: []
|
187
187
|
files:
|
188
|
+
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
189
|
+
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
190
|
+
- ".github/pull_request_template.md"
|
188
191
|
- ".gitignore"
|
189
192
|
- ".reek.yml"
|
190
193
|
- ".rspec"
|
@@ -192,6 +195,7 @@ files:
|
|
192
195
|
- ".travis.yml"
|
193
196
|
- CODEOWNERS
|
194
197
|
- CODE_OF_CONDUCT.md
|
198
|
+
- CONTRIBUTING.md
|
195
199
|
- Gemfile
|
196
200
|
- LICENSE.txt
|
197
201
|
- README.md
|
@@ -209,6 +213,9 @@ files:
|
|
209
213
|
- lib/apple_auth/helpers/conditions/iss_condition.rb
|
210
214
|
- lib/apple_auth/helpers/conditions/jwt_validation_error.rb
|
211
215
|
- lib/apple_auth/helpers/jwt_conditions.rb
|
216
|
+
- lib/apple_auth/helpers/jwt_decoder.rb
|
217
|
+
- lib/apple_auth/helpers/jwt_server_conditions.rb
|
218
|
+
- lib/apple_auth/server_identity.rb
|
212
219
|
- lib/apple_auth/token.rb
|
213
220
|
- lib/apple_auth/user_identity.rb
|
214
221
|
- lib/generators/apple_auth/apple_auth_controller/apple_auth_controller_generator.rb
|
@@ -222,7 +229,7 @@ metadata:
|
|
222
229
|
homepage_uri: https://github.com/rootstrap/apple_auth
|
223
230
|
source_code_uri: https://github.com/rootstrap/apple_auth
|
224
231
|
changelog_uri: https://github.com/rootstrap/apple_auth
|
225
|
-
post_install_message:
|
232
|
+
post_install_message:
|
226
233
|
rdoc_options: []
|
227
234
|
require_paths:
|
228
235
|
- lib
|
@@ -237,8 +244,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
237
244
|
- !ruby/object:Gem::Version
|
238
245
|
version: '0'
|
239
246
|
requirements: []
|
240
|
-
rubygems_version: 3.
|
241
|
-
signing_key:
|
247
|
+
rubygems_version: 3.1.6
|
248
|
+
signing_key:
|
242
249
|
specification_version: 4
|
243
250
|
summary: Integration with Apple Sign In and Devise for backend. Validate and Verify
|
244
251
|
user token.
|