rack-oauth2-server 1.2.2 → 1.3.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.
Files changed (40) hide show
  1. data/CHANGELOG +14 -0
  2. data/Gemfile +3 -0
  3. data/README.rdoc +26 -7
  4. data/Rakefile +1 -1
  5. data/VERSION +1 -0
  6. data/lib/rack/oauth2/admin/css/screen.css +233 -0
  7. data/lib/rack/oauth2/admin/images/loading.gif +0 -0
  8. data/lib/rack/oauth2/admin/js/application.js +154 -0
  9. data/lib/rack/oauth2/admin/js/jquery.js +166 -0
  10. data/lib/rack/oauth2/admin/js/jquery.tmpl.js +414 -0
  11. data/lib/rack/oauth2/admin/js/sammy.js +5 -0
  12. data/lib/rack/oauth2/admin/js/sammy.json.js +5 -0
  13. data/lib/rack/oauth2/admin/js/sammy.storage.js +5 -0
  14. data/lib/rack/oauth2/admin/js/sammy.title.js +5 -0
  15. data/lib/rack/oauth2/admin/js/sammy.tmpl.js +5 -0
  16. data/lib/rack/oauth2/admin/js/underscore.js +722 -0
  17. data/lib/rack/oauth2/admin/views/client.tmpl +48 -0
  18. data/lib/rack/oauth2/admin/views/clients.tmpl +36 -0
  19. data/lib/rack/oauth2/admin/views/edit.tmpl +57 -0
  20. data/lib/rack/oauth2/admin/views/index.html +26 -0
  21. data/lib/rack/oauth2/models/access_grant.rb +6 -4
  22. data/lib/rack/oauth2/models/access_token.rb +36 -4
  23. data/lib/rack/oauth2/models/auth_request.rb +4 -3
  24. data/lib/rack/oauth2/models/client.rb +15 -2
  25. data/lib/rack/oauth2/server.rb +71 -58
  26. data/lib/rack/oauth2/server/admin.rb +216 -0
  27. data/lib/rack/oauth2/server/helper.rb +4 -4
  28. data/lib/rack/oauth2/sinatra.rb +2 -2
  29. data/rack-oauth2-server.gemspec +2 -3
  30. data/test/admin/api_test.rb +196 -0
  31. data/test/admin_test_.rb +49 -0
  32. data/test/{access_grant_test.rb → oauth/access_grant_test.rb} +1 -1
  33. data/test/{access_token_test.rb → oauth/access_token_test.rb} +83 -12
  34. data/test/{authorization_test.rb → oauth/authorization_test.rb} +1 -1
  35. data/test/rails/config/environment.rb +2 -0
  36. data/test/rails/log/test.log +72938 -0
  37. data/test/setup.rb +17 -1
  38. data/test/sinatra/my_app.rb +1 -1
  39. metadata +27 -9
  40. data/lib/rack/oauth2/server/version.rb +0 -9
