oo_auth 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +3 -0
- data/LICENSE +20 -0
- data/README.md +95 -0
- data/lib/oo_auth/configuration_error.rb +3 -0
- data/lib/oo_auth/constants.rb +19 -0
- data/lib/oo_auth/credentials.rb +22 -0
- data/lib/oo_auth/nonce/abstract_store.rb +13 -0
- data/lib/oo_auth/nonce/redis_store.rb +28 -0
- data/lib/oo_auth/nonce.rb +48 -0
- data/lib/oo_auth/request_proxy.rb +135 -0
- data/lib/oo_auth/signature.rb +55 -0
- data/lib/oo_auth/version.rb +3 -0
- data/lib/oo_auth.rb +94 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e5200d37778ac2220a1003d8551caf8c12d69fbd
|
4
|
+
data.tar.gz: 5a10c0b125e737c49ca54ae444f9205bb06671e3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d4f0c1423e3e41f26ff2bce7acdb83cb42a7b623732385d7259a0cb88f722eb8b8779b2481aded7f454c197e2f47c94d4b00ef88fa39b83ade09323e1399a6ea
|
7
|
+
data.tar.gz: 32d8a1dc341e408ca95fcb50fec6fa58a7621309ea7c95de0286c9a64013b08e5b0315ac5f4f236e6e007e9567fbf6b7e941d93867d306932070dd85d6fd9444
|
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Matthias Grosser
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/oo_auth.png)](http://badge.fury.io/rb/oo_auth) [![Code Climate](https://codeclimate.com/github/mtgrosser/oo_auth.png)](https://codeclimate.com/github/mtgrosser/oo_auth)
|
2
|
+
|
3
|
+
# oo_auth
|
4
|
+
|
5
|
+
OAuth Out Of Band - Sign, verify and authorize OAuth requests
|
6
|
+
|
7
|
+
OoAuth is a stripped-down implementation of the OAuth 1.0a protocol.
|
8
|
+
|
9
|
+
It only cares for signing and verifying OAuth requests, supporting both
|
10
|
+
```Net::HTTP``` and ```ActionDispatch::Request```.
|
11
|
+
|
12
|
+
OoAuth does not include any models or controllers dealing with token and
|
13
|
+
secret exchange, storage or lookup. Instead, it offers a simplistic API
|
14
|
+
where you can hook your own implementations as desired.
|
15
|
+
|
16
|
+
OoAuth comes with optional Redis support for short-time high performance storage
|
17
|
+
of OAuth nonces.
|
18
|
+
|
19
|
+
It can be used for implementing OAuth consumers as well as providers.
|
20
|
+
|
21
|
+
## Install
|
22
|
+
|
23
|
+
In your Gemfile:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'oo_auth'
|
27
|
+
```
|
28
|
+
|
29
|
+
## Prerequisites
|
30
|
+
|
31
|
+
OoAuth requires your application to provide stores for authorization tokens
|
32
|
+
and OAuth nonces.
|
33
|
+
|
34
|
+
OoAuth stores can be simple lambdas or regular ruby objects.
|
35
|
+
|
36
|
+
### Authorization store
|
37
|
+
|
38
|
+
The authorization store should return an instance of ```OoAuth::Credentials```.
|
39
|
+
It can either be a lambda or an object implementing the `authorization` method.
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
# your own implementation in SomeClass model
|
43
|
+
OoAuth.authorization_store = lambda { |consumer_key, token| SomeClass.find_by_tokens(consumer_key, token) }
|
44
|
+
```
|
45
|
+
```ruby
|
46
|
+
# direct lookup
|
47
|
+
OoAuth.authorization_store = User
|
48
|
+
```
|
49
|
+
### Nonce store
|
50
|
+
```ruby
|
51
|
+
require 'oo_auth/nonce/redis_store'
|
52
|
+
|
53
|
+
OoAuth.nonce_store = OoAuth::Nonce::RedisStore.new(namespace: 'foobar')
|
54
|
+
```
|
55
|
+
## Use
|
56
|
+
|
57
|
+
### OAuth consumer
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
http = Net::HTTP.new('photos.example.net', Net::HTTP.http_default_port)
|
61
|
+
request = Net::HTTP::Get.new('/photos?file=vacation.jpg&size=original')
|
62
|
+
|
63
|
+
credentials = OoAuth::Credentials.new('consumer_key',
|
64
|
+
'consumer_secret',
|
65
|
+
'access_token',
|
66
|
+
'access_token_secret')
|
67
|
+
|
68
|
+
OoAuth.sign!(http, request, credentials)
|
69
|
+
|
70
|
+
request['Authorization']
|
71
|
+
=> "OAuth oauth_version=\"1.0\", oauth_nonce=\"ly9V24IvFMhEGSlGW1tPniUVnVzQkWvn4W6Bwtmc4\", oauth_timestamp=\"1384116351\", oauth_signature_method=\"HMAC-SHA1\", oauth_consumer_key=\"consumer_key\", oauth_token=\"access_token\", oauth_signature=\"5G1ktyWhicZGnSu2AKkjok9%2BMPo%3D\""
|
72
|
+
```
|
73
|
+
|
74
|
+
### OAuth provider
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
class FoobarController < ApplicationController
|
78
|
+
|
79
|
+
before_filter :oauth_required
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def oauth_required
|
84
|
+
if authorization = OoAuth.authorize!(request)
|
85
|
+
self.current_user = authorization.user
|
86
|
+
else
|
87
|
+
render nothing: true, status: 401
|
88
|
+
end
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
|
93
|
+
## TODO
|
94
|
+
|
95
|
+
* Support POST body signing
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module OoAuth
|
2
|
+
|
3
|
+
# request tokens are passed between the consumer and the provider out of
|
4
|
+
# band (i.e. callbacks cannot be used), per section 6.1.1
|
5
|
+
OUT_OF_BAND = 'oob'
|
6
|
+
|
7
|
+
# FIXME: ordering
|
8
|
+
# required parameters, per sections 6.1.1, 6.3.1, and 7
|
9
|
+
PARAMETERS = %w(oauth_callback oauth_consumer_key oauth_token oauth_signature_method oauth_timestamp oauth_nonce oauth_verifier oauth_version oauth_signature oauth_body_hash)
|
10
|
+
|
11
|
+
# reserved character regexp, per section 5.1
|
12
|
+
RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~]/
|
13
|
+
|
14
|
+
# OoAuth only supports HMAC-SHA1
|
15
|
+
SIGNATURE_METHOD = 'HMAC-SHA1'
|
16
|
+
|
17
|
+
MAX_TIMESTAMP_DEVIATION = 5 * 60
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module OoAuth
|
2
|
+
class Credentials
|
3
|
+
AUTH_ATTRIBUTES = [:consumer_key, :consumer_secret, :token, :token_secret]
|
4
|
+
|
5
|
+
attr_reader *AUTH_ATTRIBUTES
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def generate
|
9
|
+
new(*4.times.collect { OoAuth.generate_key })
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(consumer_key, consumer_secret, token, token_secret)
|
14
|
+
@consumer_key, @consumer_secret, @token, @token_secret = consumer_key, consumer_secret, token, token_secret
|
15
|
+
end
|
16
|
+
|
17
|
+
def attributes
|
18
|
+
AUTH_ATTRIBUTES.inject({}) { |hsh, attr| hsh.update(attr => send(attr)) }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module OoAuth
|
2
|
+
class Nonce
|
3
|
+
class RedisStore < AbstractStore
|
4
|
+
attr_reader :redis, :namespace, :ttl
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
options.symbolize_keys!
|
8
|
+
@namespace = options.delete(:namespace)
|
9
|
+
@ttl = options.delete(:ttl) || 15.minutes
|
10
|
+
@redis = Redis.new(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create(nonce)
|
14
|
+
return nonce if @redis.set(key(nonce), '1', { nx: true, ex: ttl })
|
15
|
+
false
|
16
|
+
rescue Errno::ECONNREFUSED
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def key(nonce)
|
23
|
+
"#{@namespace}:oo_auth_nonce:#{nonce.timestamp}:#{nonce.value}"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module OoAuth
|
2
|
+
class Nonce
|
3
|
+
MAX_LENGTH = 255
|
4
|
+
|
5
|
+
attr_reader :value, :timestamp, :errors
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def store
|
9
|
+
OoAuth.nonce_store || fail(ConfigurationError, 'no nonce store set')
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(nonce)
|
13
|
+
if store.respond_to?(:call)
|
14
|
+
store.call(nonce)
|
15
|
+
elsif store.respond_to?(:create)
|
16
|
+
store.create(nonce)
|
17
|
+
else
|
18
|
+
fail ConfigurationError, 'nonce store not callable'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def remember(value, timestamp)
|
23
|
+
new(value, timestamp).save
|
24
|
+
end
|
25
|
+
|
26
|
+
def generate
|
27
|
+
new(OoAuth.generate_nonce, Time.now.utc.to_i)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(value, timestamp)
|
32
|
+
@value, @timestamp = value, timestamp.to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
def valid?
|
36
|
+
@errors = []
|
37
|
+
@errors << 'nonce value cannot be blank' if value.to_s == ''
|
38
|
+
@errors << 'nonce value too big' if value.size > MAX_LENGTH
|
39
|
+
@errors << 'illegal nonce timestamp' if timestamp <= 0
|
40
|
+
@errors.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def save
|
44
|
+
!!(valid? && self.class.create(self))
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module OoAuth
|
2
|
+
class RequestProxy
|
3
|
+
|
4
|
+
attr_reader :port, :ssl, :host, :path, :headers, :method, :body
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
|
9
|
+
# Parse an Authorization / WWW-Authenticate header into a hash. Takes care of unescaping and
|
10
|
+
# removing surrounding quotes. Raises a OAuth::Problem if the header is not parsable into a
|
11
|
+
# valid hash. Does not validate the keys or values.
|
12
|
+
#
|
13
|
+
# hash = parse(headers['Authorization'] || headers['WWW-Authenticate'])
|
14
|
+
# hash['oauth_timestamp']
|
15
|
+
# #=>"1234567890"
|
16
|
+
#
|
17
|
+
def parse(header)
|
18
|
+
header = header.to_s
|
19
|
+
return unless header.start_with?('OAuth ')
|
20
|
+
# decompose
|
21
|
+
params = header[6, header.length].split(',').inject({}) do |hsh, str|
|
22
|
+
key, value = str.split('=').map { |s| OoAuth.unescape(s.strip) }
|
23
|
+
if PARAMETERS.include?(key)
|
24
|
+
hsh[key] = value.sub(/^\"(.*)\"$/, '\1')
|
25
|
+
end
|
26
|
+
hsh
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(*args)
|
32
|
+
case args.size
|
33
|
+
when 1 # ActionDispatch request
|
34
|
+
request = args[0]
|
35
|
+
@port = request.port
|
36
|
+
@ssl = request.ssl?
|
37
|
+
@path = request.fullpath
|
38
|
+
@host = request.host
|
39
|
+
@headers = request.headers
|
40
|
+
when 2 # Net:HTTP request
|
41
|
+
http, request = args[0], args[1]
|
42
|
+
@port = http.port
|
43
|
+
@ssl = http.use_ssl?
|
44
|
+
@path = request.path
|
45
|
+
@host = http.address
|
46
|
+
@headers = request
|
47
|
+
else
|
48
|
+
raise ArgumentError, 'wrong number of arguments'
|
49
|
+
end
|
50
|
+
@method = request.method
|
51
|
+
@body = request.body
|
52
|
+
end
|
53
|
+
|
54
|
+
def normalized_request_uri
|
55
|
+
if self.port == Net::HTTP.default_port
|
56
|
+
scheme, port = :http, nil
|
57
|
+
elsif self.port == Net::HTTP.https_default_port
|
58
|
+
scheme, port = :https, nil
|
59
|
+
elsif ssl
|
60
|
+
scheme, port = :https, self.port
|
61
|
+
else
|
62
|
+
scheme, port = :http, self.port
|
63
|
+
end
|
64
|
+
|
65
|
+
uri = "#{scheme}://#{host.downcase}"
|
66
|
+
uri += ":#{port}" if port
|
67
|
+
uri += path.split('?').first
|
68
|
+
uri
|
69
|
+
end
|
70
|
+
|
71
|
+
def oauth_params
|
72
|
+
self.class.parse(authorization)
|
73
|
+
end
|
74
|
+
|
75
|
+
def oauth_params_without_signature
|
76
|
+
params = oauth_params
|
77
|
+
params.delete('oauth_signature')
|
78
|
+
params
|
79
|
+
end
|
80
|
+
|
81
|
+
def authorization
|
82
|
+
headers['Authorization']
|
83
|
+
end
|
84
|
+
|
85
|
+
def authorization=(header)
|
86
|
+
headers['Authorization'] = header
|
87
|
+
end
|
88
|
+
|
89
|
+
PARAMETERS.each do |parameter|
|
90
|
+
define_method "#{parameter[6..-1]}" do
|
91
|
+
oauth_params[parameter]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def post?
|
96
|
+
'POST' == method
|
97
|
+
end
|
98
|
+
|
99
|
+
def signature_base_string(params = {})
|
100
|
+
encoded_params = params_encode(params_array(self) + params_array(params))
|
101
|
+
OoAuth.encode(method, normalized_request_uri, encoded_params)
|
102
|
+
end
|
103
|
+
|
104
|
+
# FIXME: cf nested params implementation in oauth gem
|
105
|
+
# TODO: support oauth body signature for non-formencoded content types
|
106
|
+
def params_array(object)
|
107
|
+
case object
|
108
|
+
when Array then object
|
109
|
+
when Hash then object.to_a
|
110
|
+
when RequestProxy
|
111
|
+
tmp = object.path.split('?')
|
112
|
+
params = tmp[1] ? params_decode(tmp[1]) : []
|
113
|
+
if object.post? && object.headers['Content-Type'].to_s.start_with?('application/x-www-form-urlencoded')
|
114
|
+
params.concat params_decode(object.body)
|
115
|
+
end
|
116
|
+
params
|
117
|
+
else
|
118
|
+
raise "error: cannot convert #{object.class} object to params array"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def params_decode(string)
|
123
|
+
string.split('&').each_with_object([]) do |param, array|
|
124
|
+
k, v = *param.split('=')
|
125
|
+
array << [OoAuth.unescape(k), v && OoAuth.unescape(v)]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# cf. http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
|
130
|
+
def params_encode(params)
|
131
|
+
params.map { |k, v| [OoAuth.escape(k), OoAuth.escape(v)] }.sort.map { |k, v| "#{k}=#{v}" }.join('&')
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module OoAuth
|
2
|
+
module Signature
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def hmac_sha1_signature(base_string, consumer_secret, token_secret)
|
6
|
+
Base64.strict_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, OoAuth.encode(consumer_secret, token_secret), base_string))
|
7
|
+
end
|
8
|
+
|
9
|
+
def calculate_signature(proxy, credentials, params)
|
10
|
+
hmac_sha1_signature(proxy.signature_base_string(params), credentials.consumer_secret, credentials.token_secret)
|
11
|
+
end
|
12
|
+
|
13
|
+
def sign!(proxy, credentials)
|
14
|
+
params = {
|
15
|
+
oauth_version: '1.0',
|
16
|
+
oauth_nonce: OoAuth.generate_nonce,
|
17
|
+
oauth_timestamp: OoAuth.timestamp,
|
18
|
+
oauth_signature_method: SIGNATURE_METHOD,
|
19
|
+
oauth_consumer_key: credentials.consumer_key,
|
20
|
+
oauth_token: credentials.token
|
21
|
+
}
|
22
|
+
|
23
|
+
params[:oauth_signature] = calculate_signature(proxy, credentials, params)
|
24
|
+
|
25
|
+
proxy.authorization = authorization_header(params)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Check signature validity without remembering nonce - DO NOT use to authorize actual requests
|
29
|
+
def valid?(proxy, credentials)
|
30
|
+
verify_timestamp!(proxy) &&
|
31
|
+
calculate_signature(proxy, credentials, proxy.oauth_params_without_signature) == proxy.signature
|
32
|
+
end
|
33
|
+
|
34
|
+
# Verify signature and remember nonce - use this to authorize actual requests
|
35
|
+
def verify!(proxy, credentials)
|
36
|
+
valid?(proxy, credentials) && remember_nonce!(proxy)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def verify_timestamp!(proxy)
|
42
|
+
(OoAuth.timestamp - proxy.timestamp.to_i).abs < MAX_TIMESTAMP_DEVIATION
|
43
|
+
end
|
44
|
+
|
45
|
+
def remember_nonce!(proxy)
|
46
|
+
Nonce.remember(proxy.nonce, proxy.timestamp)
|
47
|
+
end
|
48
|
+
|
49
|
+
def authorization_header(params)
|
50
|
+
'OAuth ' + params.map { |k, v| "#{OoAuth.escape(k)}=\"#{OoAuth.escape(v)}\"" }.join(', ')
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/oo_auth.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'uri'
|
3
|
+
require 'net/http'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
require 'oo_auth/version'
|
7
|
+
require 'oo_auth/constants'
|
8
|
+
require 'oo_auth/configuration_error'
|
9
|
+
require 'oo_auth/nonce'
|
10
|
+
require 'oo_auth/nonce/abstract_store'
|
11
|
+
require 'oo_auth/request_proxy'
|
12
|
+
require 'oo_auth/credentials'
|
13
|
+
require 'oo_auth/signature'
|
14
|
+
|
15
|
+
module OoAuth
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# Initialize with instance of store
|
19
|
+
# OoAuth.nonce_store = OoAuth::Nonce::RedisStore.new(namespace: 'foo')
|
20
|
+
attr_accessor :nonce_store
|
21
|
+
|
22
|
+
|
23
|
+
# Define a lookup method for access token verification
|
24
|
+
# It should be callable (proc) or provide an +authorization+ method,
|
25
|
+
# with the argument being the consumer key and token.
|
26
|
+
# The proc or method call should return
|
27
|
+
# - if the consumer key/token combination exists:
|
28
|
+
# an object which responding to +credentials+ with an
|
29
|
+
# initialized instance of
|
30
|
+
# OoAuth::Credentials
|
31
|
+
# - nil otherwise.
|
32
|
+
attr_accessor :authorization_store
|
33
|
+
|
34
|
+
# Generate a random key of up to +size+ bytes. The value returned is Base64 encoded with non-word
|
35
|
+
# characters removed.
|
36
|
+
def generate_key(size = 32)
|
37
|
+
Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/, '')
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :generate_nonce, :generate_key
|
41
|
+
|
42
|
+
# Escape +value+ by URL encoding all non-reserved character.
|
43
|
+
#
|
44
|
+
# See Also: {OAuth core spec version 1.0, section 5.1}[http://oauth.net/core/1.0#rfc.section.5.1]
|
45
|
+
def escape(value)
|
46
|
+
URI.escape(value.to_s, RESERVED_CHARACTERS)
|
47
|
+
rescue ArgumentError
|
48
|
+
URI.escape(value.to_s.force_encoding(Encoding::UTF_8), RESERVED_CHARACTERS)
|
49
|
+
end
|
50
|
+
|
51
|
+
def unescape(value)
|
52
|
+
URI.unescape(value.gsub('+', '%2B'))
|
53
|
+
end
|
54
|
+
|
55
|
+
# cf. http://tools.ietf.org/html/rfc5849#section-3.4.1.1
|
56
|
+
# cf. http://tools.ietf.org/html/rfc5849#section-3.4.4
|
57
|
+
def encode(*components)
|
58
|
+
components.map { |component| OoAuth.escape(component) }.join('&')
|
59
|
+
end
|
60
|
+
|
61
|
+
# Current UTC timestamp
|
62
|
+
def timestamp
|
63
|
+
Time.now.utc.to_i
|
64
|
+
end
|
65
|
+
|
66
|
+
def authorization(consumer_key, token)
|
67
|
+
if authorization_store.respond_to?(:call)
|
68
|
+
authorization_store.call(consumer_key, token)
|
69
|
+
elsif authorization_store.respond_to?(:authorization)
|
70
|
+
authorization_store.authorization(consumer_key, token)
|
71
|
+
else
|
72
|
+
fail ConfigurationError, 'authorization store not callable'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Use this to sign Net::HTTP or ActionDispatch requests
|
77
|
+
def sign!(*args)
|
78
|
+
credentials = args.pop
|
79
|
+
proxy = RequestProxy.new(*args)
|
80
|
+
Signature.sign!(proxy, credentials)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Use this in your controllers to verify the OAuth signature
|
84
|
+
# of a request.
|
85
|
+
def authorize!(*args)
|
86
|
+
proxy = RequestProxy.new(*args)
|
87
|
+
return unless authorization = self.authorization(proxy.consumer_key, proxy.token)
|
88
|
+
return unless Signature.verify!(proxy, authorization.credentials)
|
89
|
+
authorization
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oo_auth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matthias Grosser
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: byebug
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: simplecov
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.8.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.8.7
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '4.7'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '4.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: timecop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.6.3
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.6.3
|
83
|
+
description: Out Of Band OAuth
|
84
|
+
email: mtgrosser@gmx.net
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- lib/oo_auth/configuration_error.rb
|
90
|
+
- lib/oo_auth/constants.rb
|
91
|
+
- lib/oo_auth/credentials.rb
|
92
|
+
- lib/oo_auth/nonce/abstract_store.rb
|
93
|
+
- lib/oo_auth/nonce/redis_store.rb
|
94
|
+
- lib/oo_auth/nonce.rb
|
95
|
+
- lib/oo_auth/request_proxy.rb
|
96
|
+
- lib/oo_auth/signature.rb
|
97
|
+
- lib/oo_auth/version.rb
|
98
|
+
- lib/oo_auth.rb
|
99
|
+
- LICENSE
|
100
|
+
- README.md
|
101
|
+
- CHANGELOG
|
102
|
+
homepage: http://github.com/mtgrosser/oo_auth
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata: {}
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 2.0.3
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
125
|
+
summary: OAuth without the callbacks
|
126
|
+
test_files: []
|