apple_id 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57f3b090e123b3239c7ee81b0d54fb532d1cded860820d69f66985a5c764c789
4
- data.tar.gz: 6f89d4c1685fd2b59220aab71e3a11d852ac75aaf6422c3b959626482467bb0f
3
+ metadata.gz: e000f6003a2baeecbb6683a9636b311cac3eec9ec20df988f79f0bbd50231135
4
+ data.tar.gz: e4890bdb603981bcd9d4489915e607029490c221210816315f96dd3d6fccfd9d
5
5
  SHA512:
6
- metadata.gz: bbe0117b9c67246b03b6814c5ba75f1fd7f893129cfdcac09d5ba9aa731f7f3f462cb233634a75acd6edd38ffeb34982a37b4591278271654a11a91d79d11170
7
- data.tar.gz: 7541063a289eec294bcaadd363f6bbb23f937c42fd1334e6ff7be8a1e4a24e945cb62268cb91da896e256eca3060ea1bd402a9cee96b4566397a05b286453032
6
+ metadata.gz: 1bb4b14f28e1906cf6517e2382422a5dd44c864479ea550bf2ad54a2758b166ab0464e0e04539f0e2f26c592daae932eba81d4f82f508b0aae2d45f956d1bcce
7
+ data.tar.gz: 406ae5c6dca6868bfac746b83b3422b1a92876e6f6d75ced236f2b653b88e8d6469ca5544b33afae387d8bca78aa2237608e1557022daff8d3f334667c8e1663
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # AppleID
2
2
 
3
- 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_id`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ "Sign-in with Apple" is an implementation of OpenID Connect with small custom features.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ This gem handles such custom features.
6
+
7
+ Basically, this gem is based on my [OpenID Connect gem](https://github.com/nov/openid_connect) and [OAuth2 gem](https://github.com/nov/rack-oauth2), so the usage is almost same with them.
6
8
 
7
9
  ## Installation
8
10
 
@@ -22,7 +24,54 @@ Or install it yourself as:
22
24
 
23
25
  ## Usage
24
26
 
25
- TODO: Write usage instructions here
27
+ There is [a sample rails app](https://github.com/nov/signin-with-apple) running at [signin-with-apple.herokuapp.com](https://signin-with-apple.herokuapp.com).
28
+
29
+ If you run script in your terminal only, do like below.
30
+
31
+ ```ruby
32
+ require 'apple_id'
33
+
34
+ # NOTE: in debugging mode, you can see all HTTPS request & response in the log.
35
+ # AppleID.debug!
36
+
37
+ pem = <<-PEM
38
+ -----BEGIN PRIVATE KEY-----
39
+ :
40
+ :
41
+ -----END PRIVATE KEY-----
42
+ PEM
43
+ private_key = OpenSSL::PKey::EC.new pem
44
+
45
+ client = AppleID::Client.new(
46
+ identifier: '<YOUR-CLIENT-ID>',
47
+ team_id: '<YOUR-TEAM-ID>',
48
+ key_id: '<YOUR-KEY-ID>',
49
+ private_key: private_key,
50
+ redirect_uri: '<YOUR-REDIRECT-URI>'
51
+ )
52
+
53
+ authorization_uri = client.authorization_uri(scope: [:email, :name])
54
+ puts authorization_uri
55
+ `open "#{authorization_uri}"`
56
+
57
+ print 'code: ' and STDOUT.flush
58
+ code = gets.chop
59
+
60
+ client.authorization_code = code
61
+ response = client.access_token!
62
+
63
+ response.id_token.verify!(
64
+ client,
65
+ access_token: response.access_token,
66
+
67
+ # NOTE:
68
+ # When verifying signature, one http request to Apple's JWKs are required.
69
+ # You can skip ID Token signature verification when you got the token directly from the token endpoint in TLS channel.
70
+ verify_signature: false
71
+ )
72
+ puts response.id_token.sub # => OpenID Connect Subject Identifier (= Apple User ID)
73
+ puts response.id_token.original_jwt.pretty_generate
74
+ ```
26
75
 
27
76
  ## Development
28
77
 
data/Rakefile CHANGED
@@ -1,6 +1,19 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
3
 
4
+ require 'rspec/core/rake_task'
4
5
  RSpec::Core::RakeTask.new(:spec)
5
6
 
7
+ namespace :coverage do
8
+ desc 'Open coverage report'
9
+ task :report do
10
+ require 'simplecov'
11
+ `open "#{File.join SimpleCov.coverage_path, 'index.html'}"`
12
+ end
13
+ end
14
+
15
+ task :spec do
16
+ Rake::Task[:'coverage:report'].invoke unless ENV['TRAVIS_RUBY_VERSION']
17
+ end
18
+
6
19
  task :default => :spec
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.1.0
@@ -20,7 +20,10 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_runtime_dependency 'rack-oauth2', '~> 1.9.3'
22
22
  spec.add_runtime_dependency 'openid_connect', '~> 1.0'
23
- spec.add_development_dependency 'bundler', '~> 1.17'
24
- spec.add_development_dependency 'rake', '~> 10.0'
25
- spec.add_development_dependency 'rspec', '~> 3.0'
23
+ spec.add_development_dependency 'bundler'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'rspec'
26
+ spec.add_development_dependency 'rspec-its'
27
+ spec.add_development_dependency 'webmock'
28
+ spec.add_development_dependency 'simplecov'
26
29
  end
@@ -2,6 +2,7 @@ require 'openid_connect'
2
2
 
3
3
  module AppleID
4
4
  ISSUER = 'https://appleid.apple.com'
5
+ JWKS_URI = 'https://appleid.apple.com/auth/keys'
5
6
 
6
7
  VERSION = ::File.read(
7
8
  ::File.join(::File.dirname(__FILE__), '../VERSION')
@@ -50,4 +51,6 @@ module AppleID
50
51
  self.debugging = false
51
52
  end
52
53
 
53
- require 'apple_id/client'
54
+ require 'apple_id/client'
55
+ require 'apple_id/access_token'
56
+ require 'apple_id/id_token'
@@ -0,0 +1,10 @@
1
+ module AppleID
2
+ class AccessToken < OpenIDConnect::AccessToken
3
+ undef_required_attributes :client
4
+
5
+ def initialize(access_token, attributes = {})
6
+ super attributes.merge(access_token: access_token)
7
+ self.id_token = IdToken.decode(id_token) if id_token.present?
8
+ end
9
+ end
10
+ end
@@ -1,12 +1,13 @@
1
1
  module AppleID
2
2
  class Client < OpenIDConnect::Client
3
+ class Error < Rack::OAuth2::Client::Error; end
4
+
3
5
  attr_required :team_id, :key_id, :private_key
4
6
 
5
7
  def initialize(attributes)
6
8
  attributes_with_default = {
7
- host: 'appleid.apple.com',
8
- authorization_endpoint: '/auth/authorize',
9
- token_endpoint: '/auth/token'
9
+ authorization_endpoint: File.join(ISSUER, '/auth/authorize'),
10
+ token_endpoint: File.join(ISSUER, '/auth/token')
10
11
  }.merge(attributes)
11
12
  super attributes_with_default
12
13
  end
@@ -21,7 +22,7 @@ module AppleID
21
22
  def client_secret_jwt
22
23
  jwt = JSON::JWT.new(
23
24
  iss: team_id,
24
- aud: AppleID::ISSUER,
25
+ aud: ISSUER,
25
26
  sub: identifier,
26
27
  iat: Time.now,
27
28
  exp: 1.minutes.from_now
@@ -29,5 +30,22 @@ module AppleID
29
30
  jwt.kid = key_id
30
31
  jwt.sign(private_key)
31
32
  end
33
+
34
+ def setup_required_scope(scopes)
35
+ # NOTE:
36
+ # openid_connect gem add `openid` scope automatically.
37
+ # However, it's not required for Sign-in with Apple.
38
+ scopes
39
+ end
40
+
41
+ def handle_success_response(response)
42
+ token_hash = JSON.parse(response.body).with_indifferent_access
43
+ AccessToken.new token_hash.delete(:access_token), token_hash
44
+ end
45
+
46
+ def handle_error_response(response)
47
+ error = JSON.parse(response.body).with_indifferent_access
48
+ raise Error.new(response.status, error)
49
+ end
32
50
  end
33
- end
51
+ end
@@ -0,0 +1,45 @@
1
+ module AppleID
2
+ class IdToken < OpenIDConnect::ResponseObject::IdToken
3
+ class VerificationFailed < StandardError; end
4
+
5
+ alias_method :original_jwt, :raw_attributes
6
+
7
+ def verify!(expected_client, access_token: nil, code: nil, verify_signature: true)
8
+ verify_signature! if verify_signature
9
+ verify_claims! expected_client, access_token, code
10
+ self
11
+ end
12
+
13
+ class << self
14
+ def decode(jwt_string)
15
+ super jwt_string, :skip_verification
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def jwks
22
+ @jwks ||= JSON.parse(
23
+ OpenIDConnect.http_client.get_content(JWKS_URI)
24
+ ).with_indifferent_access
25
+ JSON::JWK::Set.new @jwks[:keys]
26
+ end
27
+
28
+ def verify_signature!
29
+ original_jwt.verify! jwks
30
+ rescue
31
+ raise VerificationFailed, 'Signature Verification Failed'
32
+ end
33
+
34
+ def verify_claims!(expected_client, access_token, code)
35
+ # TODO: verify at_hash & c_hash
36
+ unless (
37
+ iss == ISSUER &&
38
+ aud == expected_client.identifier &&
39
+ Time.now.to_i.between?(iat, exp)
40
+ )
41
+ raise VerificationFailed, 'Claims Verification Failed'
42
+ end
43
+ end
44
+ end
45
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apple_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-04 00:00:00.000000000 Z
11
+ date: 2019-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack-oauth2
@@ -42,44 +42,86 @@ dependencies:
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.17'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '1.17'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '10.0'
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '10.0'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '3.0'
75
+ version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-its
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
81
123
  - !ruby/object:Gem::Version
82
- version: '3.0'
124
+ version: '0'
83
125
  description: Sign-in with Apple backend library in Ruby.
84
126
  email:
85
127
  - nov@matake.jp
@@ -100,7 +142,9 @@ files:
100
142
  - bin/console
101
143
  - bin/setup
102
144
  - lib/apple_id.rb
145
+ - lib/apple_id/access_token.rb
103
146
  - lib/apple_id/client.rb
147
+ - lib/apple_id/id_token.rb
104
148
  homepage: https://github.com/nov/apple_id
105
149
  licenses:
106
150
  - MIT