@@ -0,0 +1,48 @@
1
+ <div class="client">
2
+ <div class="details">
3
+ <a href="${link}" class="name">{{if imageUrl}}<img src="${imageUrl}">{{/if}} ${displayName}</a>
4
+ <a href="#/client/${id}/edit" rel="edit">Edit</a>
5
+ {{if !revoked}}
6
+ <a href="${revoke}" data-method="post" data-confirm="There is no undo. Are you really really sure?" rel="revoke">Revoke</a>
7
+ {{/if}}
8
+ <div class="meta">
9
+ Created {{html $.shortdate(revoked)}}
10
+ {{if revoked}}Revoked {{html $.shortdate(revoked)}}{{/if}}
11
+ </div>
12
+ </div>
13
+ <ul class="badges">
14
+ <li title="Access tokens granted, lifetime total"><big>${$.thousands(tokens.total)}</big><small>Granted</small></li>
15
+ <li title="Access tokens granted, last 7 days"><big>${$.thousands(tokens.week)}</big><small>This Week</small></li>
16
+ <li title="Access tokens revoked, last 7 days"><big>${$.thousands(tokens.revoked)}</big><small>Revoked (Week)</small></li>
17
+ </ul>
18
+ <table class="tokens">
19
+ <thead>
20
+ <th>Token</th>
21
+ <th>Identity</th>
22
+ <th>Scope</th>
23
+ <th>Created</th>
24
+ <th>Revoked</th>
25
+ </thead>
26
+ <tbody>
27
+ {{each tokens.list}}
28
+ <tr>
29
+ <td class="token">${token}</td>
30
+ <td class="identity">${identity}</td>
31
+ <td class="scope">${scope}</td>
32
+ <td class="created">{{html $.shortdate(created)}}</td>
33
+ <td class="revoke">
34
+ {{if revoked}}
35
+ {{html $.shortdate(revoked)}}
36
+ {{else}}
37
+ <a href="${revoke}" data-method="post" data-confirm="Are you sure?" rel="revoke">Revoke</a>
38
+ {{/if}}
39
+ </td>
40
+ </tr>
41
+ {{/each}}
42
+ </tbody>
43
+ </table>
44
+ <div class="pagination">
45
+ {{if tokens.previous}}<a href="#/client/${id}/${tokens.page - 1}" rel="previous">Previous</a>{{/if}}
46
+ {{if tokens.next}}<a href="#/client/${id}/${tokens.page + 1}" rel="next">Next</a>{{/if}}
47
+ </div>
48
+ </div>
@@ -0,0 +1,36 @@
1
+ <div class="client">
2
+ <a href="#/new" style="float:left">Add New Client</a>
3
+ <ul class="badges">
4
+ <li title="Access tokens granted, lifetime total"><big>${$.thousands(tokens.total)}</big><small>Granted</small></li>
5
+ <li title="Access tokens granted, last 7 days"><big>${$.thousands(tokens.week)}</big><small>This Week</small></li>
6
+ <li title="Access tokens revoked, last 7 days"><big>${$.thousands(tokens.revoked)}</big><small>Revoked (Week)</small></li>
7
+ </ul>
8
+ <table class="clients">
9
+ <thead>
10
+ <th>Application</th>
11
+ <th>ID/Secret</th>
12
+ <th>Created</th>
13
+ <th>Revoked</th>
14
+ </thead>
15
+ {{each clients}}
16
+ <tr class="${revoked ? "revoked" : "active"}">
17
+ <td class="name">
18
+ <a href="#/client/${id}">
19
+ {{if imageUrl}}<img src="${imageUrl}">{{/if}}
20
+ ${displayName}
21
+ </a>
22
+ </td>
23
+ <td class="secrets">
24
+ <a href="" rel="toggle">Reveal</a>
25
+ <dl>
26
+ <dt>ID</dt><dd>${id}</dd>
27
+ <dt>Secret</dt><dd>${secret}</dd>
28
+ <dt>Redirect</dt><dd>${redirectUri}</dd>
29
+ </dl>
30
+ </td>
31
+ <td class="created">{{html $.shortdate(created)}}</td>
32
+ <td class="revoke">{{if revoked}}{{html $.shortdate(revoked)}}{{/if}}</td>
33
+ </tr>
34
+ {{/each}}
35
+ </table>
36
+ </div>
@@ -0,0 +1,57 @@
1
+ {{if id}}<form action="#/client/${id}" method="put" class="client edit">
2
+ {{else}}<form action="#/clients" method="post" class="client new">{{/if}}
3
+ <img id="image">
4
+ <label>Display Name
5
+ <input type="text" name="displayName" value="${displayName}" size="30" autofocus>
6
+ <p class="hint">This is the application name that users see when asked to authorize.</p>
7
+ </label>
8
+ <label>Site URL
9
+ <input type="text" name="link" value="${link}" size="30">
10
+ <p class="hint">This is a link to the application's site.</p>
11
+ </label>
12
+ <label>Image URL
13
+ <input type="text" name="imageUrl" value="${imageUrl}" size="30">
14
+ <p class="hint">This is a link to the application's icon (48x48).</p>
15
+ </label>
16
+ <label>Redirect URI
17
+ <input type="text" name="redirectUri" value="${redirectUri}" size="30">
18
+ <p class="hint">Users redirected back to this URL on successful authorization.</p>
19
+ </label>
20
+ {{if id}}<button>Save Changes</button>
21
+ {{else}}<button>Create Client</button>{{/if}}
22
+ </form>
23
+ <script type="text/javascript">
24
+ $(function() {
25
+ var image = $("#image");
26
+ image.load(function() {
27
+ image.show().removeClass("loading");
28
+ }).error(function() {
29
+ if (image.attr("src"))
30
+ image.removeClass("loading");
31
+ });
32
+
33
+ var imageUrl = $("input[name=imageUrl]");
34
+ imageUrl.change(function() {
35
+ var url = $(this).val().trim();
36
+ if (url == "") {
37
+ image.hide();
38
+ } else {
39
+ image.attr("src", "admin/images/loading.gif").show().addClass("loading");
40
+ setTimeout(function() { image.attr("src", url); }, 10);
41
+ }
42
+ }).trigger("change");
43
+
44
+ $("input[name=link]").change(function() {
45
+ if (imageUrl.val().trim() == "") {
46
+ $("#image").show().addClass("loading").attr("src", null);
47
+ var image = new Image();
48
+ image.src = $(this).val().trim().replace(/^(https?:\/\/)(.+?)(\/.*|$)/, "$1$2/favicon.ico");
49
+ image.onload = function() {
50
+ if (imageUrl.val().trim() == "") {
51
+ imageUrl.val(image.src).trigger("change");
52
+ }
53
+ }
54
+ }
55
+ });
56
+ })
57
+ </script>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>OAuth Console</title>
5
+ <link href="admin/css/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
6
+ <script src="admin/js/jquery.js" type="text/javascript"></script>
7
+ <script src="admin/js/jquery.tmpl.js" type="text/javascript"></script>
8
+ <script src="admin/js/sammy.js" type="text/javascript"></script>
9
+ <script src="admin/js/sammy.tmpl.js" type="text/javascript"></script>
10
+ <script src="admin/js/sammy.json.js" type="text/javascript"></script>
11
+ <script src="admin/js/sammy.storage.js" type="text/javascript"></script>
12
+ <script src="admin/js/sammy.title.js" type="text/javascript"></script>
13
+ <script src="admin/js/underscore.js" type="text/javascript"></script>
14
+ <script src="admin/js/application.js" type="text/javascript"></script>
15
+ </head>
16
+ <body>
17
+ <div id="notice" style="display:none"></div>
18
+ <div id="header"><a href="#/" class="title">OAuth Console</a></div>
19
+ <div id="main"></div>
20
+ <script>
21
+ var loading = new Image();
22
+ loading.src = "admin/images/loading.gif";
23
+ $(function() { Sammy("#main").run("#/"); });
24
+ </script>
25
+ </body>
26
+ </html>
@@ -13,8 +13,9 @@ module Rack
13
13
 
14
14
  # Create a new access grant.
15
15
  def create(identity, scope, client_id, redirect_uri)
16
- fields = { :_id=>Server.secure_random, :identity=>identity.to_s, :scope=>scope, :client_id=>BSON::ObjectId(client_id.to_s),
17
- :redirect_uri=>redirect_uri, :created_at=>Time.now.utc, :granted_at=>nil, :access_token=>nil, :revoked=>nil }
16
+ fields = { :_id=>Server.secure_random, :identity=>identity.to_s, :scope=>scope,
17
+ :client_id=>BSON::ObjectId(client_id.to_s), :redirect_uri=>redirect_uri,
18
+ :created_at=>Time.now.utc.to_i, :granted_at=>nil, :access_token=>nil, :revoked=>nil }
18
19
  collection.insert fields
19
20
  Server.new_instance self, fields
20
21
  end
@@ -54,7 +55,7 @@ module Rack
54
55
  raise InvalidGrantError if self.access_token || self.revoked
55
56
  access_token = AccessToken.get_token_for(identity, scope, client_id)
56
57
  self.access_token = access_token.token
57
- self.granted_at = Time.now.utc
58
+ self.granted_at = Time.now.utc.to_i
58
59
  self.class.collection.update({ :_id=>code, :access_token=>nil, :revoked=>nil }, { :$set=>{ :granted_at=>granted_at, :access_token=>access_token.token } }, :safe=>true)
59
60
  reload = self.class.collection.find_one({ :_id=>code, :revoked=>nil }, { :fields=>%w{access_token} })
