goinstant-auth 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e57579ef9a598241a353985a3e2196c9356595a8
4
+ data.tar.gz: 791f93734b346080b6cef9b2f2fb96f89c167de2
5
+ SHA512:
6
+ metadata.gz: 93cbae429b8bf7f8404159209c383454c998b04e21aa617ce33b816191b7fd6a051369f172cdde902b066e6f1f3668ce5fdec242042faf0bb394438398962ac1
7
+ data.tar.gz: 948cbffa95ab09c084b18616928aa4b4e8ec83ff5f3164fbc8590e9c53da0ed81b69fa4ec36597374d8f44c0df3830ecfc14559e8aa697a08d182bfcb203fb9d
data/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2013, GoInstant Inc., a salesforce.com company
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice, this
11
+ list of conditions and the following disclaimer in the documentation and/or
12
+ other materials provided with the distribution.
13
+
14
+ * Neither the name of GoInstant Inc., a salesforce.com company, nor the names
15
+ of its contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # ruby-goinstant-auth
2
+
3
+ GoInstant Authentication for Your Ruby Application
4
+
5
+ [![Build Status](https://magnum.travis-ci.com/goinstant/ruby-goinstant-auth.png?token=fy6GC4GtQkNjSzNF3geU&branch=master)](https://magnum.travis-ci.com/goinstant/ruby-goinstant-auth)
6
+
7
+ This is an implementation of JWT tokens consistent with what's specified in the
8
+ [GoInstant Users and Authentication
9
+ Guide](https://developers.goinstant.com/v1/guides/users_and_authentication.html).
10
+
11
+ This library is not intended as a general-use JWT library. For a more general
12
+ library, see [progrium/ruby-jwt](https://github.com/progrium/ruby-jwt) (but
13
+ please note its supported version).
14
+ At the time of this writing, GoInstant supports the [JWT IETF draft
15
+ version 08](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-08).
16
+
17
+ # Installation
18
+
19
+ ```sh
20
+ gem install goinstant-auth
21
+ ```
22
+
23
+ # Usage
24
+
25
+ Construct a signer with your goinstant application key. The application key
26
+ should be in base64url or base64 string format. To get your key, go to [your
27
+ goinstant dashboard](https://goinstant.com/dashboard) and click on your App.
28
+
29
+ :warning: **Remember, the Secret Key needs to be treated like a password!**
30
+ Never share it with your users!
31
+
32
+ ```ruby
33
+ require 'goinstant/auth'
34
+
35
+ signer = GoInstant::Auth::Signer.new(secret_key)
36
+ ```
37
+
38
+ You can then use this `signer` to create as many tokens as you want. The
39
+ `:domain` parameter should be replaced with your website's domain. Groups are
40
+ optional.
41
+
42
+ ```ruby
43
+ jwt = signer.sign({
44
+ :domain => 'example.com', # TODO: replace me
45
+ :id => user.id,
46
+ :display_name => user.full_name,
47
+ :groups => [
48
+ {
49
+ :id => 'room-42',
50
+ :display_name => 'Room 42 ACL Group'
51
+ }
52
+ ]
53
+ })
54
+ ```
55
+
56
+ This token can be safely inlined into an ERB template. For example, a fairly
57
+ basic template for calling [`goinstant.connect`
58
+ call](https://developers.goinstant.com/v1/javascript_api/connect.html) might
59
+ look like this:
60
+
61
+ ```html
62
+ <script type="text/javascript">
63
+ (function() {
64
+ var token = "<%= token %>";
65
+ var url = 'https://goinstant.net/YOURACCOUNT/YOURAPP'
66
+
67
+ var opts = {
68
+ user: token,
69
+ rooms: [ 'room-42' ]
70
+ };
71
+
72
+ goinstant.connect(url, opts, function(err, conn, room) {
73
+ if (err) {
74
+ throw err;
75
+ }
76
+ runYourApp(room);
77
+ });
78
+ }());
79
+ </script>
80
+ ```
81
+
82
+ # Methods
83
+
84
+ ## `GoInstant::Auth::Signer`
85
+
86
+ Re-usable object for creating user tokens.
87
+
88
+ ### `.new(secret_key)`
89
+
90
+ Constructs a `Signer` object from a base64url or base64 secret key string.
91
+
92
+ Throws an Error if the `secretKey` could not be parsed.
93
+
94
+ ### `#sign(user_data, extra_headers={})`
95
+
96
+ Creates a JWT as a JWS in Compact Serialization format. Can be called multiple
97
+ times on the same object, saving you from having to load your secret GoInstant
98
+ application key every time.
99
+
100
+ `user_data` is a Hash with the following required fields, plus any other
101
+ custom ones you want to include in the JWT.
102
+
103
+ - `:domain` - the domain of your website
104
+ - `:id` - the unique, permanent identity of this user on your website
105
+ - `:display_name` - the name to initially display for this user
106
+ - `:groups` - an array of groups, each group requiring:
107
+ - `:id` - the unique ID of this group, which is handy for defining [GoInstant ACLs](https://developers.goinstant.com/v1/guides/creating_and_managing_acl.html)
108
+ - `:display_name` - the name to display for this group
109
+
110
+ `extra_headers` is completely optional. It's used to define any additional
111
+ [JWS header fields](http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-11#section-4.1)
112
+ that you want to include.
113
+
114
+ # Technicals
115
+
116
+ The `sign()` method's `user_data` maps to the following JWT claims.
117
+ The authoritative list of claims used in GoInstant can be found in the [Users and Authentication Guide](https://developers.goinstant.com/v1/guides/users_and_authentication.html#which-reserved-claims-are-required).
118
+
119
+ - `:domain` -> `iss` (standard claim)
120
+ - `:id` -> `sub` (standard claim)
121
+ - `:display_name` -> `dn` (GoInstant private claim)
122
+ - `:groups` -> `g` (GoInstant private claim)
123
+ - `:id` -> `id` (GoInstant private claim)
124
+ - `:display_name` -> `dn` (GoInstant private claim)
125
+ - `'goinstant.net'` -> `aud` (standard claim) _automatically added_
126
+
127
+ For the `extra_headers` parameter in `sign()`, the `alg` and `typ` headers will
128
+ be overridden by this library.
129
+
130
+ # Contributing
131
+
132
+ ## Development Dependencies
133
+
134
+ Base dependencies are as follows.
135
+
136
+ - [ruby](https://www.ruby-lang.org/en/downloads/) >= 1.9.2
137
+ - [rubygems](https://rubygems.org/pages/download) >= 2.1
138
+
139
+ The following gems should then be installed via `gem install`:
140
+
141
+ - [bundler](http://bundler.io/) >= 1.5
142
+ - [rake](http://rake.rubyforge.org/) >= 10.1
143
+
144
+ ## Set-up
145
+
146
+ ```sh
147
+ git clone git@github.com:goinstant/ruby-goinstant-auth.git
148
+ cd ruby-goinstant-auth
149
+
150
+ # if you haven't already:
151
+ gem install bundler
152
+ gem install rake
153
+
154
+ # then, install dev dependencies:
155
+ bundle install
156
+ ```
157
+
158
+ ## Testing
159
+
160
+ Testing is done with RSpec. Tests are located in the `spec/` folder.
161
+
162
+ ```sh
163
+ rake # 'test' is the default
164
+ ```
165
+
166
+ ## Developer Documentation
167
+
168
+ You can view the documentation generated by `yard` easily:
169
+
170
+ ```sh
171
+ gem install yard
172
+ rake doc
173
+ open doc/frames.html
174
+ ```
175
+
176
+ # Support
177
+
178
+ Email [GoInstant Support](mailto:support@goinstant.com) or stop by [#goinstant
179
+ on freenode](irc://irc.freenode.net/#goinstant).
180
+
181
+ For responsible disclosures, email [GoInstant Security](mailto:security@goinstant.com).
182
+
183
+ To [file a bug](https://github.com/goinstant/node-goinstant-auth/issues) or
184
+ [propose a patch](https://github.com/goinstant/node-goinstant-auth/pulls),
185
+ please use github directly.
186
+
187
+ # Legal
188
+
189
+ &copy; 2013 GoInstant Inc., a salesforce.com company. All Rights Reserved.
190
+
191
+ Licensed under the 3-clause BSD license
@@ -0,0 +1,51 @@
1
+ require 'json'
2
+ require 'base64'
3
+ require_relative 'auth/signer'
4
+
5
+ # GoInstant Ruby Modules
6
+ module GoInstant
7
+
8
+ # Authentication classes and functions for use with GoInstant
9
+ module Auth
10
+
11
+ # Pads base64 strings.
12
+ # @param str [String]
13
+ # @return [String] padded (extra '=' added to multiple of 4).
14
+ def self.pad64(str)
15
+ rem = str.size % 4
16
+ if rem > 0 then
17
+ str << ("=" * (4-rem))
18
+ end
19
+ return str
20
+ end
21
+
22
+ # Encodes base64 and base64url encoded strings.
23
+ # @param str [String]
24
+ # @return [String]
25
+ def self.encode64(str)
26
+ return Base64.urlsafe_encode64(str).sub(/=+$/,'')
27
+ end
28
+
29
+ # Decodes base64 and base64url encoded strings.
30
+ # @param str [String]
31
+ # @return [String]
32
+ def self.decode64(str)
33
+ str = str.gsub(/\s+/,'').tr('-_','+/').sub(/=+$/,'')
34
+ return Base64.decode64(pad64(str))
35
+ end
36
+
37
+ # Decode the Compact Serialization of a thing.
38
+ # @param str [String]
39
+ # @return [Hash|String|Object]
40
+ def self.compact_decode(str)
41
+ return JSON.parse(decode64(str))
42
+ end
43
+
44
+ # Create a Compact Serialization of a thing.
45
+ # @param thing [Hash|String|Object] anything, passed to JSON.generate.
46
+ # @return [String]
47
+ def self.compact_encode(thing)
48
+ return encode64(JSON.generate(thing))
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,140 @@
1
+ #encoding: UTF-8
2
+ require 'openssl'
3
+
4
+ module GoInstant
5
+ module Auth
6
+
7
+ # Thrown when a Signer has problems with its input.
8
+ class SignerError < StandardError
9
+ end
10
+
11
+ # Creates JWTs from user-hashes, signing with your GoInstant app secret key.
12
+ class Signer
13
+
14
+ # Create a Signer with a particular key.
15
+ #
16
+ # A single Signer can be used to create multiple tokens.
17
+ #
18
+ # @param secret_key [String] A base64 or base64url format string
19
+ # representing the secret key for your GoInstant App.
20
+ #
21
+ # @raise [TypeError] when the key isn't in base64/base64url format.
22
+ # @raise [StandardError] when the key is too short
23
+ #
24
+ def initialize(secret_key)
25
+ if secret_key.nil? then
26
+ raise TypeError.new('Signer requires key in base64url or base64 format')
27
+ end
28
+
29
+ @binary_key = Auth.decode64(secret_key)
30
+ if !@binary_key or @binary_key == '' then
31
+ raise TypeError.new('Signer requires key in base64url or base64 format')
32
+ end
33
+
34
+ if @binary_key.size < 32 then
35
+ raise StandardError.new(
36
+ 'expected key length >= 32 bytes, got %d bytes' % @binary_key.size
37
+ )
38
+ end
39
+ end
40
+
41
+ # Create and sign a token for a user.
42
+ #
43
+ # @param user_data [Hash] properties about this user.
44
+ # See README.md for a complete list of options.
45
+ # @param extra_headers [Hash] Optional, additional JWT headers to include.
46
+ #
47
+ # @raise [SignerError] if a required user or group claim is missing
48
+ #
49
+ # @return [String] a JWS Compact Serialization format-string representing this user.
50
+ #
51
+ def sign(user_data, extra_headers={})
52
+ if !user_data.is_a?(Hash) then
53
+ raise SignerError.new('Signer#sign() requires a user_data Hash')
54
+ end
55
+ claims = user_data.clone
56
+ Signer.map_required_claims(claims, REQUIRED_CLAIMS)
57
+ Signer.map_optional_claims(claims, OPTIONAL_CLAIMS)
58
+ claims[:aud] = 'goinstant.net'
59
+
60
+ if claims.has_key?(:g) then
61
+ groups = claims[:g]
62
+ if !groups.is_a?(Array) then
63
+ raise SignerError.new('groups must be an Array')
64
+ end
65
+ i = 0
66
+ claims[:g] = groups.map do |group|
67
+ group = group.clone
68
+ msg = "group #{i} missing required key: %s"
69
+ i += 1
70
+ Signer.map_required_claims(group, REQUIRED_GROUP_CLAIMS, msg)
71
+ end
72
+ else
73
+ claims[:g] = []
74
+ end
75
+
76
+ headers = extra_headers.clone
77
+ headers[:typ] = 'JWT'
78
+ headers[:alg] = 'HS256'
79
+
80
+ signing_input = '%s.%s' % [headers, claims].map{ |x| Auth.compact_encode(x) }
81
+ sig = OpenSSL::HMAC::digest('SHA256', @binary_key, signing_input)
82
+ return '%s.%s' % [ signing_input, Auth.encode64(sig) ]
83
+ end
84
+
85
+ private
86
+
87
+ # Required user_data properties and their corresponding JWT claim name
88
+ #
89
+ REQUIRED_CLAIMS = {
90
+ :domain => :iss,
91
+ :id => :sub,
92
+ :display_name => :dn
93
+ }
94
+
95
+ # Optional user_data properties and their corresponding JWT claim name
96
+ #
97
+ OPTIONAL_CLAIMS = {
98
+ :groups => :g
99
+ }
100
+
101
+ # Required group properties and their corresponding JWT claim name
102
+ #
103
+ REQUIRED_GROUP_CLAIMS = {
104
+ :id => :id,
105
+ :display_name => :dn
106
+ }
107
+
108
+ # Maps required claims, mutating in-place (caller should clone).
109
+ #
110
+ # @raise [SignerError] if a required claim is missing
111
+ # @param claims [Hash] modified to use JWT claim names
112
+ # @param table [Hash] conversion table of required keys
113
+ # @param msg [String] message format for the SignerError
114
+ def self.map_required_claims(claims, table, msg="missing required key: %s")
115
+ table.each do |name,claimName|
116
+ if !claims.has_key?(name) then
117
+ raise SignerError.new(msg % name)
118
+ end
119
+ claims[claimName] = claims.delete(name)
120
+ end
121
+ return claims
122
+ end
123
+
124
+ # Maps optional claims, mutating in-place (caller should clone).
125
+ #
126
+ # @param claims [Hash] modified to use JWT claim names
127
+ # @param table [Hash] conversion table of optional keys
128
+ def self.map_optional_claims(claims, table)
129
+ table.each do |name,claimName|
130
+ if claims.has_key?(name) then
131
+ claims[claimName] = claims.delete(name)
132
+ end
133
+ end
134
+ return claims
135
+ end
136
+
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,188 @@
1
+ require 'spec_helper'
2
+ require 'rspec'
3
+
4
+ describe GoInstant::Auth::Signer do
5
+ it "doesn't accept nil" do
6
+ expect {
7
+ GoInstant::Auth::Signer.new(nil)
8
+ }.to raise_error(TypeError, 'Signer requires key in base64url or base64 format')
9
+ end
10
+
11
+ it "needs base64" do
12
+ expect {
13
+ GoInstant::Auth::Signer.new('!@#$%^&*()')
14
+ }.to raise_error(TypeError, 'Signer requires key in base64url or base64 format')
15
+ end
16
+
17
+ it "decodes base64url" do
18
+ signer = GoInstant::Auth::Signer.new('HKYdFdnezle2yrI2_Ph3cHz144bISk-cvuAbeAAA999')
19
+ signer.should_not == nil
20
+ end
21
+
22
+ it "decodes base64" do
23
+ signer = GoInstant::Auth::Signer.new('HKYdFdnezle2yrI2/Ph3cHz144bISk+cvuAbeAAA999')
24
+ signer.should_not == nil
25
+ end
26
+
27
+ it "handles base64 with padding too" do
28
+ signer = GoInstant::Auth::Signer.new('HKYdFdnezle2yrI2/Ph3cHz144bISk+cvuAbeAAA999=')
29
+ signer.should_not == nil
30
+ end
31
+
32
+ it "can compact serialization encode" do
33
+ str = GoInstant::Auth.compact_encode({ :asdf => 42 })
34
+ str.should == 'eyJhc2RmIjo0Mn0' # note: no padding
35
+ decoded = GoInstant::Auth.compact_decode(str)
36
+ expect(decoded).to be_a_kind_of(Hash)
37
+ end
38
+
39
+ it "complains if too short" do
40
+ expect {
41
+ GoInstant::Auth::Signer.new('abcd')
42
+ }.to raise_error(StandardError, 'expected key length >= 32 bytes, got 3 bytes')
43
+ end
44
+
45
+ def validate_jwt(jwt, expect_claims, expect_sig)
46
+ parts = jwt.split(/\./)
47
+ parts.size.should == 3
48
+
49
+ header = GoInstant::Auth.compact_decode(parts[0])
50
+ header['typ'].should == 'JWT'
51
+ header['alg'].should == 'HS256'
52
+
53
+ claims = GoInstant::Auth.compact_decode(parts[1])
54
+ expect(claims).to eql(expect_claims)
55
+
56
+ sig = parts[2]
57
+ sig.size.should == 43
58
+ sig.should == expect_sig
59
+ end
60
+
61
+ context "with valid key," do
62
+ before do
63
+ secret_key = 'HKYdFdnezle2yrI2_Ph3cHz144bISk-cvuAbeAAA999'
64
+ @signer = GoInstant::Auth::Signer.new(secret_key)
65
+ end
66
+
67
+ it "won't accept string user_data" do
68
+ expect {
69
+ @signer.sign('asdfasdf')
70
+ }.to raise_error(GoInstant::Auth::SignerError, 'Signer#sign() requires a user_data Hash')
71
+ end
72
+
73
+ it "needs user_data to have an id" do
74
+ user_data = {
75
+ :domain => 'example.com',
76
+ :display_name => 'bob'
77
+ }
78
+ expect {
79
+ @signer.sign(user_data)
80
+ }.to raise_error(GoInstant::Auth::SignerError, 'missing required key: id')
81
+ end
82
+
83
+ it "needs user_data to have a display_name" do
84
+ user_data = {
85
+ :id => 'bar',
86
+ :domain => 'example.com'
87
+ }
88
+ expect {
89
+ @signer.sign(user_data)
90
+ }.to raise_error(GoInstant::Auth::SignerError, 'missing required key: display_name')
91
+ end
92
+
93
+ it "needs user_data to have a domain" do
94
+ user_data = {
95
+ :id => 'bar',
96
+ :display_name => 'bob'
97
+ }
98
+ expect {
99
+ @signer.sign(user_data)
100
+ }.to raise_error(GoInstant::Auth::SignerError, 'missing required key: domain')
101
+ end
102
+
103
+ it "checks that groups is an array, if present" do
104
+ user_data = {
105
+ :id => 'bar',
106
+ :domain => 'example.com',
107
+ :display_name => 'bob',
108
+ :groups => 'nope'
109
+ }
110
+ expect {
111
+ @signer.sign(user_data)
112
+ }.to raise_error(GoInstant::Auth::SignerError, 'groups must be an Array')
113
+ end
114
+
115
+ it "happily signs without groups" do
116
+ user_data = {
117
+ :id => 'bar',
118
+ :domain => 'example.com',
119
+ :display_name => 'bob'
120
+ }
121
+
122
+ jwt = @signer.sign(user_data)
123
+ validate_jwt(jwt, {
124
+ 'aud' => 'goinstant.net',
125
+ 'sub' => 'bar',
126
+ 'iss' => 'example.com',
127
+ 'dn' => 'bob',
128
+ 'g' => []
129
+ }, 'GtNNsSjgB4ubwW4aFQlgT2E1F8UO7VMxf7ppXmBRlGc')
130
+ end
131
+
132
+ it 'needs groups to have an id' do
133
+ user_data = {
134
+ :id => 'bar',
135
+ :domain => 'example.com',
136
+ :display_name => 'bob',
137
+ :groups => [
138
+ { :display_name => 'MyGroup' }
139
+ ]
140
+ }
141
+
142
+ expect {
143
+ @signer.sign(user_data)
144
+ }.to raise_error(GoInstant::Auth::SignerError, 'group 0 missing required key: id')
145
+ end
146
+
147
+ it 'needs groups to have a display_name' do
148
+ user_data = {
149
+ :id => 'bar',
150
+ :domain => 'example.com',
151
+ :display_name => 'bob',
152
+ :groups => [
153
+ { :id => 99, :display_name => 'Gretzky Lovers' },
154
+ { :id => 1234 }
155
+ ]
156
+ }
157
+
158
+ expect {
159
+ @signer.sign(user_data)
160
+ }.to raise_error(GoInstant::Auth::SignerError, 'group 1 missing required key: display_name')
161
+ end
162
+
163
+ it 'happily signs with groups' do
164
+ user_data = {
165
+ :id => 'bar',
166
+ :domain => 'example.com',
167
+ :display_name => 'bob',
168
+ :groups => [
169
+ { :id => 1234, :display_name => 'Group 1234' },
170
+ { :id => 42, :display_name => 'Meaning Group' }
171
+ ]
172
+ }
173
+
174
+ jwt = @signer.sign(user_data)
175
+ validate_jwt(jwt, {
176
+ 'aud' => 'goinstant.net',
177
+ 'sub' => 'bar',
178
+ 'iss' => 'example.com',
179
+ 'dn' => 'bob',
180
+ 'g' => [
181
+ { 'id' => 1234, 'dn' => 'Group 1234' },
182
+ { 'id' => 42, 'dn' => 'Meaning Group' }
183
+ ]
184
+ }, '5isd3i1A4so7MwKm0VHWYHuWRy3WwGFipO0kkelNRLU')
185
+ end
186
+ end
187
+ end
188
+
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+
3
+ require 'bundler'
4
+ Bundler.setup(:default, :test)
5
+
6
+ require 'goinstant/auth'
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: goinstant-auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - GoInstant Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Lets your users log-in to your GoInstant app
14
+ email: support@goinstant.org
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/goinstant/auth/signer.rb
20
+ - lib/goinstant/auth.rb
21
+ - spec/goinstant/auth/signer_spec.rb
22
+ - spec/spec_helper.rb
23
+ - LICENSE
24
+ - README.md
25
+ homepage: https://github.com/goinstant/ruby-goinstant-auth
26
+ licenses:
27
+ - BSD-3-Clause
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.0.3
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: GoInstant Authentication for Your Ruby Application
49
+ test_files: []
50
+ has_rdoc: