apple_auth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6c6e2838dce13def54be641c346b9618bb3e3bdc10288b98a2accb639e3ea107
4
+ data.tar.gz: c6a16b50b7d59bdf788385d4718d6664e94d6206fbcb2febbe73e92ec4e25440
5
+ SHA512:
6
+ metadata.gz: 18707eddc8b74cc651477b0464dd890c83b4d8c5acb3080d0943c5a6eb6fa5a2c8f808c4e2c8ca9ab9c757f89d1c2494a04f4ff044e5f5ae8484893a5745d6fb
7
+ data.tar.gz: 8abc50e0cef074c7228ab3e7e2fe64822161f9b1c724e2b62877ecd8b23caf8862ff305d1b8025bbf0e8892a38559aef90c736aead513aacd4f801e9685cec2d
@@ -0,0 +1,21 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .byebug_history
13
+ .DS_Store
14
+
15
+ Gemfile.lock
16
+
17
+ Dockerfile
18
+ docker-compose.yml
19
+
20
+ # File created by the generators tests
21
+ spec/generators/tmp/
@@ -0,0 +1,128 @@
1
+ detectors:
2
+ Attribute:
3
+ enabled: false
4
+ exclude: []
5
+ BooleanParameter:
6
+ enabled: true
7
+ exclude: []
8
+ ClassVariable:
9
+ enabled: false
10
+ exclude: []
11
+ ControlParameter:
12
+ enabled: true
13
+ exclude: []
14
+ DataClump:
15
+ enabled: true
16
+ exclude: []
17
+ max_copies: 2
18
+ min_clump_size: 2
19
+ DuplicateMethodCall:
20
+ enabled: true
21
+ exclude: []
22
+ max_calls: 1
23
+ allow_calls: []
24
+ FeatureEnvy:
25
+ enabled: true
26
+ exclude: []
27
+ InstanceVariableAssumption:
28
+ enabled: false
29
+ IrresponsibleModule:
30
+ enabled: false
31
+ exclude: []
32
+ LongParameterList:
33
+ enabled: true
34
+ exclude: []
35
+ max_params: 4
36
+ overrides:
37
+ initialize:
38
+ max_params: 5
39
+ LongYieldList:
40
+ enabled: true
41
+ exclude: []
42
+ max_params: 3
43
+ ManualDispatch:
44
+ enabled: true
45
+ exclude: []
46
+ MissingSafeMethod:
47
+ enabled: false
48
+ exclude: []
49
+ ModuleInitialize:
50
+ enabled: true
51
+ exclude: []
52
+ NestedIterators:
53
+ enabled: true
54
+ exclude: []
55
+ max_allowed_nesting: 2
56
+ ignore_iterators: []
57
+ NilCheck:
58
+ enabled: false
59
+ exclude: []
60
+ RepeatedConditional:
61
+ enabled: true
62
+ exclude: []
63
+ max_ifs: 3
64
+ SubclassedFromCoreClass:
65
+ enabled: true
66
+ exclude: []
67
+ TooManyConstants:
68
+ enabled: true
69
+ exclude: []
70
+ max_constants: 5
71
+ TooManyInstanceVariables:
72
+ enabled: true
73
+ exclude: []
74
+ max_instance_variables: 9
75
+ TooManyMethods:
76
+ enabled: true
77
+ exclude: []
78
+ max_methods: 25
79
+ TooManyStatements:
80
+ enabled: true
81
+ exclude:
82
+ - initialize
83
+ max_statements: 12
84
+ UncommunicativeMethodName:
85
+ enabled: true
86
+ exclude: []
87
+ reject:
88
+ - "/^[a-z]$/"
89
+ - "/[0-9]$/"
90
+ - "/[A-Z]/"
91
+ accept: []
92
+ UncommunicativeModuleName:
93
+ enabled: true
94
+ exclude: []
95
+ reject:
96
+ - "/^.$/"
97
+ - "/[0-9]$/"
98
+ accept:
99
+ - Inline::C
100
+ - "/V[0-9]/"
101
+ UncommunicativeParameterName:
102
+ enabled: true
103
+ exclude: []
104
+ reject:
105
+ - "/^.$/"
106
+ - "/[0-9]$/"
107
+ - "/[A-Z]/"
108
+ accept: []
109
+ UncommunicativeVariableName:
110
+ enabled: true
111
+ exclude: []
112
+ reject:
113
+ - "/^.$/"
114
+ - "/[0-9]$/"
115
+ - "/[A-Z]/"
116
+ accept:
117
+ - _
118
+ - e
119
+ UnusedParameters:
120
+ enabled: true
121
+ exclude: []
122
+ UnusedPrivateMethod:
123
+ enabled: false
124
+ UtilityFunction:
125
+ enabled: false
126
+
127
+ exclude_paths:
128
+ - config
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,81 @@
1
+ Style/Documentation:
2
+ Enabled: false
3
+
4
+ Layout/SpaceBeforeFirstArg:
5
+ Exclude:
6
+
7
+ Lint/AmbiguousBlockAssociation:
8
+ Exclude:
9
+ - spec/**/*
10
+
11
+ Metrics/AbcSize:
12
+ # The ABC size is a calculated magnitude, so this number can be an Integer or
13
+ # a Float.
14
+ Max: 15
15
+
16
+ Metrics/BlockLength:
17
+ CountComments: false
18
+ Max: 25
19
+ Exclude:
20
+ - '*.gemspec'
21
+ - config/**/*
22
+ - spec/**/*
23
+ ExcludedMethods:
24
+ - class_methods
25
+
26
+ Metrics/BlockNesting:
27
+ Max: 4
28
+
29
+ Metrics/ClassLength:
30
+ CountComments: false
31
+ Max: 200
32
+
33
+ # Avoid complex methods.
34
+ Metrics/CyclomaticComplexity:
35
+ Max: 7
36
+
37
+ Metrics/MethodLength:
38
+ CountComments: false
39
+ Max: 24
40
+
41
+ Metrics/ModuleLength:
42
+ CountComments: false
43
+ Max: 200
44
+
45
+ Layout/LineLength:
46
+ Max: 100
47
+ # To make it possible to copy or click on URIs in the code, we allow lines
48
+ # containing a URI to be longer than Max.
49
+ AllowURI: true
50
+ URISchemes:
51
+ - http
52
+ - https
53
+
54
+ Metrics/ParameterLists:
55
+ Max: 5
56
+ CountKeywordArgs: true
57
+
58
+ Metrics/PerceivedComplexity:
59
+ Max: 12
60
+
61
+ Style/FrozenStringLiteralComment:
62
+ Enabled: true
63
+
64
+ Style/ModuleFunction:
65
+ Enabled: false
66
+
67
+ Style/RescueModifier:
68
+ Exclude:
69
+ - spec/**/*
70
+
71
+ Naming/PredicateName:
72
+ Enabled: false
73
+
74
+ Style/HashEachMethods:
75
+ Enabled: true
76
+
77
+ Style/HashTransformKeys:
78
+ Enabled: true
79
+
80
+ Style/HashTransformValues:
81
+ Enabled: true
@@ -0,0 +1,28 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.5.0
5
+ - 2.6.0
6
+ - ruby-head
7
+
8
+ dist: bionic
9
+
10
+ jobs:
11
+ allow_failures:
12
+ - rvm: ruby-head
13
+
14
+ env:
15
+ global:
16
+ - CC_TEST_REPORTER_ID=cb01575b98b3b80848a3bc292ca6145860871470e5d0669453030d36578f9115
17
+
18
+ before_script:
19
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
20
+ - chmod +x ./cc-test-reporter
21
+ - ./cc-test-reporter before-build
22
+
23
+ script:
24
+ - bundle exec rake code_analysis
25
+ - bundle exec rspec
26
+
27
+ after_script:
28
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
@@ -0,0 +1,7 @@
1
+
2
+ # These owners will be the default owners for everything in
3
+ # the repo. Unless a later match takes precedence,
4
+ # @global-owner1 and @global-owner2 will be requested for
5
+ # review when someone opens a pull request.
6
+
7
+ * @fedeagripa @vitogit @mrubi-rootstrap @santib @juanmanuelramallo
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at federicogagripa@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in apple_sign_in.gemspec
6
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 fedeagripa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,116 @@
1
+ # AppleAuth
2
+
3
+ [![CI](https://travis-ci.org/rootstrap/apple_sign_in.svg?branch=master)](https://travis-ci.org/rootstrap/apple_sign_in)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/78453501221a76e3806e/maintainability)](https://codeclimate.com/github/rootstrap/apple_sign_in/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/78453501221a76e3806e/test_coverage)](https://codeclimate.com/github/rootstrap/apple_sign_in/test_coverage)
6
+
7
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/apple_auth`. To experiment with that code, run `bin/console` for an interactive prompt.
8
+
9
+ TODO: Delete this and the text above, and describe your gem
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'apple_auth'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle install
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install apple_auth
26
+
27
+ ------------------
28
+
29
+ After installing the gem, you need to run the generator.
30
+
31
+ $ rails g apple_auth:config
32
+
33
+ This will generate a new initializer: `apple_auth.rb` containing the following default configuration:
34
+ ```
35
+ AppleAuth.configure do |config|
36
+ # config.apple_client_id = <Your client_id in your Apple Developer account>
37
+ # config.apple_private_key = <Your private key provided by Apple>
38
+ # config.apple_key_id = <Your kid provided by Apple>
39
+ # config.apple_team_id = <Your team id provided by Apple>
40
+ # config.redirect_uri = <Your app redicrect url>
41
+ end
42
+ ```
43
+ Set your different credentials in the file by uncommenting the lines and adding your keys.
44
+
45
+ ## Usage
46
+
47
+ This show you an example of a settings
48
+
49
+ ```ruby
50
+ AppleAuth.configure do |config|
51
+ config.apple_client_id = 'com.yourapp...'
52
+ config.apple_private_key = "-----BEGIN PRIVATE KEY-----\nMIGTAgEA....\n-----END PRIVATE KEY-----"
53
+ config.apple_key_id = 'RTZ...'
54
+ config.apple_team_id = 'WNU...'
55
+ config.redirect_uri = 'https://localhost:3000'
56
+ end
57
+ ```
58
+
59
+ We strongly recommend to use environment variable when you add this values.
60
+
61
+ Apple sign in workflow:
62
+
63
+ ![alt text](https://docs-assets.developer.apple.com/published/360d59b776/rendered2x-1592224731.png)
64
+
65
+ For more information, check the [Apple oficial documentation](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api)
66
+
67
+ Validate JWT token and get user information:
68
+
69
+ ```ruby
70
+ # with a valid JWT
71
+ user_id = '000343.1d22d2937c7a4e56806dfb802b06c430...'
72
+ valid_jwt_token = 'eyJraWQiOiI4NkQ4OEtmIiwiYWxnIjoiUlMyNTYifQ.eyJpc...'
73
+ AppleAuth::UserIdentity.new(user_id, valid_jwt_token).validate!
74
+ >> { exp: 1595279622, email: "user@example.com", email_verified: true , ...}
75
+
76
+ # with an invalid JWT
77
+ invalid_jwt_token = 'eyJraWQiOiI4NkQsd4OEtmIiwiYWxnIjoiUlMyNTYifQ.edsyJpc...'
78
+ AppleAuth::UserIdentity.new(user_id, invalid_jwt_token).validate!
79
+ >> Traceback (most recent call last):..
80
+ >> ...
81
+ >> AppleAuth::Conditions::JWTValidationError
82
+ ```
83
+
84
+ Verify user identity and get token:
85
+
86
+ ```ruby
87
+ code = 'cfb77c21ecd444390a2c214cd33decdfb.0.mr...'
88
+ AppleAuth::Token.new(code).authenticate
89
+ >> { access_token: "a7058d...", expires_at: 1595894672, refresh_token: "r8f1ce..." }
90
+ ```
91
+
92
+ ## Development
93
+
94
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
95
+
96
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
97
+
98
+ ## Contributing
99
+
100
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/apple_sign_in. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/apple_auth/blob/master/CODE_OF_CONDUCT.md).
101
+
102
+
103
+ ## License
104
+
105
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
106
+
107
+ ## Code of Conduct
108
+
109
+ Everyone interacting in the AppleAuth project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/apple_auth/blob/master/CODE_OF_CONDUCT.md).
110
+
111
+ ## Credits
112
+
113
+ apple_auth is maintained by [Rootstrap](http://www.rootstrap.com) with the help of our
114
+ [contributors](https://github.com/rootstrap/apple_sign_in/contributors).
115
+
116
+ [<img src="https://s3-us-west-1.amazonaws.com/rootstrap.com/img/rs.png" width="100"/>](http://www.rootstrap.com)
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ task :code_analysis do
4
+ sh 'bundle exec rubocop lib spec'
5
+ sh 'bundle exec reek lib'
6
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/apple_auth/base/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'apple_auth'
7
+ spec.version = AppleAuth::Base::VERSION
8
+ spec.authors = ['Timothy Peraza, Antonieta Alvarez, Martín Morón']
9
+ spec.email = ['timothy@rootstrap.com, antonieta.alvarez@rootstrap.com, martin.jaime@rootstrap.com']
10
+
11
+ spec.summary = 'Integration with Apple Sign In'
12
+ spec.homepage = 'https://github.com/rootstrap/apple_auth'
13
+ spec.license = 'MIT'
14
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/rootstrap/apple_auth'
18
+ spec.metadata['changelog_uri'] = 'https://github.com/rootstrap/apple_auth'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ # Production dependencies
30
+ spec.add_dependency 'jwt', '~> 2.2'
31
+ spec.add_dependency 'oauth2', '~> 1.4'
32
+
33
+ # Development dependencies
34
+ spec.add_development_dependency 'generator_spec', '~> 0.9.4'
35
+ spec.add_development_dependency 'byebug', '~> 11.1'
36
+ spec.add_development_dependency 'railties', '~> 6.0'
37
+ spec.add_development_dependency 'rake', '~> 13.0'
38
+ spec.add_development_dependency 'reek', '~> 5.6'
39
+ spec.add_development_dependency 'rspec', '~> 3.9'
40
+ spec.add_development_dependency 'rubocop', '~> 0.80'
41
+ spec.add_development_dependency 'simplecov', '~> 0.17.1'
42
+ spec.add_development_dependency 'webmock', '~> 3.8'
43
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'apple_auth'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Rails Core
4
+ require 'active_support/core_ext/hash'
5
+ require 'rails/generators'
6
+
7
+ # Ruby Core
8
+ require 'base64'
9
+ require 'json'
10
+ require 'net/http'
11
+
12
+ # Gems
13
+ require 'jwt'
14
+ require 'oauth2'
15
+
16
+ # Files
17
+ require 'apple_auth/config'
18
+ require 'apple_auth/helpers/conditions/jwt_validation_error'
19
+
20
+ require 'apple_auth/helpers/conditions/aud_condition'
21
+ require 'apple_auth/helpers/conditions/exp_condition'
22
+ require 'apple_auth/helpers/conditions/iat_condition'
23
+ require 'apple_auth/helpers/conditions/iss_condition'
24
+ require 'apple_auth/helpers/jwt_conditions'
25
+
26
+ require 'apple_auth/user_identity'
27
+ require 'apple_auth/token'
28
+
29
+ require 'generators/apple_auth/config/config_generator'
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apple_auth/base/version'
4
+
5
+ module AppleAuth
6
+ module Base
7
+ class Error < StandardError; end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ module Base
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ class << self
5
+ def configure
6
+ yield config
7
+ end
8
+
9
+ def reset_configuration
10
+ @config = Config.new
11
+ end
12
+
13
+ def config
14
+ @config ||= Config.new
15
+ end
16
+ end
17
+
18
+ class Config
19
+ attr_accessor :apple_client_id, :apple_private_key, :apple_key_id,
20
+ :apple_team_id, :redirect_uri
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ module Conditions
5
+ class AudCondition
6
+ def initialize(jwt)
7
+ @aud = jwt['aud']
8
+ end
9
+
10
+ def validate!
11
+ return true if @aud == AppleAuth.config.apple_client_id
12
+
13
+ raise JWTValidationError, 'jwt_aud is different to apple_client_id'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ module Conditions
5
+ class ExpCondition
6
+ def initialize(jwt)
7
+ @exp = jwt['exp'].to_i
8
+ end
9
+
10
+ def validate!
11
+ return true if @exp > Time.now.to_i
12
+
13
+ raise JWTValidationError, 'Expired jwt_exp'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ module Conditions
5
+ class IatCondition
6
+ def initialize(jwt)
7
+ @iat = jwt['iat'].to_i
8
+ end
9
+
10
+ def validate!
11
+ return true if @iat <= Time.now.to_i
12
+
13
+ raise JWTValidationError, 'jwt_iat is greater than now'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ module Conditions
5
+ class IssCondition
6
+ APPLE_ISS = 'https://appleid.apple.com'
7
+
8
+ def initialize(jwt)
9
+ @iss = jwt['iss']
10
+ end
11
+
12
+ def validate!
13
+ return true if @iss.include?(APPLE_ISS)
14
+
15
+ raise JWTValidationError, 'jwt_iss is different to apple_iss'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ module Conditions
5
+ class JWTValidationError < StandardError; end
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: false
2
+
3
+ module AppleAuth
4
+ class JWTConditions
5
+ include Conditions
6
+
7
+ CONDITIONS = [
8
+ AudCondition,
9
+ ExpCondition,
10
+ IatCondition,
11
+ IssCondition
12
+ ].freeze
13
+
14
+ attr_reader :user_identity, :decoded_jwt
15
+
16
+ def initialize(user_identity, decoded_jwt)
17
+ @user_identity = user_identity
18
+ @decoded_jwt = decoded_jwt
19
+ end
20
+
21
+ def validate!
22
+ JWT::ClaimsValidator.new(decoded_jwt).validate! && validate_sub! && jwt_conditions_validate!
23
+ rescue JWT::InvalidPayload => e
24
+ raise JWTValidationError, e.message
25
+ end
26
+
27
+ private
28
+
29
+ def validate_sub!
30
+ return true if user_identity && user_identity == decoded_jwt['sub']
31
+
32
+ raise JWTValidationError, 'Not valid Sub'
33
+ end
34
+
35
+ def jwt_conditions_validate!
36
+ conditions_results = CONDITIONS.map do |condition|
37
+ condition.new(decoded_jwt).validate!
38
+ end
39
+ conditions_results.all? { |value| value == true }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ class Token
5
+ APPLE_AUD = 'https://appleid.apple.com'
6
+ APPLE_CONFIG = AppleAuth.config
7
+ APPLE_CODE_TYPE = 'authorization_code'
8
+ APPLE_ALG = 'ES256'
9
+
10
+ def initialize(code)
11
+ @code = code
12
+ end
13
+
14
+ # :reek:FeatureEnvy
15
+ def authenticate
16
+ access_token = apple_access_token
17
+ access_token.refresh! if access_token.expired?
18
+
19
+ reponse_hash(access_token)
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :code
25
+
26
+ def apple_token_params
27
+ {
28
+ client_id: APPLE_CONFIG.apple_team_id,
29
+ client_secret: client_secret_from_jwt,
30
+ grant_type: APPLE_CODE_TYPE,
31
+ redirect_uri: APPLE_CONFIG.redirect_uri,
32
+ code: code
33
+ }
34
+ end
35
+
36
+ def client_secret_from_jwt
37
+ JWT.encode(claims, gen_private_key, APPLE_ALG, claims_headers)
38
+ end
39
+
40
+ def claims
41
+ time_now = Time.now.to_i
42
+ {
43
+ iss: APPLE_CONFIG.apple_team_id,
44
+ iat: time_now,
45
+ exp: time_now + 10.minutes.to_i,
46
+ aud: APPLE_AUD,
47
+ sub: APPLE_CONFIG.apple_client_id
48
+ }
49
+ end
50
+
51
+ def claims_headers
52
+ {
53
+ alg: APPLE_ALG,
54
+ kid: AppleAuth.config.apple_key_id
55
+ }
56
+ end
57
+
58
+ def request_header
59
+ {
60
+ 'Content-Type': 'application/x-www-form-urlencoded'
61
+ }
62
+ end
63
+
64
+ def gen_private_key
65
+ key = AppleAuth.config.apple_private_key
66
+ key = OpenSSL::PKey::EC.new(key) unless key.class == OpenSSL::PKey::EC
67
+ key
68
+ end
69
+
70
+ def client_urls
71
+ {
72
+ site: APPLE_AUD,
73
+ authorize_url: '/auth/authorize',
74
+ token_url: '/auth/token'
75
+ }
76
+ end
77
+
78
+ def reponse_hash(access_token)
79
+ token_hash = { access_token: access_token.token }
80
+
81
+ expires = access_token.expires?
82
+ if expires
83
+ token_hash[:expires_at] = access_token.expires_at
84
+ refresh_token = access_token.refresh_token
85
+ token_hash[:refresh_token] = refresh_token if refresh_token
86
+ end
87
+
88
+ token_hash
89
+ end
90
+
91
+ def apple_access_token
92
+ client = ::OAuth2::Client.new(APPLE_CONFIG.apple_client_id,
93
+ client_secret_from_jwt,
94
+ client_urls)
95
+ client.auth_code.get_token(code, { redirect_uri: APPLE_CONFIG.redirect_uri }, {})
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ class UserIdentity
5
+ APPLE_KEY_URL = 'https://appleid.apple.com/auth/keys'
6
+
7
+ attr_reader :user_identity, :jwt
8
+
9
+ def initialize(user_identity, jwt)
10
+ @user_identity = user_identity
11
+ @jwt = jwt
12
+ end
13
+
14
+ def validate!
15
+ token_data = decoded_jwt
16
+
17
+ JWTConditions.new(user_identity, token_data).validate!
18
+
19
+ token_data.symbolize_keys
20
+ 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
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppleAuth
4
+ module Generators
5
+ class ConfigGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ def copy_config_file
9
+ copy_file 'config.rb', 'config/initializers/apple_auth.rb'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ AppleAuth.configure do |config|
4
+ # config.apple_client_id = <Your client_id in your Apple Developer account>
5
+ # config.apple_private_key = <Your private key provided by Apple>
6
+ # config.apple_key_id = <Your kid provided by Apple>
7
+ # config.apple_team_id = <Your team id provided by Apple>
8
+ # config.redirect_uri = <Your app redicrect url>
9
+ end
metadata ADDED
@@ -0,0 +1,228 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apple_auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Timothy Peraza, Antonieta Alvarez, Martín Morón
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-07-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jwt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: oauth2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: generator_spec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.4
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.4
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '11.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '11.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: railties
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '6.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '6.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '13.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '13.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: reek
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '5.6'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '5.6'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.9'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.9'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.80'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.80'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.17.1
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 0.17.1
153
+ - !ruby/object:Gem::Dependency
154
+ name: webmock
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '3.8'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '3.8'
167
+ description:
168
+ email:
169
+ - timothy@rootstrap.com, antonieta.alvarez@rootstrap.com, martin.jaime@rootstrap.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".gitignore"
175
+ - ".reek.yml"
176
+ - ".rspec"
177
+ - ".rubocop.yml"
178
+ - ".travis.yml"
179
+ - CODEOWNERS
180
+ - CODE_OF_CONDUCT.md
181
+ - Gemfile
182
+ - LICENSE.txt
183
+ - README.md
184
+ - Rakefile
185
+ - apple_auth.gemspec
186
+ - bin/console
187
+ - bin/setup
188
+ - lib/apple_auth.rb
189
+ - lib/apple_auth/base.rb
190
+ - lib/apple_auth/base/version.rb
191
+ - lib/apple_auth/config.rb
192
+ - lib/apple_auth/helpers/conditions/aud_condition.rb
193
+ - lib/apple_auth/helpers/conditions/exp_condition.rb
194
+ - lib/apple_auth/helpers/conditions/iat_condition.rb
195
+ - lib/apple_auth/helpers/conditions/iss_condition.rb
196
+ - lib/apple_auth/helpers/conditions/jwt_validation_error.rb
197
+ - lib/apple_auth/helpers/jwt_conditions.rb
198
+ - lib/apple_auth/token.rb
199
+ - lib/apple_auth/user_identity.rb
200
+ - lib/generators/apple_auth/config/config_generator.rb
201
+ - lib/generators/apple_auth/config/templates/config.rb
202
+ homepage: https://github.com/rootstrap/apple_auth
203
+ licenses:
204
+ - MIT
205
+ metadata:
206
+ homepage_uri: https://github.com/rootstrap/apple_auth
207
+ source_code_uri: https://github.com/rootstrap/apple_auth
208
+ changelog_uri: https://github.com/rootstrap/apple_auth
209
+ post_install_message:
210
+ rdoc_options: []
211
+ require_paths:
212
+ - lib
213
+ required_ruby_version: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: 2.3.0
218
+ required_rubygems_version: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ requirements: []
224
+ rubygems_version: 3.0.3
225
+ signing_key:
226
+ specification_version: 4
227
+ summary: Integration with Apple Sign In
228
+ test_files: []