blockstack 7.0.0 → 7.0.1
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 +1 -1
- data/Rakefile +10 -4
- data/bin/console +4 -4
- data/blockstack.gemspec +18 -18
- data/lib/blockstack/user.rb +100 -105
- data/lib/blockstack/version.rb +1 -1
- data/lib/blockstack.rb +133 -133
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98dc45c77ec49a8d6725daf6e4eb4e642edad166
|
4
|
+
data.tar.gz: 02e764103cafbc18bac059b23487a8827ccc1128
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64266c0200959358ae3f0e06d3d7678d56bc1a0c9030e831dd21b56b209e0d2397a636cdf47d04107b6b960b11e18d9124172504aa3f62f1c138f886b4d81d43
|
7
|
+
data.tar.gz: 589cec9d95fc24c24f64679da6b3f4fdea85e9d9924044fd0a1038f8482fd80e5aa074771a5f81f4d7b6c4746e9490a480bda99b1d1a73753efb2876224d6b8a
|
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
RuboCop::RakeTask.new(:rubocop) do |t|
|
6
|
+
t.options = ['--lint']
|
7
|
+
end
|
3
8
|
|
4
9
|
RSpec::Core::RakeTask.new
|
5
10
|
|
6
|
-
|
7
|
-
task :
|
11
|
+
|
12
|
+
task test: [:rubocop, :spec]
|
13
|
+
task default: :test
|
data/bin/console
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'blockstack'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
8
8
|
|
9
9
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require
|
10
|
+
# require 'pry'
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start
|
data/blockstack.gemspec
CHANGED
@@ -4,29 +4,29 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'blockstack/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'blockstack'
|
8
8
|
spec.version = Blockstack::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.homepage =
|
13
|
-
spec.license =
|
9
|
+
spec.authors = ['Larry Salibra']
|
10
|
+
spec.email = ['rubygems@larrysalibra.com']
|
11
|
+
spec.summary = 'The Blockstack ruby library for identity and authentication'
|
12
|
+
spec.homepage = 'https://github.com/larrysalibra/blockstack-ruby'
|
13
|
+
spec.license = 'MIT'
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
|
-
|
16
|
+
f.match(%r{^(test|spec|features)/})
|
17
17
|
end
|
18
|
-
spec.bindir =
|
18
|
+
spec.bindir = 'exe'
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
|
28
|
-
spec.add_dependency "jwtb", "2.0.0.beta2.bsk1"
|
29
|
-
spec.add_dependency "bitcoin-ruby"
|
30
|
-
spec.add_dependency "faraday"
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
23
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
24
|
+
spec.add_development_dependency 'rspec'
|
25
|
+
spec.add_development_dependency 'webmock'
|
26
|
+
spec.add_development_dependency 'vcr'
|
27
|
+
spec.add_development_dependency 'rubocop'
|
31
28
|
|
29
|
+
spec.add_dependency 'jwtb', '2.0.0.beta2.bsk1'
|
30
|
+
spec.add_dependency 'bitcoin-ruby'
|
31
|
+
spec.add_dependency 'faraday'
|
32
32
|
end
|
data/lib/blockstack/user.rb
CHANGED
@@ -1,124 +1,119 @@
|
|
1
1
|
module Blockstack
|
2
2
|
class User
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
def self.from_json(json, username)
|
4
|
+
User.new(json, username)
|
5
|
+
end
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
7
|
+
attr_reader :username
|
8
|
+
attr_reader :name_formatted
|
9
|
+
attr_reader :avatar_url
|
10
|
+
attr_reader :cover_url
|
11
|
+
attr_reader :location_formatted
|
12
|
+
attr_reader :website
|
13
|
+
attr_reader :bio
|
14
|
+
attr_reader :angellist_username
|
15
|
+
attr_reader :github_username
|
16
|
+
attr_reader :facebook_username
|
17
|
+
attr_reader :twitter_username
|
18
|
+
attr_reader :instagram_username
|
19
|
+
attr_reader :linkedin_url
|
20
|
+
attr_reader :bitcoin_address
|
21
|
+
attr_reader :bitmessage_address
|
22
|
+
attr_reader :bitcoinotc_username
|
23
|
+
attr_reader :pgp_fingerprint
|
24
|
+
attr_reader :pgp_url
|
25
|
+
attr_reader :orgs
|
26
|
+
attr_reader :schema_version
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
json = json["profile"]
|
31
|
-
end
|
32
|
-
if(json['v'] == "0.2")
|
33
|
-
@username = username
|
34
|
-
@name_formatted = json["name"]["formatted"] if json["name"]
|
35
|
-
@avatar_url = json["avatar"]["url"] if json["avatar"]
|
36
|
-
@cover_url = json["cover"]["url"] if json["cover"]
|
37
|
-
@location_formatted = json["location"]["formatted"] if json["location"]
|
38
|
-
@website = json["website"]
|
39
|
-
@bio = json["bio"]
|
40
|
-
@angellist_username = json["angellist"]["username"] if json["angellist"]
|
41
|
-
@github_username = json["github"]["username"] if json["github"]
|
42
|
-
@facebook_username = json["facebook"]["username"] if json["facebook"]
|
43
|
-
@twitter_username = json["twitter"]["username"] if json["twitter"]
|
44
|
-
@instagram_username = json["instagram"]["username"] if json["instagram"]
|
45
|
-
@linkedin_url = json["linkedin"]["url"] if json["linkedin"]
|
46
|
-
@bitcoin_address = json["bitcoin"]["address"] if json["bitcoin"]
|
47
|
-
@bitmessage_address = json["bitmessage"]["address"] if json["bitmessage"]
|
48
|
-
@bitcoinotc_username = json["bitcoinotc"]["username"] if json["bitcoinotc"]
|
49
|
-
@pgp_fingerprint = json["pgp"]["fingerprint"] if json["pgp"]
|
50
|
-
@pgp_url = json["pgp"]["url"] if json["pgp"]
|
51
|
-
@schema_version = json["v"]
|
52
|
-
@orgs = parse_orgs(json["orgs"])
|
53
|
-
else
|
54
|
-
@username = username
|
55
|
-
@name_formatted = json["name"] if json["name"]
|
56
|
-
@avatar_url = find_image_url(json, 'avatar')
|
57
|
-
@cover_url = find_image_url(json, 'cover')
|
58
|
-
@location_formatted = json["address"]["addressLocality"] if json["address"]
|
59
|
-
@website = json["website"][0]["url"] if json["website"] && json["website"][0]
|
60
|
-
@bio = json["description"]
|
61
|
-
@angellist_username = find_account_username(json, "angellist")
|
62
|
-
@github_username = find_account_username(json, "github")
|
63
|
-
@facebook_username = find_account_username(json, "facebook")
|
64
|
-
@twitter_username = find_account_username(json, "twitter")
|
65
|
-
@instagram_username = find_account_username(json, "instagram")
|
66
|
-
@linkedin_url = find_account_username(json, "linkedin")
|
67
|
-
@bitcoin_address = find_account_username(json, "bitcoin")
|
68
|
-
@bitmessage_address = find_account_username(json, "bitmessage")
|
69
|
-
@bitcoinotc_username = find_account_username(json, "bitcoinotc")
|
70
|
-
@pgp_fingerprint = find_account_username(json, "pgp")
|
71
|
-
@pgp_url = find_account(json, "pgp")['contentUrl'] if @pgp_fingerprint
|
72
|
-
@schema_version = "0.3"
|
73
|
-
@orgs = parse_orgs(json["orgs"])
|
74
|
-
end
|
28
|
+
def initialize(json, username)
|
29
|
+
json = json['profile'] if json['profile']
|
75
30
|
|
31
|
+
if json['v'] == '0.2'
|
32
|
+
@username = username
|
33
|
+
@name_formatted = json['name']['formatted'] if json['name']
|
34
|
+
@avatar_url = json['avatar']['url'] if json['avatar']
|
35
|
+
@cover_url = json['cover']['url'] if json['cover']
|
36
|
+
@location_formatted = json['location']['formatted'] if json['location']
|
37
|
+
@website = json['website']
|
38
|
+
@bio = json['bio']
|
39
|
+
@angellist_username = json['angellist']['username'] if json['angellist']
|
40
|
+
@github_username = json['github']['username'] if json['github']
|
41
|
+
@facebook_username = json['facebook']['username'] if json['facebook']
|
42
|
+
@twitter_username = json['twitter']['username'] if json['twitter']
|
43
|
+
@instagram_username = json['instagram']['username'] if json['instagram']
|
44
|
+
@linkedin_url = json['linkedin']['url'] if json['linkedin']
|
45
|
+
@bitcoin_address = json['bitcoin']['address'] if json['bitcoin']
|
46
|
+
@bitmessage_address = json['bitmessage']['address'] if json['bitmessage']
|
47
|
+
@bitcoinotc_username = json['bitcoinotc']['username'] if json['bitcoinotc']
|
48
|
+
@pgp_fingerprint = json['pgp']['fingerprint'] if json['pgp']
|
49
|
+
@pgp_url = json['pgp']['url'] if json['pgp']
|
50
|
+
@schema_version = json['v']
|
51
|
+
@orgs = parse_orgs(json['orgs'])
|
52
|
+
else
|
53
|
+
@username = username
|
54
|
+
@name_formatted = json['name'] if json['name']
|
55
|
+
@avatar_url = find_image_url(json, 'avatar')
|
56
|
+
@cover_url = find_image_url(json, 'cover')
|
57
|
+
@location_formatted = json['address']['addressLocality'] if json['address']
|
58
|
+
@website = json['website'][0]['url'] if json['website'] && json['website'][0]
|
59
|
+
@bio = json['description']
|
60
|
+
@angellist_username = find_account_username(json, 'angellist')
|
61
|
+
@github_username = find_account_username(json, 'github')
|
62
|
+
@facebook_username = find_account_username(json, 'facebook')
|
63
|
+
@twitter_username = find_account_username(json, 'twitter')
|
64
|
+
@instagram_username = find_account_username(json, 'instagram')
|
65
|
+
@linkedin_url = find_account_username(json, 'linkedin')
|
66
|
+
@bitcoin_address = find_account_username(json, 'bitcoin')
|
67
|
+
@bitmessage_address = find_account_username(json, 'bitmessage')
|
68
|
+
@bitcoinotc_username = find_account_username(json, 'bitcoinotc')
|
69
|
+
@pgp_fingerprint = find_account_username(json, 'pgp')
|
70
|
+
@pgp_url = find_account(json, 'pgp')['contentUrl'] if @pgp_fingerprint
|
71
|
+
@schema_version = '0.3'
|
72
|
+
@orgs = parse_orgs(json['orgs'])
|
76
73
|
end
|
74
|
+
end
|
77
75
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
76
|
+
def openname
|
77
|
+
warn '[DEPRECATION] `openname` is deprecated. Please use `username` instead.'
|
78
|
+
username
|
79
|
+
end
|
82
80
|
|
83
|
-
|
81
|
+
protected
|
84
82
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
end
|
83
|
+
def find_image_url(json, type)
|
84
|
+
images = json['image']
|
85
|
+
if images && images.is_a?(Array)
|
86
|
+
images.each do |image|
|
87
|
+
return image['contentUrl'] if image['name'] == type
|
91
88
|
end
|
92
|
-
nil
|
93
89
|
end
|
90
|
+
nil
|
91
|
+
end
|
94
92
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
93
|
+
def find_account(json, service)
|
94
|
+
accounts = json['account']
|
95
|
+
if accounts && accounts.is_a?(Array)
|
96
|
+
accounts.each do |account|
|
97
|
+
return account if account['service'] == service
|
101
98
|
end
|
102
|
-
nil
|
103
99
|
end
|
100
|
+
nil
|
101
|
+
end
|
104
102
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
nil
|
111
|
-
end
|
103
|
+
def find_account_username(json, service)
|
104
|
+
account = find_account(json, service)
|
105
|
+
return account['identifier'] if account
|
106
|
+
nil
|
107
|
+
end
|
112
108
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
120
|
-
orgs
|
109
|
+
def parse_orgs(orgs_json)
|
110
|
+
orgs = []
|
111
|
+
if orgs_json
|
112
|
+
orgs_json.each do |org_json|
|
113
|
+
orgs << Org.new(org_json)
|
114
|
+
end
|
121
115
|
end
|
122
|
-
|
116
|
+
orgs
|
117
|
+
end
|
123
118
|
end
|
124
119
|
end
|
data/lib/blockstack/version.rb
CHANGED
data/lib/blockstack.rb
CHANGED
@@ -1,137 +1,137 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require 'blockstack/version'
|
2
|
+
require 'blockstack/user'
|
3
|
+
require 'bitcoin'
|
4
|
+
require 'faraday'
|
5
|
+
require 'jwtb'
|
6
6
|
|
7
7
|
module Blockstack
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
decoded_token = decoded_tokens[0]
|
71
|
-
|
72
|
-
raise InvalidAuthResponse.new("Public keys don't match issuer address") unless self.public_keys_match_issuer?(decoded_token)
|
73
|
-
|
74
|
-
raise InvalidAuthResponse.new("Public keys don't match owner of claimed username") unless self.public_keys_match_username?(decoded_token)
|
75
|
-
|
76
|
-
return decoded_token
|
77
|
-
rescue JWTB::VerificationError => error
|
78
|
-
raise InvalidAuthResponse.new("Signature on JWT is invalid")
|
79
|
-
rescue JWTB::DecodeError => error
|
80
|
-
raise InvalidAuthResponse.new("Unable to decode JWT")
|
81
|
-
rescue RuntimeError => error
|
82
|
-
raise InvalidAuthResponse.new(error.message)
|
83
|
-
end
|
84
|
-
|
85
|
-
def self.get_did_type(decentralized_id)
|
86
|
-
did_parts = decentralized_id.split(':')
|
87
|
-
raise 'Decentralized IDs must have 3 parts' if did_parts.length != 3
|
88
|
-
raise 'Decentralized IDs must start with "did"' if did_parts[0].downcase != 'did'
|
89
|
-
did_parts[1].downcase
|
8
|
+
class InvalidAuthResponse < StandardError; end
|
9
|
+
|
10
|
+
USER_AGENT = "blockstack-ruby #{VERSION}"
|
11
|
+
ALGORITHM = 'ES256K'
|
12
|
+
REQUIRED_CLAIMS = %w(iss iat jti exp username profile public_keys)
|
13
|
+
|
14
|
+
DEFAULT_LEEWAY = 30 # seconds
|
15
|
+
DEFAULT_VALID_WITHIN = 30 # seconds
|
16
|
+
DEFAULT_API = 'https://core.blockstack.org'
|
17
|
+
|
18
|
+
def self.api=(api)
|
19
|
+
@api = api || DEFAULT_API
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.api
|
23
|
+
@api
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.leeway=(leeway)
|
27
|
+
@leeway = leeway || DEFAULT_LEEWAY
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.leeway
|
31
|
+
@leeway
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.valid_within=(valid_within)
|
35
|
+
@valid_within = valid_within || DEFAULT_VALID_WITHIN
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.valid_within
|
39
|
+
@valid_within
|
40
|
+
end
|
41
|
+
|
42
|
+
# decode & verify token without checking signature so we can extract
|
43
|
+
# public keys
|
44
|
+
def self.verify_without_signature(auth_token)
|
45
|
+
public_key = nil
|
46
|
+
verify = false
|
47
|
+
decoded_tokens = JWTB.decode auth_token, public_key, verify, algorithm: ALGORITHM
|
48
|
+
decoded_tokens[0]
|
49
|
+
end
|
50
|
+
|
51
|
+
# decode & verify signature
|
52
|
+
def self.verify_with_signature(auth_token)
|
53
|
+
compressed_hex_public_key = public_keys[0]
|
54
|
+
bignum = OpenSSL::BN.new(compressed_hex_public_key, 16)
|
55
|
+
group = OpenSSL::PKey::EC::Group.new 'secp256k1'
|
56
|
+
public_key = OpenSSL::PKey::EC::Point.new(group, bignum)
|
57
|
+
ecdsa_key = OpenSSL::PKey::EC.new 'secp256k1'
|
58
|
+
ecdsa_key.public_key = public_key
|
59
|
+
verify = true
|
60
|
+
|
61
|
+
decoded_tokens = JWTB.decode auth_token, ecdsa_key, verify, algorithm: ALGORITHM, exp_leeway: leeway
|
62
|
+
decoded_tokens[0]
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.verify_auth_response(auth_token)
|
66
|
+
decoded_token = verify_without_signature(auth_token)
|
67
|
+
|
68
|
+
REQUIRED_CLAIMS.each do |field|
|
69
|
+
fail InvalidAuthResponse.new("Missing required '#{field}' claim.") unless decoded_token.key?(field.to_s)
|
90
70
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
71
|
+
fail InvalidAuthResponse.new("Missing required 'iat' claim.") unless decoded_token['iat']
|
72
|
+
fail InvalidAuthResponse.new("'iat' timestamp claim is skewed too far from present.") if (Time.now.to_i - decoded_token['iat']).abs > valid_within
|
73
|
+
|
74
|
+
public_keys = decoded_token['public_keys']
|
75
|
+
fail InvalidAuthResponse.new('Invalid public_keys array: only 1 key is supported') unless public_keys.length == 1
|
76
|
+
|
77
|
+
decoded_token = verify_with_signature(auth_token)
|
78
|
+
fail InvalidAuthResponse.new("Public keys don't match issuer address") unless self.public_keys_match_issuer?(decoded_token)
|
79
|
+
fail InvalidAuthResponse.new("Public keys don't match owner of claimed username") unless self.public_keys_match_username?(decoded_token)
|
80
|
+
|
81
|
+
return decoded_token
|
82
|
+
rescue JWTB::VerificationError
|
83
|
+
raise InvalidAuthResponse.new('Signature on JWT is invalid')
|
84
|
+
rescue JWTB::DecodeError
|
85
|
+
raise InvalidAuthResponse.new('Unable to decode JWT')
|
86
|
+
rescue RuntimeError => error
|
87
|
+
raise InvalidAuthResponse.new(error.message)
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.get_did_type(decentralized_id)
|
91
|
+
did_parts = decentralized_id.split(':')
|
92
|
+
fail 'Decentralized IDs must have 3 parts' if did_parts.length != 3
|
93
|
+
fail 'Decentralized IDs must start with "did"' if did_parts[0].downcase != 'did'
|
94
|
+
did_parts[1].downcase
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.get_address_from_did(decentralized_id)
|
98
|
+
did_type = get_did_type(decentralized_id)
|
99
|
+
return nil if did_type != 'btc-addr'
|
100
|
+
decentralized_id.split(':')[2]
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.public_keys_match_issuer?(decoded_token)
|
104
|
+
public_keys = decoded_token['public_keys']
|
105
|
+
address_from_issuer = get_address_from_did(decoded_token['iss'])
|
106
|
+
|
107
|
+
fail 'Multiple public keys are not supported' unless public_keys.count == 1
|
108
|
+
|
109
|
+
address_from_public_keys = Bitcoin.pubkey_to_address(public_keys.first)
|
110
|
+
address_from_issuer == address_from_public_keys
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.public_keys_match_username?(decoded_token)
|
114
|
+
username = decoded_token['username']
|
115
|
+
return true if username.nil?
|
116
|
+
|
117
|
+
response = Faraday.get "#{api}/v1/names/#{username}"
|
118
|
+
json = JSON.parse response.body
|
119
|
+
|
120
|
+
fail "Issuer claimed username that doesn't exist" if response.status == 404
|
121
|
+
fail "Unable to verify issuer's claimed username" if response.status != 200
|
122
|
+
|
123
|
+
name_owning_address = json['address']
|
124
|
+
address_from_issuer = get_address_from_did decoded_token['iss']
|
125
|
+
name_owning_address == address_from_issuer
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.faraday
|
129
|
+
connection = Faraday.new
|
130
|
+
connection.headers[:user_agent] = USER_AGENT
|
131
|
+
connection
|
132
|
+
end
|
133
|
+
|
134
|
+
@leeway = DEFAULT_LEEWAY
|
135
|
+
@valid_within = DEFAULT_VALID_WITHIN
|
136
|
+
@api = DEFAULT_API
|
137
137
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blockstack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0.
|
4
|
+
version: 7.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Larry Salibra
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
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'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: jwtb
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|