monk-id 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.
- data/.gitignore +17 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/README.md +40 -0
- data/Rakefile +1 -0
- data/config/monk_id.sample.yml +11 -0
- data/lib/monk/id.rb +186 -0
- data/lib/monk/id/version.rb +6 -0
- data/monk-id.gemspec +24 -0
- metadata +106 -0
data/.gitignore
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
lib/**/*.rb
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
Monk ID Ruby
|
2
|
+
============
|
3
|
+
|
4
|
+
Full API docs: http://monkdev.github.io/monk-id-ruby/Monk/Id.html
|
5
|
+
|
6
|
+
Add to your `Gemfile`:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
gem 'monk-id', :github => 'MonkDev/monk-id-ruby', :branch => 'next'
|
10
|
+
```
|
11
|
+
|
12
|
+
For Rails and Sinatra, copy `config/monk_id.sample.yml` in this repository to
|
13
|
+
`config/monkid.yml` in your app. This will be loaded automatically. All other
|
14
|
+
apps need to load their config explicitly:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
Monk::Id.load_config('/path/to/monk_id.yml', 'development')
|
18
|
+
```
|
19
|
+
|
20
|
+
Next, load the payload:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
Monk::Id.load_payload(params[:monk_id_payload])
|
24
|
+
```
|
25
|
+
|
26
|
+
Or, if using the `cookie` option, simply pass in a hash-like cookies object:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
Monk::Id.load_payload(cookies)
|
30
|
+
```
|
31
|
+
|
32
|
+
Then you can access the user's ID and email:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
Monk::Id.user_id
|
36
|
+
Monk::Id.user_email
|
37
|
+
```
|
38
|
+
|
39
|
+
`nil` is returned if the user isn't signed in or the payload can't be decoded
|
40
|
+
and verified.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/lib/monk/id.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'json'
|
3
|
+
require 'openssl'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
# Global Monk namespace.
|
7
|
+
module Monk
|
8
|
+
# Integrate Monk ID on the server-side by accessing payloads from the
|
9
|
+
# client-side JavaScript.
|
10
|
+
#
|
11
|
+
# @author Monk Development, Inc.
|
12
|
+
module Id
|
13
|
+
# Expected path of config file in Rails and Sinatra relative to the app's
|
14
|
+
# root directory.
|
15
|
+
CONFIG_FILE = 'config/monk_id.yml'.freeze
|
16
|
+
|
17
|
+
# Name of the cookie that (optionally) stores the payload.
|
18
|
+
COOKIE_NAME = '_monkIdPayload'.freeze
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Load a YAML config file for a specific environment. Rails and Sinatra
|
22
|
+
# apps don't need to call this method if the config file is stored at
|
23
|
+
# {CONFIG_FILE}, as it's loaded automatically.
|
24
|
+
#
|
25
|
+
# @param path [String] Path of YAML config file to load. Leave `nil` to
|
26
|
+
# read from environment's `MONK_ID_CONFIG` value.
|
27
|
+
# @param environment [String] Environment section to use. Leave `nil` to
|
28
|
+
# read from environment's `MONK_ID_ENV` value. Defaults to
|
29
|
+
# `development`.
|
30
|
+
# @raise [StandardError] If the file doesn't exist or can't be read.
|
31
|
+
# @return [Hash<String>] Loaded config values.
|
32
|
+
def load_config(path = nil, environment = nil)
|
33
|
+
if defined? Rails
|
34
|
+
path ||= File.join(Rails.root, CONFIG_FILE)
|
35
|
+
environment ||= Rails.env
|
36
|
+
elsif defined? Sinatra
|
37
|
+
path ||= File.join(Sinatra::Application.settings.root, CONFIG_FILE)
|
38
|
+
environment ||= Sinatra::Application.settings.environment.to_s
|
39
|
+
else
|
40
|
+
path ||= ENV['MONK_ID_CONFIG']
|
41
|
+
environment ||= ENV['MONK_ID_ENV'] || 'development'
|
42
|
+
end
|
43
|
+
|
44
|
+
config = YAML.load_file(path)[environment]
|
45
|
+
|
46
|
+
verify_config(config)
|
47
|
+
|
48
|
+
@@config = config
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get a config value. Attempts to load the config if it hasn't already
|
52
|
+
# been loaded.
|
53
|
+
#
|
54
|
+
# @param key [String] Name of config value.
|
55
|
+
# @raise [StandardError] If the config can't be loaded.
|
56
|
+
# @return [*] Config value.
|
57
|
+
def config(key)
|
58
|
+
load_config unless @@config
|
59
|
+
|
60
|
+
@@config[key]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Load a payload from the client-side.
|
64
|
+
#
|
65
|
+
# @param encoded_payload [String, #[]] Encoded payload or Hash-like
|
66
|
+
# cookies object to automatically load the payload from.
|
67
|
+
# @return [Hash<Symbol>] Decoded and verified payload. Empty if there's no
|
68
|
+
# payload or it fails verification.
|
69
|
+
def load_payload(encoded_payload = nil)
|
70
|
+
if encoded_payload.is_a? String
|
71
|
+
payload = encoded_payload
|
72
|
+
elsif encoded_payload.respond_to? :[]
|
73
|
+
payload = encoded_payload[COOKIE_NAME]
|
74
|
+
end
|
75
|
+
|
76
|
+
return @@payload = {} unless payload
|
77
|
+
|
78
|
+
begin
|
79
|
+
payload = decode_payload(payload)
|
80
|
+
verified = verify_payload(payload)
|
81
|
+
rescue
|
82
|
+
verified = false
|
83
|
+
end
|
84
|
+
|
85
|
+
@@payload = verified ? payload : {}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Get the signed in user's UUID.
|
89
|
+
#
|
90
|
+
# @return [String] If signed in user.
|
91
|
+
# @return [nil] If no signed in user.
|
92
|
+
def user_id
|
93
|
+
payload_user :id
|
94
|
+
end
|
95
|
+
|
96
|
+
# Get the signed in user's email address.
|
97
|
+
#
|
98
|
+
# @return [String] If signed in user.
|
99
|
+
# @return [nil] If no signed in user.
|
100
|
+
def user_email
|
101
|
+
payload_user :email
|
102
|
+
end
|
103
|
+
|
104
|
+
# Check whether there's a signed in user.
|
105
|
+
#
|
106
|
+
# @return [Boolean] Whether there's a signed in user.
|
107
|
+
def signed_in?
|
108
|
+
!!user_id
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
# Loaded config values.
|
114
|
+
@@config = nil
|
115
|
+
|
116
|
+
# Loaded payload.
|
117
|
+
@@payload = nil
|
118
|
+
|
119
|
+
# Verify that a config has all the required values.
|
120
|
+
#
|
121
|
+
# @param config [Hash<String>] Config values.
|
122
|
+
# @raise [RuntimeError] If invalid.
|
123
|
+
# @return [true] If valid.
|
124
|
+
def verify_config(config)
|
125
|
+
raise 'no config loaded' unless config
|
126
|
+
raise 'no `app_id` config value' unless config['app_id']
|
127
|
+
raise 'no `app_secret` config value' unless config['app_secret']
|
128
|
+
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
# Decode a payload from the client-side.
|
133
|
+
#
|
134
|
+
# @param encoded_payload [String] Encoded payload.
|
135
|
+
# @raise [JSON::ParserError] If invalid JSON.
|
136
|
+
# @return [Hash<Symbol>] Decoded payload.
|
137
|
+
def decode_payload(encoded_payload)
|
138
|
+
JSON.parse(Base64.decode64(encoded_payload), :symbolize_names => true)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Generate the expected signature of a payload using the app's secret.
|
142
|
+
#
|
143
|
+
# @param payload [Hash<Symbol>] Decoded payload.
|
144
|
+
# @return [String] Expected signature of the payload.
|
145
|
+
def expected_signature(payload)
|
146
|
+
payload_clone = payload.clone
|
147
|
+
payload_clone[:user].delete(:signature)
|
148
|
+
|
149
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::SHA512.new, config('app_secret'), JSON.generate(payload_clone[:user]))
|
150
|
+
end
|
151
|
+
|
152
|
+
# Verify that a payload hasn't been tampered with or faked by comparing
|
153
|
+
# signatures.
|
154
|
+
#
|
155
|
+
# @param payload [Hash<Symbol>] Decoded payload.
|
156
|
+
# @return [Boolean] Whether the payload is legit.
|
157
|
+
def verify_payload(payload)
|
158
|
+
signature = Base64.decode64(payload[:user][:signature])
|
159
|
+
|
160
|
+
signature == expected_signature(payload)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Get the loaded payload.
|
164
|
+
#
|
165
|
+
# @return [Hash<Symbol>] Loaded payload. Empty if there's no payload or it
|
166
|
+
# failed verification.
|
167
|
+
def payload
|
168
|
+
@@payload || load_payload
|
169
|
+
end
|
170
|
+
|
171
|
+
# Get a value from the `user` hash of the loaded payload.
|
172
|
+
#
|
173
|
+
# @param key [Symbol] Name of value.
|
174
|
+
# @return [*] Requested value or `nil` if not set.
|
175
|
+
def payload_user(key)
|
176
|
+
payload = self.payload
|
177
|
+
|
178
|
+
if payload.key?(:user)
|
179
|
+
payload[:user][key]
|
180
|
+
else
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
data/monk-id.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'monk/id/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'monk-id'
|
8
|
+
spec.version = Monk::Id::VERSION.dup
|
9
|
+
spec.authors = ['Monk Development, Inc.']
|
10
|
+
spec.email = ['support@monkdevelopment.com']
|
11
|
+
spec.description = 'Integrate Monk ID on the server-side by accessing payloads from the client-side JavaScript.'
|
12
|
+
spec.summary = spec.description
|
13
|
+
spec.homepage = 'https://github.com/MonkDev/monk-id-ruby'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'yard'
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: monk-id
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Monk Development, Inc.
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-02-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: yard
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Integrate Monk ID on the server-side by accessing payloads from the client-side
|
63
|
+
JavaScript.
|
64
|
+
email:
|
65
|
+
- support@monkdevelopment.com
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- .gitignore
|
71
|
+
- .yardopts
|
72
|
+
- Gemfile
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- config/monk_id.sample.yml
|
76
|
+
- lib/monk/id.rb
|
77
|
+
- lib/monk/id/version.rb
|
78
|
+
- monk-id.gemspec
|
79
|
+
homepage: https://github.com/MonkDev/monk-id-ruby
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ! '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 1.8.23
|
101
|
+
signing_key:
|
102
|
+
specification_version: 3
|
103
|
+
summary: Integrate Monk ID on the server-side by accessing payloads from the client-side
|
104
|
+
JavaScript.
|
105
|
+
test_files: []
|
106
|
+
has_rdoc:
|