60
61
  raise InvalidGrantError unless reload && reload["access_token"] == access_token.token
@@ -62,7 +63,8 @@ module Rack
62
63
  end
63
64
 
64
65
  def revoke!
65
- self.class.collection.update({ :_id=>code, :revoked=>nil }, { :$set=>{ :revoked=>Time.now.utc } })
66
+ self.revoked = Time.now.utc.to_i
67
+ self.class.collection.update({ :_id=>code, :revoked=>nil }, { :$set=>{ :revoked=>revoked } })
66
68
  end
67
69
 
68
70
  Server.create_indexes do
@@ -8,6 +8,7 @@ module Rack
8
8
  # and scope. It may be revoked, or expire after a certain period.
9
9
  class AccessToken
10
10
  class << self
11
+
11
12
  # Find AccessToken from token. Does not return revoked tokens.
12
13
  def from_token(token)
13
14
  Server.new_instance self, collection.find_one({ :_id=>token, :revoked=>nil })
@@ -16,9 +17,11 @@ module Rack
16
17
  # Get an access token (create new one if necessary).
17
18
  def get_token_for(identity, scope, client_id)
18
19
  scope = scope.split.sort.join(" ") # Make sure always in same order.
19
- unless token = collection.find_one({ :identity=>identity.to_s, :scope=>scope, :client_id=>BSON::ObjectId(client_id.to_s) })
20
- token = { :_id=>Server.secure_random, :identity=>identity.to_s, :scope=>scope, :client_id=>BSON::ObjectId(client_id.to_s),
21
- :created_at=>Time.now.utc, :expires_at=>nil, :revoked=>nil }
20
+ client_id = BSON::ObjectId(client_id.to_s)
21
+ unless token = collection.find_one({ :identity=>identity.to_s, :scope=>scope, :client_id=>client_id })
22
+ token = { :_id=>Server.secure_random, :identity=>identity.to_s, :scope=>scope,
23
+ :client_id=>client_id, :created_at=>Time.now.utc.to_i,
24
+ :expires_at=>nil, :revoked=>nil }
22
25
  collection.insert token
23
26
  end
24
27
  Server.new_instance self, token
@@ -29,6 +32,35 @@ module Rack
29
32
  collection.find({ :identity=>identity }).map { |fields| Server.new_instance self, fields }
30
33
  end
31
34
 
35
+ # Returns all access tokens for a given client, Use limit and offset
36
+ # to return a subset of tokens, sorted by creation date.
37
+ def for_client(client_id, offset = 0, limit = 100)
38
+ client_id = BSON::ObjectId(client_id.to_s)
39
+ collection.find({ :client_id=>client_id }, { :sort=>[[:created_at, Mongo::ASCENDING]], :skip=>offset, :limit=>limit }).
40
+ map { |token| Server.new_instance self, token }
41
+ end
42
+
43
+ # Returns count of access tokens.
44
+ #
45
+ # @param [Hash] filter Count only a subset of access tokens
46
+ # @option filter [Integer] days Only count that many days (since now)
47
+ # @option filter [Boolean] revoked Only count revoked (true) or non-revoked (false) tokens; count all tokens if nil
48
+ # @option filter [String, ObjectId] client_id Only tokens grant to this client
49
+ def count(filter = {})
50
+ select = {}
51
+ if filter[:days]
52
+ now = Time.now.utc.to_i
53
+ select[:created_at] = { :$gt=>now - filter[:days] * 86400, :$lte=>now }
54
+ end
55
+ if filter.has_key?(:revoked)
56
+ select[:revoked] = filter[:revoked] ? { :$ne=>nil } : { :$eq=>nil }
57
+ end
58
+ if filter[:client_id]
59
+ select[:client_id] = BSON::ObjectId(filter[:client_id].to_s)
60
+ end
61
+ collection.find(select).count
62
+ end
63
+
32
64
  def collection
33
65
  Server.database["oauth2.access_tokens"]
34
66
  end
@@ -52,7 +84,7 @@ module Rack
52
84
 
53
85
  # Revokes this access token.
54
86
  def revoke!
55
- self.revoked = Time.now.utc
87
+ self.revoked = Time.now.utc.to_i
56
88
  AccessToken.collection.update({ :_id=>token }, { :$set=>{ :revoked=>revoked } })
57
89
  end
58
90
 
@@ -18,7 +18,8 @@ module Rack
18
18
  # and any state value to pass back in that redirect.
19
19
  def create(client_id, scope, redirect_uri, response_type, state)
20
20
  fields = { :client_id=>BSON::ObjectId(client_id.to_s), :scope=>scope, :redirect_uri=>redirect_uri, :state=>state,
21
- :response_type=>response_type, :created_at=>Time.now.utc, :grant_code=>nil, :authorized_at=>nil, :revoked=>nil }
21
+ :response_type=>response_type, :created_at=>Time.now.utc.to_i, :grant_code=>nil,
22
+ :authorized_at=>nil, :revoked=>nil }
22
23
  fields[:_id] = collection.insert(fields)
23
24
  Server.new_instance self, fields
24
25
  end
@@ -56,7 +57,7 @@ module Rack
56
57
  def grant!(identity)
57
58
  raise ArgumentError, "Must supply a identity" unless identity
58
59
  return if revoked
59
- self.authorized_at = Time.now.utc
60
+ self.authorized_at = Time.now.utc.to_i
60
61
  if response_type == "code" # Requested authorization code
61
62
  access_grant = AccessGrant.create(identity, scope, client_id, redirect_uri)
62
63
  self.grant_code = access_grant.code
@@ -71,7 +72,7 @@ module Rack
71
72
 
72
73
  # Deny access.
73
74
  def deny!
74
- self.authorized_at = Time.now.utc
75
+ self.authorized_at = Time.now.utc.to_i
75
76
  self.class.collection.update({ :_id=>id }, { :$set=>{ :authorized_at=>authorized_at } })
76
77
  end
77
78
 
@@ -25,7 +25,8 @@ module Rack
25
25
  def create(args)
26
26
  redirect_uri = Server::Utils.parse_redirect_uri(args[:redirect_uri]).to_s if args[:redirect_uri]
27
27
  fields = { :secret=>Server.secure_random, :display_name=>args[:display_name], :link=>args[:link],
28
- :image_url=>args[:image_url], :redirect_uri=>redirect_uri, :created_at=>Time.now.utc, :revoked=>nil }
28
+ :image_url=>args[:image_url], :redirect_uri=>redirect_uri, :created_at=>Time.now.utc.to_i,
29
+ :revoked=>nil }
29
30
  fields[:_id] = collection.insert(fields)
30
31
  Server.new_instance self, fields
31
32
  end
@@ -38,6 +39,12 @@ module Rack
38
39
  Server.new_instance self, collection.find_one({ :display_name=>field }) || collection.find_one({ :link=>field })
39
40
  end
40
41
 
42
+ # Returns all the clients in the database, sorted alphabetically.
43
+ def all
44
+ collection.find({}, { :sort=>[[:display_name, Mongo::ASCENDING]] }).
45
+ map { |fields| Server.new_instance self, fields }
46
+ end
47
+
41
48
  def collection
42
49
  Server.database["oauth2.clients"]
43
50
  end
@@ -65,13 +72,19 @@ module Rack
65
72
  # Revoke all authorization requests, access grants and access tokens for
66
73
  # this client. Ward off the evil.
67
74
  def revoke!
68
- self.revoked = Time.now.utc
75
+ self.revoked = Time.now.utc.to_i
69
76
  Client.collection.update({ :_id=>id }, { :$set=>{ :revoked=>revoked } })
70
77
  AuthRequest.collection.update({ :client_id=>id }, { :$set=>{ :revoked=>revoked } })
71
78
  AccessGrant.collection.update({ :client_id=>id }, { :$set=>{ :revoked=>revoked } })
72
79
  AccessToken.collection.update({ :client_id=>id }, { :$set=>{ :revoked=>revoked } })
73
80
  end
74
81
 
82
+ def update(args)
83
+ fields = [:display_name, :link, :image_url].inject({}) { |h,k| v = args[k]; h[k] = v if v; h }
84
+ fields[:redirect_uri] = Server::Utils.parse_redirect_uri(args[:redirect_uri]).to_s if args[:redirect_uri]
85
+ self.class.collection.update({ :_id=>id }, { :$set=>fields })
86
+ end
87
+
75
88
  Server.create_indexes do
