apple_id 0.0.1 → 0.1.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/README.md +52 -3
- data/Rakefile +15 -2
- data/VERSION +1 -1
- data/apple_id.gemspec +6 -3
- data/lib/apple_id.rb +4 -1
- data/lib/apple_id/access_token.rb +10 -0
- data/lib/apple_id/client.rb +23 -5
- data/lib/apple_id/id_token.rb +45 -0
- metadata +58 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e000f6003a2baeecbb6683a9636b311cac3eec9ec20df988f79f0bbd50231135
|
4
|
+
data.tar.gz: e4890bdb603981bcd9d4489915e607029490c221210816315f96dd3d6fccfd9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bb4b14f28e1906cf6517e2382422a5dd44c864479ea550bf2ad54a2758b166ab0464e0e04539f0e2f26c592daae932eba81d4f82f508b0aae2d45f956d1bcce
|
7
|
+
data.tar.gz: 406ae5c6dca6868bfac746b83b3422b1a92876e6f6d75ced236f2b653b88e8d6469ca5544b33afae387d8bca78aa2237608e1557022daff8d3f334667c8e1663
|
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# AppleID
|
2
2
|
|
3
|
-
|
3
|
+
"Sign-in with Apple" is an implementation of OpenID Connect with small custom features.
|
4
4
|
|
5
|
-
|
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
|
-
|
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
|
2
|
-
|
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
|
+
0.1.0
|
data/apple_id.gemspec
CHANGED
@@ -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'
|
24
|
-
spec.add_development_dependency 'rake'
|
25
|
-
spec.add_development_dependency 'rspec'
|
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
|
data/lib/apple_id.rb
CHANGED
@@ -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
|
data/lib/apple_id/client.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
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:
|
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
|
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-
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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
|