An OAuthFilter provides both OAuth 1.0 support, plus OAuth Discovery.
Responds to the following URIs as part of the OAuth 1.0 “dance”:
/oauth/request_tokens /oauth/authorization /oauth/authorized_request_tokens/{id} /oauth/access_tokens
Responds to the following URIs as part of OAuth Discovery:
/oauth /oauth/meta
See also:
- OAuth Core 1.0
- OAuth Discovery
- Thread covering /oauth and /oauth/meta URIs.
Methods
public class
public instance
protected instance
- authorize_request_token
- challenge
- challenge_headers
- create_access_token
- create_request_token
- deny_request_token
- discovery_link
- get_descriptor
- get_meta
- inject_challenge
- inject_user_or_challenge
- load_user_from_session
- login_redirect
- oauth_disco_draft2_xrds?
- request_authorization
- valid_nonce?
- xrds_location
Included modules
Public class methods
new
(app, options={})
[show source]
# File lib/cloudkit/oauth_filter.rb, line 27 27: def initialize(app, options={}) 28: @app = app 29: @options = options 30: end
Public instance methods
call
(env)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 32 32: def call(env) 33: @@lock.synchronize do 34: @@store = OAuthStore.new 35: end unless @@store 36: 37: request = Request.new(env) 38: request.announce_auth(CLOUDKIT_OAUTH_FILTER_KEY) 39: return xrds_location(request) if oauth_disco_draft2_xrds?(request) 40: return @app.call(env) if request.path_info == '/' 41: 42: load_user_from_session(request) 43: 44: begin 45: case request 46: when r(:get, '/oauth/meta') 47: get_meta(request) 48: when r(:post, '/oauth/request_tokens', ['oauth_consumer_key']) 49: create_request_token(request) 50: when r(:get, '/oauth/authorization', ['oauth_token']) 51: request_authorization(request) 52: when r(:put, '/oauth/authorized_request_tokens/:id', ['submit' => 'Approve']) 53: # Temporarily relying on a button value until pluggable templates are 54: # introduced in 1.0. 55: authorize_request_token(request) 56: when r(:put, '/oauth/authorized_request_tokens/:id', ['submit' => 'Deny']) 57: # See previous comment. 58: deny_request_token(request) 59: when r(:post, '/oauth/authorized_request_tokens/:id', [{'_method' => 'PUT'}]) 60: authorize_request_token(request) 61: when r(:post, '/oauth/access_tokens') 62: create_access_token(request) 63: when r(:get, '/oauth') 64: get_descriptor(request) 65: else 66: inject_user_or_challenge(request) 67: @app.call(env) 68: end 69: rescue OAuth::Signature::UnknownSignatureMethod 70: # The OAuth spec suggests a 400 status, but serving a 401 with the 71: # meta/challenge info seems more appropriate as the OAuth metadata 72: # specifies the supported signature methods, giving the user agent an 73: # opportunity to fix the error. 74: return challenge(request, 'unknown signature method') 75: end 76: end
Protected instance methods
authorize_request_token
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 116 116: def authorize_request_token(request) 117: return login_redirect(request) unless request.current_user 118: 119: request_token_response = @@store.get("/cloudkit_oauth_request_tokens/#{request.last_path_element}") 120: request_token = request_token_response.parsed_content 121: if request_token['authorized_at'] 122: return challenge(request, 'invalid request token') 123: end 124: 125: request_token['user_id'] = request.current_user 126: request_token['authorized_at'] = Time.now.httpdate 127: json = JSON.generate(request_token) 128: @@store.put( 129: "/cloudkit_oauth_request_tokens/#{request.last_path_element}", 130: :etag => request_token_response.etag, 131: :json => json) 132: erb(request, :authorize_request_token) 133: end
challenge
(request, message='')
[show source]
# File lib/cloudkit/oauth_filter.rb, line 220 220: def challenge(request, message='') 221: Rack::Response.new(message, 401, challenge_headers(request)).finish 222: end
challenge_headers
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 224 224: def challenge_headers(request) 225: { 226: 'WWW-Authenticate' => "OAuth realm=\"http://#{request.env['HTTP_HOST']}\"", 227: 'Link' => discovery_link(request), 228: 'Content-Type' => 'application/json' 229: } 230: end
create_access_token
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 145 145: def create_access_token(request) 146: return challenge(request, 'invalid nonce') unless valid_nonce?(request) 147: 148: consumer_response = @@store.get("/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}") 149: unless consumer_response.status == 200 150: return challenge(request, 'invalid consumer') 151: end 152: 153: consumer = consumer_response.parsed_content 154: request_token_response = @@store.get("/cloudkit_oauth_request_tokens/#{request[:oauth_token]}") 155: unless request_token_response.status == 200 156: return challenge(request, 'invalid request token') 157: end 158: 159: request_token = request_token_response.parsed_content 160: unless request_token['consumer_key'] == request[:oauth_consumer_key] 161: return challenge(request, 'invalid consumer') 162: end 163: 164: signature = OAuth::Signature.build(request) do 165: [request_token['secret'], consumer['secret']] 166: end 167: unless signature.verify 168: return challenge(request, 'invalid signature') 169: end 170: 171: token_id, secret = OAuth::Server.new(request.host).generate_credentials 172: token_data = JSON.generate( 173: :secret => secret, 174: :consumer_key => request[:oauth_consumer_key], 175: :consumer_secret => consumer['secret'], 176: :user_id => request_token['user_id']) 177: @@store.put("/cloudkit_oauth_tokens/#{token_id}", :json => token_data) 178: @@store.delete( 179: "/cloudkit_oauth_request_tokens/#{request[:oauth_token]}", 180: :etag => request_token_response.etag) 181: Rack::Response.new("oauth_token=#{token_id}&oauth_token_secret=#{secret}", 201).finish 182: end
create_request_token
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 82 82: def create_request_token(request) 83: return challenge(request, 'invalid nonce') unless valid_nonce?(request) 84: 85: consumer_result = @@store.get("/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}") 86: unless consumer_result.status == 200 87: return challenge(request, 'invalid consumer') 88: end 89: 90: consumer = consumer_result.parsed_content 91: signature = OAuth::Signature.build(request) { [nil, consumer['secret']] } 92: return challenge(request, 'invalid signature') unless signature.verify 93: 94: token_id, secret = OAuth::Server.new(request.host).generate_credentials 95: request_token = JSON.generate( 96: :secret => secret, 97: :consumer_key => request[:oauth_consumer_key]) 98: @@store.put( 99: "/cloudkit_oauth_request_tokens/#{token_id}", 100: :json => request_token) 101: Rack::Response.new("oauth_token=#{token_id}&oauth_token_secret=#{secret}", 201).finish 102: end
deny_request_token
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 135 135: def deny_request_token(request) 136: return login_redirect(request) unless request.current_user 137: 138: request_token_response = @@store.get("/cloudkit_oauth_request_tokens/#{request.last_path_element}") 139: @@store.delete( 140: "/cloudkit_oauth_request_tokens/#{request.last_path_element}", 141: :etag => request_token_response.etag) 142: erb(request, :request_token_denied) 143: end
discovery_link
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 232 232: def discovery_link(request) 233: "<#{request.scheme}://#{request.env['HTTP_HOST']}/oauth/meta>; rel=\"http://oauth.net/discovery/1.0/rel/provider\"" 234: end
get_descriptor
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 262 262: def get_descriptor(request) 263: erb(request, :oauth_descriptor, {'Content-Type' => 'application/xrds+xml'}) 264: end
get_meta
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 245 245: def get_meta(request) 246: # Expected in next OAuth Discovery Draft 247: erb(request, :oauth_meta) 248: end
inject_challenge
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 216 216: def inject_challenge(request) 217: request.env[CLOUDKIT_AUTH_CHALLENGE] = challenge_headers(request) 218: end
inject_user_or_challenge
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 184 184: def inject_user_or_challenge(request) 185: unless valid_nonce?(request) 186: request.current_user = '' 187: inject_challenge(request) 188: return 189: end 190: 191: result = @@store.get("/cloudkit_oauth_tokens/#{request[:oauth_token]}") 192: access_token = result.parsed_content 193: signature = OAuth::Signature.build(request) do 194: [access_token['secret'], access_token['consumer_secret']] 195: end 196: if signature.verify 197: request.current_user = access_token['user_id'] 198: else 199: request.current_user = '' 200: inject_challenge(request) 201: end 202: end
load_user_from_session
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 241 241: def load_user_from_session(request) 242: request.current_user = request.session['user_uri'] if request.session 243: end
login_redirect
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 236 236: def login_redirect(request) 237: request.session['return_to'] = request.url if request.session 238: Rack::Response.new([], 302, {'Location' => request.login_url}).finish 239: end
oauth_disco_draft2_xrds?
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 250 250: def oauth_disco_draft2_xrds?(request) 251: # Current OAuth Discovery Draft 2 / XRDS-Simple 1.0, Section 5.1.2 252: request.get? && 253: request.env['HTTP_ACCEPT'] && 254: request.env['HTTP_ACCEPT'].match(/application\/xrds\+xml/) 255: end
request_authorization
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 104 104: def request_authorization(request) 105: return login_redirect(request) unless request.current_user 106: 107: request_token_result = @@store.get("/cloudkit_oauth_request_tokens/#{request[:oauth_token]}") 108: unless request_token_result.status == 200 109: return challenge(request, 'invalid request token') 110: end 111: 112: request_token = request_token_result.parsed_content 113: erb(request, :request_authorization) 114: end
valid_nonce?
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 204 204: def valid_nonce?(request) 205: timestamp = request[:oauth_timestamp] 206: nonce = request[:oauth_nonce] 207: return false unless (timestamp && nonce) 208: 209: uri = "/cloudkit_oauth_nonces/#{timestamp},#{nonce}" 210: result = @@store.put(uri, :json => '{}') 211: return false unless result.status == 201 212: 213: true 214: end
xrds_location
(request)
[show source]
# File lib/cloudkit/oauth_filter.rb, line 257 257: def xrds_location(request) 258: # Current OAuth Discovery Draft 2 / XRDS-Simple 1.0, Section 5.1.2 259: Rack::Response.new([], 200, {'X-XRDS-Location' => "#{request.scheme}://#{request.env['HTTP_HOST']}/oauth"}).finish 260: end