76
89
  # For quickly returning clients sorted by display name, or finding
77
90
  # client from a URL.
@@ -2,7 +2,6 @@ require "rack/oauth2/models"
2
2
  require "rack/oauth2/server/errors"
3
3
  require "rack/oauth2/server/utils"
4
4
  require "rack/oauth2/server/helper"
5
- require "rack/oauth2/server/version"
6
5
 
7
6
 
8
7
  module Rack
@@ -11,6 +10,9 @@ module Rack
11
10
  # Implements an OAuth 2 Authorization Server, based on http://tools.ietf.org/html/draft-ietf-oauth-v2-10
12
11
  class Server
13
12
 
13
+ # Same as gem version number.
14
+ VERSION = IO.read(::File.expand_path("../../../VERSION", ::File.dirname(__FILE__)))
15
+
14
16
  class << self
15
17
  # Return AuthRequest from authorization request handle.
16
18
  def get_auth_request(authorization)
@@ -46,13 +48,17 @@ module Rack
46
48
  # - :authorize_path -- Path for requesting end-user authorization. By
47
49
  # convention defaults to /oauth/authorize.
48
50
  # - :database -- Mongo::DB instance.
51
+ # - :host -- Only check requests sent to this host.
52
+ # - :path -- Only check requests for resources under this path.
53
+ # - :param_authentication -- If true, supports authentication using
54
+ # query/form parameters.
49
55
  # - :realm -- Authorization realm that will show up in 401 responses.
50
56
  # Defaults to use the request host name.
51
57
  # - :scopes -- Array listing all supported scopes, e.g. %w{read write}.
52
58
  # - :logger -- The logger to use. Under Rails, defaults to use the Rails
53
59
  # logger. Will use Rack::Logger if available.
54
60
  Options = Struct.new(:access_token_path, :authenticator, :authorization_types,
55
- :authorize_path, :database, :realm, :scopes, :logger)
61
+ :authorize_path, :database, :host, :param_authentication, :path, :realm, :scopes, :logger)
56
62
 
57
63
  def initialize(app, options = Options.new, &authenticator)
58
64
  @app = app
@@ -61,76 +67,83 @@ module Rack
61
67
  @options.access_token_path ||= "/oauth/access_token"
62
68
  @options.authorize_path ||= "/oauth/authorize"
63
69
  @options.authorization_types ||= %w{code token}
70
+ @options.param_authentication = false
64
71
  end
65
72
 
66
73
  # @see Options
67
74
  attr_reader :options
68
75
 
69
76
  def call(env)
70
- # Use options.database if specified.
71
- org_database, Server.database = Server.database, options.database || Server.database
72
- logger = options.logger || env["rack.logger"]
73
77
  request = OAuthRequest.new(env)
78
+ return @app.call(env) if options.host && options.host != request.host
79
+ return @app.call(env) if options.path && request.path.index(options.path) != 0
74
80
 
75
- # 3. Obtaining End-User Authorization
76
- # Flow starts here.
77
- return request_authorization(request, logger) if request.path == options.authorize_path
78
- # 4. Obtaining an Access Token
79
- return respond_with_access_token(request, logger) if request.path == options.access_token_path
80
-
81
- # 5. Accessing a Protected Resource
82
- if request.authorization
83
- # 5.1.1. The Authorization Request Header Field
84
- token = request.credentials if request.oauth?
85
- else
86
- # 5.1.2. URI Query Parameter
87
- # 5.1.3. Form-Encoded Body Parameter
88
- token = request.GET["oauth_token"] || request.POST["oauth_token"]
89
- end
81
+ begin
82
+ # Use options.database if specified.
83
+ org_database, Server.database = Server.database, options.database || Server.database
84
+ logger = options.logger || env["rack.logger"]
90
85
 
91
- if token
92
- begin
93
- access_token = AccessToken.from_token(token)
94
- raise InvalidTokenError if access_token.nil? || access_token.revoked
95
- raise ExpiredTokenError if access_token.expires_at && access_token.expires_at <= Time.now.utc
96
- request.env["oauth.access_token"] = token
97
- request.env["oauth.identity"] = access_token.identity
98
- logger.info "Authorized #{access_token.identity}" if logger
99
- rescue Error=>error
100
- # 5.2. The WWW-Authenticate Response Header Field
101
- logger.info "HTTP authorization failed #{error.code}" if logger
102
- return unauthorized(request, error)
103
- rescue =>ex
104
- logger.info "HTTP authorization failed #{ex.message}" if logger
105
- return unauthorized(request)
86
+ # 3. Obtaining End-User Authorization
87
+ # Flow starts here.
88
+ return request_authorization(request, logger) if request.path == options.authorize_path
89
+ # 4. Obtaining an Access Token
90
+ return respond_with_access_token(request, logger) if request.path == options.access_token_path
91
+
92
+ # 5. Accessing a Protected Resource
93
+ if request.authorization
94
+ # 5.1.1. The Authorization Request Header Field
95
+ token = request.credentials if request.oauth?
96
+ elsif options.param_authentication && !request.GET["oauth_verifier"] # Ignore OAuth 1.0 callbacks
97
+ # 5.1.2. URI Query Parameter
98
+ # 5.1.3. Form-Encoded Body Parameter
99
+ token = request.GET["oauth_token"] || request.POST["oauth_token"]
106
100
  end
107
101
 
108
- # We expect application to use 403 if request has insufficient scope,
109
- # and return appropriate WWW-Authenticate header.
110
- response = @app.call(env)
111
- if response[0] == 403
112
- scope = response[1]["oauth.no_scope"] || ""
113
- scope = scope.join(" ") if scope.respond_to?(:join)
114
- challenge = 'OAuth realm="%s", error="insufficient_scope", scope="%s"' % [(options.realm || request.host), scope]
115
- return [403, { "WWW-Authenticate"=>challenge }, []]
116
- else
117
- return response
118
- end
119
- else
120
- response = @app.call(env)
121
- if response[1] && response[1]["oauth.no_access"]
122
- # OAuth access required.
123
- return unauthorized(request)
124
- elsif response[1] && response[1]["oauth.authorization"]
125
- # 3. Obtaining End-User Authorization
126
- # Flow ends here.
127
- return authorization_response(response, logger)
102
+ if token
103
+ begin
104
+ access_token = AccessToken.from_token(token)
105
+ raise InvalidTokenError if access_token.nil? || access_token.revoked
106
+ raise ExpiredTokenError if access_token.expires_at && access_token.expires_at <= Time.now.utc
107
+ request.env["oauth.access_token"] = token
108
+ request.env["oauth.identity"] = access_token.identity
109
+ logger.info "Authorized #{access_token.identity}" if logger
110
+ rescue Error=>error
111
+ # 5.2. The WWW-Authenticate Response Header Field
112
+ logger.info "HTTP authorization failed #{error.code}" if logger
113
+ return unauthorized(request, error)
114
+ rescue =>ex
115
+ logger.info "HTTP authorization failed #{ex.message}" if logger
116
+ return unauthorized(request)
117
+ end
118
+
119
+ # We expect application to use 403 if request has insufficient scope,
120
+ # and return appropriate WWW-Authenticate header.
121
+ response = @app.call(env)
122
+ if response[0] == 403
123
+ scope = response[1]["oauth.no_scope"] || ""
124
+ scope = scope.join(" ") if scope.respond_to?(:join)
125
+ challenge = 'OAuth realm="%s", error="insufficient_scope", scope="%s"' % [(options.realm || request.host), scope]
126
+ response[1]["WWW-Authenticate"] = challenge
127
+ return response
128
+ else
129
+ return response
130
+ end
128
131
  else
129
- return response
132
+ response = @app.call(env)
133
+ if response[1] && response[1].delete("oauth.no_access")
134
+ # OAuth access required.
135
+ return unauthorized(request)
136
+ elsif response[1] && response[1]["oauth.authorization"]
137
+ # 3. Obtaining End-User Authorization
138
+ # Flow ends here.
139
+ return authorization_response(response, logger)
140
+ else
141
+ return response
142
+ end
130
143
  end
144
+ ensure
145
+ Server.database = org_database
131
146
  end
132
- ensure
133
- Server.database = org_database
134
147
  end
135
148
 
136
149
  protected