tanker-identity 2.16.0.alpha.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 +7 -0
- data/LICENSE +13 -0
- data/README.md +133 -0
- data/lib/tanker-identity.rb +1 -0
- data/lib/tanker/crypto.rb +57 -0
- data/lib/tanker/identity.rb +125 -0
- data/lib/tanker/identity/version.rb +11 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 88d5a8e184f0271d9b431a9440eb7a358d9a3d7f4e0e88f5774b9c8601a74cb3
|
4
|
+
data.tar.gz: 501f29acdb04b8777fcff7fedabeebcb97d6be545b825958d31d985720c00d2c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 62fe18c7feea78adbbb5d52aae664d3a4a806bd34dd04130e9ba20a49bf8acab96741d8525a8be50d213ee86d0b9e8d8532374df33a101db1c5cb05910a7ab8f
|
7
|
+
data.tar.gz: 227bcb9e1e49db803afa686eb511048929a04ad5cac2da25ce54a800cf4ca3b295b0c6fe149826c92a5c544f29ed5c2ea5e1a16db9162eb74a48039da9662c15
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2019 Kontrol SAS
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
<a href="#readme"><img src="https://tanker.io/images/github-logo.png" alt="Tanker logo" width="180" /></a>
|
2
|
+
|
3
|
+
# Identity SDK
|
4
|
+
|
5
|
+
[](https://github.com/TankerHQ/identity-ruby/actions) [](https://codecov.io/gh/TankerHQ/identity-ruby)
|
6
|
+
|
7
|
+
Identity generation in Ruby for the [Tanker SDK](https://docs.tanker.io/latest/).
|
8
|
+
|
9
|
+
## Requirements
|
10
|
+
|
11
|
+
This gem requires Ruby v2.5 or greater (transitive requirement from [rbnacl](https://github.com/crypto-rb/rbnacl)).
|
12
|
+
|
13
|
+
If still on an older version of Ruby, use `tanker-identity` with the [`603b35f8` commit ref](https://github.com/TankerHQ/identity-ruby/tree/603b35f8e1ca889c4862e8f9c1e54632a38b32b6).
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
This project depends on the [rbnacl](https://github.com/crypto-rb/rbnacl) gem, which requires the [libsodium](https://download.libsodium.org/doc/) cryptographic library.
|
18
|
+
|
19
|
+
Before going further, please follow [instructions to install libsodium](https://github.com/crypto-rb/rbnacl/wiki/Installing-libsodium).
|
20
|
+
|
21
|
+
Then, add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'tanker-identity', git: 'https://github.com/TankerHQ/identity-ruby' #, ref: '<commit>'
|
25
|
+
```
|
26
|
+
|
27
|
+
Finally, execute:
|
28
|
+
|
29
|
+
```shell
|
30
|
+
bundle
|
31
|
+
```
|
32
|
+
|
33
|
+
## API
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
Tanker::Identity.create_identity(app_id, app_secret, user_id)
|
37
|
+
```
|
38
|
+
Create a new Tanker identity. This identity is secret and must only be given to a user who has been authenticated by your application. This identity is used by the Tanker client SDK to open a Tanker session.
|
39
|
+
|
40
|
+
**app_id**<br>
|
41
|
+
The app ID. You can access it from the [Tanker dashboard](https://dashboard.tanker.io).
|
42
|
+
|
43
|
+
**app_secret**<br>
|
44
|
+
The app secret. A secret that you have saved right after the creation of your app on the [Tanker dashboard](https://dashboard.tanker.io).
|
45
|
+
|
46
|
+
**user_id**<br>
|
47
|
+
The unique ID of a user in your application.
|
48
|
+
<br><br>
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
Tanker::Identity.create_provisional_identity(app_id, email)
|
52
|
+
```
|
53
|
+
Create a Tanker provisional identity. It allows you to share a resource with a user who does not have an account in your application yet.
|
54
|
+
|
55
|
+
**app_id**<br>
|
56
|
+
The app ID. You can access it from the [Tanker dashboard](https://dashboard.tanker.io).
|
57
|
+
|
58
|
+
**email**<br>
|
59
|
+
The email of the potential recipient of the resource.
|
60
|
+
<br><br>
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
Tanker::Identity.get_public_identity(identity)
|
64
|
+
```
|
65
|
+
Return the public identity from an identity. This public identity can be used by the Tanker client SDK to share encrypted resource.
|
66
|
+
|
67
|
+
**identity**<br>
|
68
|
+
A secret identity.
|
69
|
+
<br><br>
|
70
|
+
|
71
|
+
## Usage example
|
72
|
+
|
73
|
+
The server-side pseudo-code below demonstrates a typical flow to safely deliver identities to your users:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
require 'tanker-identity'
|
77
|
+
|
78
|
+
# 1. store these configurations in a safe place
|
79
|
+
app_id = '<app-id>'
|
80
|
+
app_secret = '<app-secret>'
|
81
|
+
|
82
|
+
# 2. you will typically have methods to check user authentication
|
83
|
+
def authenticated? # check user is authenticated on the server
|
84
|
+
def current_user # current authenticated user
|
85
|
+
|
86
|
+
# 3. you will need to add internal methods to store / load identities
|
87
|
+
def db_store_identity(user_id, identity)
|
88
|
+
def db_load_identity(user_id)
|
89
|
+
|
90
|
+
# 4. finally, add user facing functionality
|
91
|
+
def tanker_secret_identity(user_id)
|
92
|
+
raise 'Not authenticated' unless authenticated?
|
93
|
+
raise 'Not authorized' unless current_user.id == user_id
|
94
|
+
|
95
|
+
identity = db_load_identity(user_id)
|
96
|
+
|
97
|
+
if identity.nil?
|
98
|
+
identity = Tanker::Identity.create_identity(app_id, app_secret, user_id)
|
99
|
+
db_store_identity(user_id, identity)
|
100
|
+
end
|
101
|
+
|
102
|
+
identity
|
103
|
+
end
|
104
|
+
|
105
|
+
def tanker_public_identity(user_id)
|
106
|
+
raise 'Not authenticated' unless authenticated?
|
107
|
+
|
108
|
+
identity = db_load_identity(user_id)
|
109
|
+
|
110
|
+
raise 'User does not exist or has no identity yet' unless identity
|
111
|
+
|
112
|
+
Tanker::Identity.get_public_identity(identity)
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
Read more about identities in the [Tanker documentation](https://docs.tanker.io/latest/).
|
117
|
+
|
118
|
+
Check the [examples](https://github.com/TankerHQ/identity-ruby/tree/master/examples/) folder for usage examples.
|
119
|
+
|
120
|
+
## Development
|
121
|
+
|
122
|
+
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.
|
123
|
+
|
124
|
+
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).
|
125
|
+
|
126
|
+
To audit the Gemfile.lock against the [advisory database](https://rubysec.com/), run `bundle exec bundle-audit check --update`.
|
127
|
+
|
128
|
+
## Contributing
|
129
|
+
|
130
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/TankerHQ/identity-ruby.
|
131
|
+
|
132
|
+
[build-badge]: https://travis-ci.org/TankerHQ/identity-ruby.svg?branch=master
|
133
|
+
[build]: https://travis-ci.org/TankerHQ/identity-ruby
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'tanker/identity'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'rbnacl'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Tanker
|
6
|
+
module Crypto
|
7
|
+
class InvalidSignature < StandardError; end
|
8
|
+
|
9
|
+
HASH_MIN_SIZE = RbNaCl::Hash::Blake2b::BYTES_MIN # 16
|
10
|
+
|
11
|
+
def self.generichash(input, size)
|
12
|
+
binary_input = input.dup.force_encoding(Encoding::ASCII_8BIT)
|
13
|
+
RbNaCl::Hash.blake2b(binary_input, digest_size: size)
|
14
|
+
end
|
15
|
+
|
16
|
+
# We need this static method since a RbNaCl::SigningKey instance can't be
|
17
|
+
# directly initialized with a given private_signature_key
|
18
|
+
def self.sign_detached(message, private_signature_key)
|
19
|
+
signature_bytes = RbNaCl::SigningKey.signature_bytes
|
20
|
+
buffer = RbNaCl::Util.prepend_zeros(signature_bytes, message)
|
21
|
+
buffer_len = RbNaCl::Util.zeros(8) # 8 bytes for an int64 (FFI::Type::LONG_LONG.size)
|
22
|
+
|
23
|
+
RbNaCl::SigningKey.sign_ed25519(buffer, buffer_len, message, message.bytesize, private_signature_key)
|
24
|
+
|
25
|
+
buffer[0, signature_bytes]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.verify_sign_detached(message, signature, public_signature_key)
|
29
|
+
verify_key = RbNaCl::VerifyKey.new(public_signature_key)
|
30
|
+
verify_key.verify(signature, message)
|
31
|
+
rescue RbNaCl::BadSignatureError
|
32
|
+
raise InvalidSignature.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.generate_signature_keypair
|
36
|
+
signing_key = RbNaCl::SigningKey.generate
|
37
|
+
|
38
|
+
{
|
39
|
+
private_key: signing_key.keypair_bytes,
|
40
|
+
public_key: signing_key.verify_key.to_bytes
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.generate_encryption_keypair
|
45
|
+
encryption_key = RbNaCl::PrivateKey.generate
|
46
|
+
|
47
|
+
{
|
48
|
+
private_key: encryption_key.to_bytes,
|
49
|
+
public_key: encryption_key.public_key.to_bytes
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.random_bytes(size)
|
54
|
+
SecureRandom.bytes(size)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'tanker/crypto'
|
2
|
+
require 'tanker/identity/version'
|
3
|
+
require 'base64'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Tanker
|
7
|
+
module Identity
|
8
|
+
APP_CREATION_NATURE = 1
|
9
|
+
APP_SECRET_SIZE = 64
|
10
|
+
APP_PUBLIC_KEY_SIZE = 32
|
11
|
+
AUTHOR_SIZE = 32
|
12
|
+
BLOCK_HASH_SIZE = 32
|
13
|
+
USER_SECRET_SIZE = 32
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.hash_user_id(app_id, user_id)
|
18
|
+
binary_user_id = user_id.dup.force_encoding(Encoding::ASCII_8BIT)
|
19
|
+
Crypto.generichash(binary_user_id + app_id, BLOCK_HASH_SIZE)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.user_secret(hashed_user_id)
|
23
|
+
random_bytes = Crypto.random_bytes(USER_SECRET_SIZE - 1)
|
24
|
+
check = Crypto.generichash(random_bytes + hashed_user_id, Crypto::HASH_MIN_SIZE)
|
25
|
+
random_bytes + check[0]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.assert_string_values(hash)
|
29
|
+
hash.each_pair do |key, value|
|
30
|
+
unless value.is_a?(String)
|
31
|
+
raise TypeError.new("expected #{key} to be a String but was a #{value.class}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.generate_app_id(app_secret)
|
37
|
+
block_nature = APP_CREATION_NATURE.chr(Encoding::ASCII_8BIT)
|
38
|
+
none_author = 0.chr(Encoding::ASCII_8BIT) * AUTHOR_SIZE
|
39
|
+
app_public_key = app_secret[-APP_PUBLIC_KEY_SIZE..-1]
|
40
|
+
Crypto.generichash(block_nature + none_author + app_public_key, BLOCK_HASH_SIZE)
|
41
|
+
end
|
42
|
+
|
43
|
+
public
|
44
|
+
|
45
|
+
def self.deserialize(b64_json)
|
46
|
+
JSON.parse(Base64.strict_decode64(b64_json))
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.serialize(hash)
|
50
|
+
Base64.strict_encode64(JSON.generate(hash))
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.create_identity(b64_app_id, b64_app_secret, user_id)
|
54
|
+
assert_string_values({
|
55
|
+
app_id: b64_app_id,
|
56
|
+
app_secret: b64_app_secret,
|
57
|
+
user_id: user_id
|
58
|
+
})
|
59
|
+
|
60
|
+
app_id = Base64.strict_decode64(b64_app_id)
|
61
|
+
app_secret = Base64.strict_decode64(b64_app_secret)
|
62
|
+
|
63
|
+
raise ArgumentError.new("Invalid app_id") if app_id.bytesize != BLOCK_HASH_SIZE
|
64
|
+
raise ArgumentError.new("Invalid app_secret") if app_secret.bytesize != APP_SECRET_SIZE
|
65
|
+
raise ArgumentError.new("Invalid (app_id, app_secret) combination") if app_id != generate_app_id(app_secret)
|
66
|
+
|
67
|
+
hashed_user_id = hash_user_id(app_id, user_id)
|
68
|
+
signature_keypair = Crypto.generate_signature_keypair
|
69
|
+
message = signature_keypair[:public_key] + hashed_user_id
|
70
|
+
signature = Crypto.sign_detached(message, app_secret)
|
71
|
+
|
72
|
+
serialize({
|
73
|
+
trustchain_id: Base64.strict_encode64(app_id),
|
74
|
+
target: 'user',
|
75
|
+
value: Base64.strict_encode64(hashed_user_id),
|
76
|
+
delegation_signature: Base64.strict_encode64(signature),
|
77
|
+
ephemeral_public_signature_key: Base64.strict_encode64(signature_keypair[:public_key]),
|
78
|
+
ephemeral_private_signature_key: Base64.strict_encode64(signature_keypair[:private_key]),
|
79
|
+
user_secret: Base64.strict_encode64(user_secret(hashed_user_id))
|
80
|
+
})
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.create_provisional_identity(b64_app_id, email)
|
84
|
+
assert_string_values({
|
85
|
+
app_id: b64_app_id,
|
86
|
+
email: email
|
87
|
+
})
|
88
|
+
|
89
|
+
app_id = Base64.strict_decode64(b64_app_id)
|
90
|
+
raise ArgumentError.new("Invalid app_id") if app_id.bytesize != BLOCK_HASH_SIZE
|
91
|
+
|
92
|
+
encryption_keypair = Crypto.generate_encryption_keypair
|
93
|
+
signature_keypair = Crypto.generate_signature_keypair
|
94
|
+
|
95
|
+
serialize({
|
96
|
+
trustchain_id: b64_app_id,
|
97
|
+
target: 'email',
|
98
|
+
value: email,
|
99
|
+
public_encryption_key: Base64.strict_encode64(encryption_keypair[:public_key]),
|
100
|
+
private_encryption_key: Base64.strict_encode64(encryption_keypair[:private_key]),
|
101
|
+
public_signature_key: Base64.strict_encode64(signature_keypair[:public_key]),
|
102
|
+
private_signature_key: Base64.strict_encode64(signature_keypair[:private_key])
|
103
|
+
})
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.get_public_identity(serialized_identity)
|
107
|
+
assert_string_values({ identity: serialized_identity })
|
108
|
+
|
109
|
+
identity = deserialize(serialized_identity)
|
110
|
+
|
111
|
+
if identity['target'] == 'user'
|
112
|
+
public_keys = ['trustchain_id', 'target', 'value']
|
113
|
+
else
|
114
|
+
public_keys = ['trustchain_id', 'target', 'value', 'public_encryption_key', 'public_signature_key']
|
115
|
+
end
|
116
|
+
|
117
|
+
public_identity = {}
|
118
|
+
public_keys.each { |key| public_identity[key] = identity.fetch(key) }
|
119
|
+
|
120
|
+
serialize(public_identity)
|
121
|
+
rescue KeyError # failed fetch
|
122
|
+
raise ArgumentError.new('Not a valid Tanker identity')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Tanker
|
2
|
+
module Identity
|
3
|
+
VERSION = '2.16.0.alpha.1'
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.const_missing(name)
|
7
|
+
super unless name == :VERSION
|
8
|
+
warn "DEPRECATION WARNING: Using `Tanker::VERSION` is deprecated in favor of `Tanker::Identity::VERSION`"
|
9
|
+
Tanker::Identity::VERSION
|
10
|
+
end
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tanker-identity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.16.0.alpha.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tanker Team
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-06-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rbnacl
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler-audit
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.7'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '13.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '13.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubygems-tasks
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.2.5
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.2.5
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
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: codecov
|
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
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Building blocks to add Tanker identity management to your application
|
126
|
+
server
|
127
|
+
email:
|
128
|
+
- contact@tanker.io
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- LICENSE
|
134
|
+
- README.md
|
135
|
+
- lib/tanker-identity.rb
|
136
|
+
- lib/tanker/crypto.rb
|
137
|
+
- lib/tanker/identity.rb
|
138
|
+
- lib/tanker/identity/version.rb
|
139
|
+
homepage: https://tanker.io
|
140
|
+
licenses:
|
141
|
+
- Apache-2.0
|
142
|
+
metadata:
|
143
|
+
homepage_uri: https://tanker.io
|
144
|
+
source_code_uri: https://github.com/TankerHQ/identity-ruby
|
145
|
+
changelog_uri: https://docs.tanker.io/latest/release-notes/identity/ruby
|
146
|
+
post_install_message:
|
147
|
+
rdoc_options: []
|
148
|
+
require_paths:
|
149
|
+
- lib
|
150
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: 2.5.0
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 1.3.1
|
160
|
+
requirements: []
|
161
|
+
rubygems_version: 3.0.3
|
162
|
+
signing_key:
|
163
|
+
specification_version: 4
|
164
|
+
summary: Tanker identity management library packaged as a gem
|
165
|
+
test_files: []
|