rack-oauth2-server 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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";