Implemented Device Check API call

Implemented Device Check API call
This commit is contained in:
Axel
2018-10-29 23:03:15 +08:00
commit a1d5dacb5e
7 changed files with 182 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
DEVICE_CHECK_KEY_FILE="AuthKey_ABCDEFGHI.p8"
DEVICE_CHECK_KEY_ID=ABCDEFGHI
DEVICE_CHECK_TEAM_ID=HZZZZZZZZZ
+4
View File
@@ -0,0 +1,4 @@
.DS_Store
/.env
.env
*.p8
+7
View File
@@ -0,0 +1,7 @@
source 'https://rubygems.org'
gem 'dotenv'
gem 'jwt'
gem 'http'
gem 'sinatra'
gem 'sinatra-contrib'
+67
View File
@@ -0,0 +1,67 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (5.2.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
backports (3.11.4)
concurrent-ruby (1.0.5)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.5.0)
http (4.0.0)
addressable (~> 2.3)
http-cookie (~> 1.0)
http-form_data (~> 2.0)
http_parser.rb (~> 0.6.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
http-form_data (2.1.1)
http_parser.rb (0.6.0)
i18n (1.1.1)
concurrent-ruby (~> 1.0)
jwt (2.1.0)
minitest (5.11.3)
multi_json (1.13.1)
mustermann (1.0.3)
public_suffix (3.0.3)
rack (2.0.5)
rack-protection (2.0.4)
rack
sinatra (2.0.4)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.4)
tilt (~> 2.0)
sinatra-contrib (2.0.4)
activesupport (>= 4.0.0)
backports (>= 2.8.2)
multi_json
mustermann (~> 1.0)
rack-protection (= 2.0.4)
sinatra (= 2.0.4)
tilt (>= 1.3, < 3)
thread_safe (0.3.6)
tilt (2.0.8)
tzinfo (1.2.5)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
PLATFORMS
ruby
DEPENDENCIES
dotenv
http
jwt
sinatra
sinatra-contrib
BUNDLED WITH
1.16.4
+3
View File
@@ -0,0 +1,3 @@
require 'sinatra/activerecord'
require 'sinatra/activerecord/rake'
require './app'
+95
View File
@@ -0,0 +1,95 @@
require 'sinatra'
require 'sinatra/json'
require 'dotenv'
Dotenv.load
require 'openssl'
require 'http'
require 'jwt'
require 'SecureRandom'
configure do
# change to https://api.devicecheck.apple.com for production app, ie. App in App Store / Testflight
set :device_check_api_url, 'https://api.development.devicecheck.apple.com'
set :query_url, settings.device_check_api_url + '/v1/query_two_bits'
set :update_url, settings.device_check_api_url + '/v1/update_two_bits'
end
get '/' do
"Please send the base 64 encoded device check token in JSON parameter key 'token' to POST /redeem"
end
post '/redeem' do
begin
request_payload = JSON.parse request.body.read
rescue JSON::ParserError
return json({ message: 'please supply a valid token parameter', redeemable: false })
end
# request_payload['token'] is the 'token' parameter we sent in the iOS app
unless request_payload.key? 'token'
return json({ message: 'please supply a token', redeemable: false })
end
response = query_two_bits(request_payload['token'])
unless response.status == 200
return json({ message: 'Error communicating with Apple server', redeemable: false })
end
begin
response_hash = JSON.parse response.body
rescue JSON::ParserError
# if status 200 and no json returned, means the state was not set previously, we set them to nil / null
response_hash = { bit0: nil, bit1: nil }
end
# if the bit0 has been set and set to true, means user has already redeemed using their phone
if response_hash.key? 'bit0'
if response_hash['bit0'] == true
return json({ message: 'You have already redeemed it previously', redeemable: false })
end
end
# update the first bit to true, and tell the iOS app user can redeem the free gift
update_two_bits(request_payload['token'], true, false)
json({ message: 'Congratulations!', redeemable: true })
end
def jwt_token
private_key = File.read(ENV['DEVICE_CHECK_KEY_FILE'])
key_id = ENV['DEVICE_CHECK_KEY_ID']
team_id = ENV['DEVICE_CHECK_TEAM_ID']
# Elliptic curve key, similar to login password, used for communication with apple server
ec_key = OpenSSL::PKey::EC.new(private_key)
jwt_token = JWT.encode({iss: team_id, iat: Time.now.to_i}, ec_key, 'ES256', {kid: key_id,})
end
def query_two_bits(device_token)
payload = {
'device_token' => device_token,
'timestamp' => (Time.now.to_f * 1000).to_i,
'transaction_id' => SecureRandom.uuid
}
response = HTTP.auth("Bearer #{jwt_token}").post(settings.query_url, json: payload)
# if there is no bit state set before, apple will return the string 'Bit State Not Found' instead of json
# if the bit state was set before, below will be returned
#{"bit0":false,"bit1":false,"last_update_time":"2018-10"}
end
def update_two_bits(device_token, bit_zero, bit_one)
payload = {
'device_token' => device_token,
'timestamp' => (Time.now.to_f * 1000).to_i,
'transaction_id' => SecureRandom.uuid,
'bit0': bit_zero,
'bit1': bit_one
}
response = HTTP.auth("Bearer #{jwt_token}").post(settings.update_url, json: payload)
# Apple will return status 200 with blank response body if the update is successful
end
+3
View File
@@ -0,0 +1,3 @@
require './app'
run Sinatra::Application