rack-oauth2-server 1.3.0 → 1.3.1

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/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ 2010-11-07 version 1.3.1
2
+
3
+ Added command line tool, helps you get started and setup:
4
+ $ oauth2-server setup --db my_db
5
+
6
+ Added a touch of color to the UI and ability to delete a client.
7
+
8
+ You can not sign out of the Web console.
9
+
10
+
1
11
  2010-11-07 version 1.3.0
2
12
 
3
13
  Added OAuth authorization console.
data/Gemfile CHANGED
@@ -15,6 +15,5 @@ group :test do
15
15
  gem "rails", "~>2.3"
16
16
  gem "shoulda"
17
17
  gem "sinatra"
18
- gem "therubyracer", "~>0.8.0.pre"
19
18
  gem "timecop"
20
19
  end
data/README.rdoc CHANGED
@@ -281,7 +281,13 @@ Clients can also register a redirect URL. This is optional but highly
281
281
  recommended for better security, preventing other applications from hijackin
282
282
  the client's ID/secret.
283
283
 
284
- For example:
284
+ You can register clients using the command line tool +oauth2-server+:
285
+
286
+ $ oauth2-server register --db my_db
287
+
288
+ Or you can register clients using the Web-based OAuth console, see below.
289
+
290
+ Programatically, registering a new client is as simple as:
285
291
 
286
292
  $ ./script/console
287
293
  Loading development environment (Rails 2.3.8)
@@ -332,6 +338,10 @@ when making POST request, as a form field:
332
338
  $ curl -i http://localhost:3000/api/read?oauth_token=e57807eb99f8c29f60a27a75a80fec6e
333
339
  $ curl -i http://localhost:3000/api/update -F name=Superman -F oauth_token=e57807eb99f8c29f60a27a75a80fec6e
334
340
 
341
+ You'll need to set the option +param_authentication+ to true. Watch out, since
342
+ this query parameter could conflict with OAuth 1.0 authorization responses that
343
+ also use +oauth_token+ for a different purpose.
344
+
335
345
  Here's a neat trick. You can create a +.curlrc+ file and load it using the +-K+ option:
336
346
 
337
347
  $ cat .curlrc
@@ -344,6 +354,67 @@ server you +curl+. Useful for development, testing, just don't use it with any
344
354
  production access tokens.
345
355
 
346
356
 
357
+ == OAuth Web Console
358
+
359
+ We haz it, and it's pretty rad:
360
+
361
+ http://github.com/downloads/flowtown/rack-oauth2-server/OAuth%20Console%20-%20All%20Clients.png
362
+
363
+ To get the console running, you'll need to do the following. First, you'll need
364
+ to register a new client application that can access the OAuth console, with a
365
+ redirect_uri that points to where you plan the Web console to live. This URL
366
+ must end with "/admin", for example, "http://example.com/oauth/admin".
367
+
368
+ The easiest way to do this is to run the +oauth2-sever+ command line tool:
369
+
370
+ $ oauth2-server setup --db my_db
371
+
372
+ Next, in your application, make sure to ONLY AUTHORIZE ADMINISTRATORS to access
373
+ the console, by granting them access to the +oauth-admin+ scope. For example:
374
+
375
+ def authorize
376
+ # Only admins allowed to authorize the scope oauth-admin
377
+ if oauth.scope.include?("oauth-admin") && !current_user.admin?
378
+ oauth.deny! oauth.authorization
379
+ end
380
+ end
381
+
382
+ Make sure you do that, or you'll allow anyone access to the OAuth Web console.
383
+
384
+ Next, mount the OAuth Web console as part of your application, and feed it the
385
+ client ID/secret. For example, for Rails add this to +config/environment.rb+:
386
+
387
+ Rails::Initializer.run do |config|
388
+ . . .
389
+ config.middleware.use Rack::OAuth2::Server::Admin.mount
390
+ Rack::OAuth2::Server::Admin.set :client_id, "4dca20453e4859cb000007"
391
+ Rack::OAuth2::Server::Admin.set :client_secret, "981fa734e110496fcf667cbf52fbaf03"
392
+ end
393
+
394
+ For Sinatra, Padrino and other Rack-based applications, you'll want to mount
395
+ like so (e.g. in +config.ru+):
396
+
397
+ Rack::Builder.new do
398
+ map("/oauth/admin") { run Rack::OAuth2::Server::Admin }
399
+ map("/") { run MyApp }
400
+ end
401
+ Rack::OAuth2::Server::Admin.set :client_id, "4dca20453e4859cb000007"
402
+ Rack::OAuth2::Server::Admin.set :client_secret, "981fa734e110496fcf667cbf52fbaf03"
403
+
404
+ Next, open your browser to http://example.com/oauth/admin, or wherever you
405
+ mounted the console.
406
+
407
+ The OAuth Web console is a single-page client application that operates by
408
+ accessing the OAuth API. The API is mounted at /oauth/admin/api (basically /api
409
+ relative to the console), you can access it yourself if you have an access
410
+ token with the scope +oauth-admin+.
411
+
412
+ To get the access token, the console first redirects you to the path
413
+ +/oauth/authorize+, which it expects to be the OAuth 2.0 authorization
414
+ endpoint. If you need to use a different endpoint, simply set the :authorize
415
+ option to that URL.
416
+
417
+
347
418
  == Mandatory ASCII Diagram
348
419
 
349
420
  This is briefly what the authorization flow looks like, how the workload is
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.3.1
data/bin/oauth2-server ADDED
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
+ require "rack/oauth2/models"
4
+ require "uri"
5
+ include Rack::OAuth2
6
+
7
+
8
+ if (i = ARGV.index("--db")) && ARGV[i+1]
9
+ url = ARGV[i + 1]
10
+ uri = URI.parse(url)
11
+ uri = URI.parse("mongo://#{url}") if uri.opaque
12
+ db = uri.path.sub(/^\//, "")
13
+ conn = Mongo::Connection.new(uri.host, uri.port)
14
+ coon.add_auth db, uri.user, uri.password if uri.user
15
+ Server.database = conn[db]
16
+ ARGV[i,2] = []
17
+ end
18
+
19
+
20
+ case ARGV[0]
21
+ when "list"
22
+ fail "No database. Use the --db option to tell us which database to use" unless Server.database
23
+ Server::Client.all.each do |client|
24
+ next if client.revoked
25
+ print "%-30s\t%s\n" % [client.display_name, client.link]
26
+ print " ID %s\tSecret %s\n" % [client.id, client.secret]
27
+ print "\n"
28
+ end
29
+ when "register"
30
+ fail "No database. Use the --db option to tell us which database to use" unless Server.database
31
+ begin
32
+ print "Application name:\t"
33
+ display_name = $stdin.gets
34
+ print "Application URL:\t"
35
+ link = $stdin.gets
36
+ print "Redirect URI:\t\t"
37
+ redirect_uri = $stdin.gets
38
+ client = Server::Client.create(:display_name=>display_name, :link=>link, :redirect_uri=>redirect_uri)
39
+ rescue
40
+ puts "\nFailed to register client: #{$1}"
41
+ exit -1
42
+ end
43
+ puts "Registered #{client.display_name}"
44
+ puts "ID\t#{client.id}"
45
+ puts "Secret\t#{client.secret}"
46
+ when "setup"
47
+ fail "No database. Use the --db option to tell us which database to use" unless Server.database
48
+ puts "Where would you mount the Web console? This is a URL that must end with /admin,"
49
+ puts "for example, http://example.com/oauth/admin"
50
+ uri = URI.parse($stdin.gets)
51
+ begin
52
+ uri.normalize!
53
+ fail "No an HTTP/S URL" unless uri.absolute? && %{http https}.include?(uri.scheme)
54
+ fail "Path must end with /admin" unless uri.path[/\/admin$/]
55
+ client = Server::Client.create(:display_name=>"OAuth Console", :link=>uri.to_s,
56
+ :image_url=>"#{uri.to_s}/images/oauth-2.png", :redirect_uri=>uri.to_s)
57
+ rescue
58
+ puts "\nFailed to register client: #{$!}"
59
+ exit -1
60
+ end
61
+ print <<-TEXT
62
+ Make sure you ONLY authorize administrators to use the oauth-admin scope.
63
+ For example:
64
+
65
+ def authorize
66
+ # Only admins allowed to authorize the scope oauth-admin
67
+ if oauth.scope.include?("oauth-admin") && !current_user.admin?
68
+ oauth.deny! oauth.authorization
69
+ end
70
+ end
71
+
72
+ Rails 2.x, add the following to config/environment.rb:
73
+
74
+ config.middleware.use Rack::OAuth2::Server::Admin.mount "#{uri.path}"
75
+ Rack::OAuth2::Server::Admin.set :client_id, "#{client.id}"
76
+ Rack::OAuth2::Server::Admin.set :client_secret, "#{client.secret}"
77
+
78
+ Sinatra, Padrino and other Rack applications, mount the console:
79
+
80
+ Rack::Builder.new do
81
+ map("#{uri.path}") { run Rack::OAuth2::Server::Admin }
82
+ map("/") { run MyApp }
83
+ end
84
+ Rack::OAuth2::Server::Admin.set :client_id, "#{client.id}"
85
+ Rack::OAuth2::Server::Admin.set :client_secret, "#{client.secret}"
86
+
87
+ The console will authorize access by redirecting to
88
+ https://#{uri.host}/oauth/authorize
89
+
90
+ If this is not your OAuth 2.0 authorization endpoint, you can change it by
91
+ setting the :authorize option.
92
+ TEXT
93
+ else
94
+ print <<-TEXT
95
+ Usage: oauth2-server [options] COMMAND [args]
96
+
97
+ Commands:
98
+ list Lists all active clients
99
+ register Register a new client application
100
+ setup Create new admin account and help you
101
+ setup the OAuth Web console
102
+
103
+ Options:
104
+ --db database Database name or connection URL
105
+ TEXT
106
+ exit -1
107
+ end
@@ -1,5 +1,9 @@
1
+ html {
2
+ background: #eee;
3
+ }
1
4
  body {
2
5
  margin: 0;
6
+ padding: 0;
3
7
  width: 100%;
4
8
  font: 12pt "Helvetica", "Lucida Sans", "Verdana";
5
9
  }
@@ -75,20 +79,45 @@ button:active { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(0
75
79
  }
76
80
 
77
81
  #header {
78
- margin: 0 2em;
82
+ margin: 0;
83
+ padding: 2em;
84
+ background: #fff;
85
+ background: -webkit-gradient(linear, left top, left bottom, from(#ccf), to(#fff));
86
+ background: -moz-linear-gradient(top, #ccf, #fff);
79
87
  }
80
88
  #header .title {
81
- font-size: 1.4em;
89
+ font-size: 18pt;
82
90
  font-weight: bold;
83
- color: #000;
84
- text-decoration: none;
85
- margin: 0.6em 0;
86
91
  display: block;
87
92
  text-align: right;
93
+ line-height: 32px;
94
+ }
95
+ #header .title a {
96
+ color: #000;
97
+ text-decoration: none;
98
+ }
99
+ #header .title img {
100
+ width: 32px;
101
+ height: 32px;
102
+ vertical-align: bottom;
103
+ }
104
+ #header .signout {
105
+ float: right;
106
+ font-size: 11pt;
88
107
  }
89
108
 
90
109
  #main {
91
- margin: 0 2em;
110
+ padding: 0 2em 4em 2em;
111
+ background: #fff;
112
+ }
113
+ #footer {
114
+ background: #eee;
115
+ background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#eee));
116
+ background: -moz-linear-gradient(top, #fff, #eee);
117
+ color: #666;
118
+ border-top: 1px solid transparent;
119
+ font-size: 90%;
120
+ padding: 0 2em 2em 2em;
92
121
  }
93
122
 
94
123
  table {
@@ -97,6 +126,7 @@ table {
97
126
  empty-cells: show;
98
127
  border-collapse: separate;
99
128
  border-spacing: 0px;
129
+ margin-top: 2em;
100
130
  }
101
131
  table th {
102
132
  text-align: left;
@@ -177,7 +207,7 @@ table.tokens td.scope {
177
207
 
178
208
  .badges {
179
209
  list-style: none;
180
- margin: 1.1em 0;
210
+ margin: 0;
181
211
  padding: 0;
182
212
  text-align: right;
183
213
  width: 100%;
@@ -211,13 +241,19 @@ table.tokens td.scope {
211
241
  height: 24px;
212
242
  vertical-align: bottom;
213
243
  }
214
- .client .details a[rel=edit] {
215
- margin: 0 0.3em 0 1em;
244
+ .client .details a {
245
+ margin: 0 0.3em 0 0;
216
246
  }
217
247
  .client .details .meta {
218
248
  color: #888;
219
249
  font-size: 10pt;
220
250
  }
251
+
252
+ .client.new, .client.edit {
253
+ margin: 0;
254
+ padding: 1em;
255
+ border: 1px solid #eee;
256
+ }
221
257
  .client.new>#image, .client.edit>#image {
222
258
  float: left;
223
259
  margin: 0 12px 0 0;
@@ -64,6 +64,20 @@ Sammy("#main", function(app) {
64
64
  }
65
65
  })
66
66
  });
67
+ // Delete/revoke client
68
+ this.del("#/client/:id", function(context) {
69
+ $.ajax({ type: "post", url: api + "/client/" + context.params.id,
70
+ data: { _method: "delete" },
71
+ success: function() { context.redirect("#/") }
72
+ });
73
+ });
74
+ this.post("#/client/:id/revoke", function(context) {
75
+ $.post(api + "/client/" + context.params.id + "/revoke", function() { app.refresh() });
76
+ });
77
+ // Revoke token
78
+ this.post("#/token/:id/revoke", function(context) {
79
+ $.post(api + "/token/" + context.params.id + "/revoke", function() { app.refresh() });
80
+ });
67
81
  // View single client
68
82
  this.get("#/client/:id", function(context) {
69
83
  $.getJSON(api + "/client/" + context.params.id, function(client) {
@@ -101,29 +115,22 @@ Sammy("#main", function(app) {
101
115
  }
102
116
  });
103
117
  });
118
+ // Signout
119
+ this.get("#/signout", function(context) {
120
+ app.session("oauth.token", null);
121
+ context.redirect(document.location.protocol + "//" + document.location.host);
122
+ });
104
123
 
105
- // Client/token revoke buttons do this.
106
- $("a[data-method=post]").live("click", function(evt) {
124
+ // Links that use forms for various methods (i.e. post, delete).
125
+ $("a[data-method]").live("click", function(evt) {
107
126
  evt.preventDefault();
108
127
  var link = $(this);
109
128
  if (link.attr("data-confirm") && !confirm(link.attr("data-confirm")))
110
- return;
111
- $.post(link.attr("href"), function(success) {
112
- app.trigger("notice", "Revoked!");
113
- app.refresh();
114
- });
115
- });
116
- // Link to reveal/hide client ID/secret
117
- $("td.secrets a[rel=toggle]").live("click", function(evt) {
118
- evt.preventDefault();
119
- var dl = $(this).next("dl");
120
- if (dl.is(":visible")) {
121
- $(this).html("Reveal");
122
- dl.hide();
123
- } else {
124
- $(this).html("Hide");
125
- dl.show();
126
- }
129
+ return fasle;
130
+ var method = link.attr("data-method") || "get",
131
+ form = $("<form>", { style: "display:none", method: method, action: link.attr("href") });
132
+ app.$element().append(form);
133
+ form.submit();
127
134
  });
128
135
  // Error/notice at top of screen
129
136
  var noticeTimeout;
@@ -3,8 +3,9 @@
3
3
  <a href="${link}" class="name">{{if imageUrl}}<img src="${imageUrl}">{{/if}} ${displayName}</a>
4
4
  <a href="#/client/${id}/edit" rel="edit">Edit</a>
5
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>
6
+ <a href="#/client/${id}/revoke" data-method="post" data-confirm="There is no undo. Are you really really sure?" rel="revoke">Revoke</a>
7
7
  {{/if}}
8
+ <a href="#/client/${id}" data-method="delete" data-confirm="There is no undo. Are you really really sure?" rel="delete">Delete</a>
8
9
  <div class="meta">
9
10
  Created {{html $.shortdate(revoked)}}
10
11
  {{if revoked}}Revoked {{html $.shortdate(revoked)}}{{/if}}
@@ -34,7 +35,7 @@
34
35
  {{if revoked}}
35
36
  {{html $.shortdate(revoked)}}
36
37
  {{else}}
37
- <a href="${revoke}" data-method="post" data-confirm="Are you sure?" rel="revoke">Revoke</a>
38
+ <a href="#/token/${token}/revoke" data-method="post" data-confirm="Are you sure?" rel="revoke">Revoke</a>
38
39
  {{/if}}
39
40
  </td>
40
41
  </tr>
@@ -17,7 +17,7 @@
17
17
  <td class="name">
18
18
  <a href="#/client/${id}">
19
19
  {{if imageUrl}}<img src="${imageUrl}">{{/if}}
20
- ${displayName}
20
+ ${displayName.trim() == "" ? "untitled" : displayName}
21
21
  </a>
22
22
  </td>
23
23
  <td class="secrets">
@@ -34,3 +34,16 @@
34
34
  {{/each}}
35
35
  </table>
36
36
  </div>
37
+ <script type="text/javascript">
38
+ $("td.secrets a[rel=toggle]").click(function(evt) {
39
+ evt.preventDefault();
40
+ var dl = $(this).next("dl");
41
+ if (dl.is(":visible")) {
42
+ $(this).html("Reveal");
43
+ dl.hide();
44
+ } else {
45
+ $(this).html("Hide");
46
+ dl.show();
47
+ }
48
+ });
49
+ </script>
@@ -12,11 +12,20 @@
12
12
  <script src="admin/js/sammy.title.js" type="text/javascript"></script>
13
13
  <script src="admin/js/underscore.js" type="text/javascript"></script>
14
14
  <script src="admin/js/application.js" type="text/javascript"></script>
15
+ <link rel="shortcut icon" href="admin/images/oauth-2.png">
15
16
  </head>
16
17
  <body>
17
18
  <div id="notice" style="display:none"></div>
18
- <div id="header"><a href="#/" class="title">OAuth Console</a></div>
19
+ <div id="header">
20
+ <div class="title">
21
+ <a href="#/"><img src="admin/images/oauth-2.png"> OAuth Console</a>
22
+ </div>
23
+ <a href="#/signout" class="signout">Sign out</a>
24
+ </div>
19
25
  <div id="main"></div>
26
+ <div id="footer">
27
+ <p>Powered by <a href="http://github.com/flowtown/rack-oauth2-server">Rack::OAuth2::Server</a></p>
28
+ </div>
20
29
  <script>
21
30
  var loading = new Image();
22
31
  loading.src = "admin/images/loading.gif";