browserid-provider 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README.md +90 -0
- data/Rakefile +1 -0
- data/app/assets/browserid/404.html.erb +40 -0
- data/app/assets/browserid/bootstrap.css +373 -0
- data/app/assets/browserid/bootstrap.min.css +72 -0
- data/app/assets/browserid/provision.html.erb +53 -0
- data/browserid-provider.gemspec +23 -0
- data/lib/browserid-provider/config.rb +76 -0
- data/lib/browserid-provider/engine.rb +6 -0
- data/lib/browserid-provider/identity.rb +44 -0
- data/lib/browserid-provider/provider.rb +111 -0
- data/lib/browserid-provider/railtie.rb +8 -0
- data/lib/browserid-provider/template.rb +24 -0
- data/lib/browserid-provider/version.rb +3 -0
- data/lib/browserid-provider/view_helpers.rb +22 -0
- data/lib/browserid-provider.rb +10 -0
- data/test/browserid-provider.rb +81 -0
- metadata +129 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# A Rack BrowserID Provider
|
2
|
+
|
3
|
+
Become a Mozilla BrowserID Primary Identity Provider.
|
4
|
+
|
5
|
+
This is a Rack middleware for providing the BrowserID Primary Identity
|
6
|
+
service. I have so far tested this only with Ruby on Rails.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'browserid-provider'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install browserid-provider
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
In you Rails app config/application.rb, add:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
config.middleware.use BrowserID::Provider({:authentication_path => "/login" })
|
28
|
+
```
|
29
|
+
|
30
|
+
The default setup relies on Warden to see which user is logged in. This
|
31
|
+
can easily be customized to fit any middleware function.
|
32
|
+
|
33
|
+
The available configuration options are the following:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
#
|
37
|
+
# authentication_path Where to redirect users for login
|
38
|
+
# defaults to: "/users/sign_in" (Devise default)
|
39
|
+
#
|
40
|
+
# provision_path What HTTP path to deliver provisioning from
|
41
|
+
# defaults to: "/browserid/provision"
|
42
|
+
# certify_path What HTTP path to deliver certifying from
|
43
|
+
# defaults to: "/browserid/certify"
|
44
|
+
# whoami_path What HTTP path to serve user credentials at
|
45
|
+
# defaults to: "/browserid/whoami"
|
46
|
+
#
|
47
|
+
# whoami What function to call for the current user object (must respond to :email method)
|
48
|
+
# defaults to: "@env['warden'].user"
|
49
|
+
#
|
50
|
+
# private_key_path Where is the BrowserID OpenSSL private key located
|
51
|
+
# defaults to: "config/browserid_provider.pem"
|
52
|
+
#
|
53
|
+
# The "/.well-known/browserid" path is required from the BrowserID spec and used here.
|
54
|
+
#
|
55
|
+
# browserid_url Which BrowserID server to use, ca be one of the following:
|
56
|
+
# * dev.diresworb.org for development (default)
|
57
|
+
# * diresworb.org for beta
|
58
|
+
# * browserid.org for production
|
59
|
+
#
|
60
|
+
# server_name The domain name we are providing BrowserID for (default to example.org)
|
61
|
+
#
|
62
|
+
```
|
63
|
+
|
64
|
+
The client side is JavaScript enabled. For Rails use:
|
65
|
+
|
66
|
+
```erb
|
67
|
+
<%= browserid_authentication_tag %>
|
68
|
+
<!-- Enable BrowserID authentication API on the form #new_user -->
|
69
|
+
<%= enable_browserid_javascript_tag "new_user" %>
|
70
|
+
```
|
71
|
+
|
72
|
+
In your login form, add a cancel button like this:
|
73
|
+
|
74
|
+
```erb
|
75
|
+
<%= button_to_function "Cancel", "navigator.id.cancelAuthentication()" %>
|
76
|
+
```
|
77
|
+
|
78
|
+
Without Rails view helpers (in any framework), you can do:
|
79
|
+
|
80
|
+
```javascript
|
81
|
+
$('form#new_user').bind('ajax:success', function(data, status, xhr) { navigator.id.completeAuthentication() })
|
82
|
+
```
|
83
|
+
|
84
|
+
## Contributing
|
85
|
+
|
86
|
+
1. Fork it
|
87
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
89
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
90
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<title>404 - BrowserID</title>
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
+
<meta name="description" content="">
|
8
|
+
<meta name="author" content="">
|
9
|
+
|
10
|
+
<!-- Le styles -->
|
11
|
+
<style>
|
12
|
+
<%= BrowserID::Template.css_styles %>
|
13
|
+
body {
|
14
|
+
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
|
15
|
+
}
|
16
|
+
.hero-unit img {
|
17
|
+
float: right;
|
18
|
+
}
|
19
|
+
</style>
|
20
|
+
|
21
|
+
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
|
22
|
+
<!--[if lt IE 9]>
|
23
|
+
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
24
|
+
<![endif]-->
|
25
|
+
</head>
|
26
|
+
|
27
|
+
<body>
|
28
|
+
<div class="hero-unit">
|
29
|
+
<h1>404</h1>
|
30
|
+
<p>No BrowserID content found here.
|
31
|
+
<img src="https://github.com/mozilla/browserid/raw/dev/resources/assets/browserID-366x72.png" alt="BrowserID" />
|
32
|
+
</p>
|
33
|
+
<p>
|
34
|
+
<a class="btn btn-primary btn-large">
|
35
|
+
Learn more
|
36
|
+
</a>
|
37
|
+
</p>
|
38
|
+
</div>
|
39
|
+
</body>
|
40
|
+
</html>
|
@@ -0,0 +1,373 @@
|
|
1
|
+
/*!
|
2
|
+
* Bootstrap v2.0.2
|
3
|
+
*
|
4
|
+
* Copyright 2012 Twitter, Inc
|
5
|
+
* Licensed under the Apache License v2.0
|
6
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
*
|
8
|
+
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
9
|
+
*/
|
10
|
+
.clearfix {
|
11
|
+
*zoom: 1;
|
12
|
+
}
|
13
|
+
.clearfix:before,
|
14
|
+
.clearfix:after {
|
15
|
+
display: table;
|
16
|
+
content: "";
|
17
|
+
}
|
18
|
+
.clearfix:after {
|
19
|
+
clear: both;
|
20
|
+
}
|
21
|
+
.hide-text {
|
22
|
+
overflow: hidden;
|
23
|
+
text-indent: 100%;
|
24
|
+
white-space: nowrap;
|
25
|
+
}
|
26
|
+
.input-block-level {
|
27
|
+
display: block;
|
28
|
+
width: 100%;
|
29
|
+
min-height: 28px;
|
30
|
+
/* Make inputs at least the height of their button counterpart */
|
31
|
+
|
32
|
+
/* Makes inputs behave like true block-level elements */
|
33
|
+
|
34
|
+
-webkit-box-sizing: border-box;
|
35
|
+
-moz-box-sizing: border-box;
|
36
|
+
-ms-box-sizing: border-box;
|
37
|
+
box-sizing: border-box;
|
38
|
+
}
|
39
|
+
article,
|
40
|
+
aside,
|
41
|
+
details,
|
42
|
+
figcaption,
|
43
|
+
figure,
|
44
|
+
footer,
|
45
|
+
header,
|
46
|
+
hgroup,
|
47
|
+
nav,
|
48
|
+
section {
|
49
|
+
display: block;
|
50
|
+
}
|
51
|
+
audio,
|
52
|
+
canvas,
|
53
|
+
video {
|
54
|
+
display: inline-block;
|
55
|
+
*display: inline;
|
56
|
+
*zoom: 1;
|
57
|
+
}
|
58
|
+
audio:not([controls]) {
|
59
|
+
display: none;
|
60
|
+
}
|
61
|
+
html {
|
62
|
+
font-size: 100%;
|
63
|
+
-webkit-text-size-adjust: 100%;
|
64
|
+
-ms-text-size-adjust: 100%;
|
65
|
+
}
|
66
|
+
a:focus {
|
67
|
+
outline: thin dotted #333;
|
68
|
+
outline: 5px auto -webkit-focus-ring-color;
|
69
|
+
outline-offset: -2px;
|
70
|
+
}
|
71
|
+
a:hover,
|
72
|
+
a:active {
|
73
|
+
outline: 0;
|
74
|
+
}
|
75
|
+
sub,
|
76
|
+
sup {
|
77
|
+
position: relative;
|
78
|
+
font-size: 75%;
|
79
|
+
line-height: 0;
|
80
|
+
vertical-align: baseline;
|
81
|
+
}
|
82
|
+
sup {
|
83
|
+
top: -0.5em;
|
84
|
+
}
|
85
|
+
sub {
|
86
|
+
bottom: -0.25em;
|
87
|
+
}
|
88
|
+
img {
|
89
|
+
height: auto;
|
90
|
+
border: 0;
|
91
|
+
-ms-interpolation-mode: bicubic;
|
92
|
+
vertical-align: middle;
|
93
|
+
}
|
94
|
+
button,
|
95
|
+
input,
|
96
|
+
select,
|
97
|
+
textarea {
|
98
|
+
margin: 0;
|
99
|
+
font-size: 100%;
|
100
|
+
vertical-align: middle;
|
101
|
+
}
|
102
|
+
button,
|
103
|
+
input {
|
104
|
+
*overflow: visible;
|
105
|
+
line-height: normal;
|
106
|
+
}
|
107
|
+
button::-moz-focus-inner,
|
108
|
+
input::-moz-focus-inner {
|
109
|
+
padding: 0;
|
110
|
+
border: 0;
|
111
|
+
}
|
112
|
+
button,
|
113
|
+
input[type="button"],
|
114
|
+
input[type="reset"],
|
115
|
+
input[type="submit"] {
|
116
|
+
cursor: pointer;
|
117
|
+
-webkit-appearance: button;
|
118
|
+
}
|
119
|
+
input[type="search"] {
|
120
|
+
-webkit-appearance: textfield;
|
121
|
+
-webkit-box-sizing: content-box;
|
122
|
+
-moz-box-sizing: content-box;
|
123
|
+
box-sizing: content-box;
|
124
|
+
}
|
125
|
+
input[type="search"]::-webkit-search-decoration,
|
126
|
+
input[type="search"]::-webkit-search-cancel-button {
|
127
|
+
-webkit-appearance: none;
|
128
|
+
}
|
129
|
+
textarea {
|
130
|
+
overflow: auto;
|
131
|
+
vertical-align: top;
|
132
|
+
}
|
133
|
+
body {
|
134
|
+
margin: 0;
|
135
|
+
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
136
|
+
font-size: 13px;
|
137
|
+
line-height: 18px;
|
138
|
+
color: #333333;
|
139
|
+
background-color: #ffffff;
|
140
|
+
}
|
141
|
+
a {
|
142
|
+
color: #0088cc;
|
143
|
+
text-decoration: none;
|
144
|
+
}
|
145
|
+
a:hover {
|
146
|
+
color: #005580;
|
147
|
+
text-decoration: underline;
|
148
|
+
}
|
149
|
+
p {
|
150
|
+
margin: 0 0 9px;
|
151
|
+
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
152
|
+
font-size: 13px;
|
153
|
+
line-height: 18px;
|
154
|
+
}
|
155
|
+
p small {
|
156
|
+
font-size: 11px;
|
157
|
+
color: #999999;
|
158
|
+
}
|
159
|
+
.lead {
|
160
|
+
margin-bottom: 18px;
|
161
|
+
font-size: 20px;
|
162
|
+
font-weight: 200;
|
163
|
+
line-height: 27px;
|
164
|
+
}
|
165
|
+
h1,
|
166
|
+
h2,
|
167
|
+
h3,
|
168
|
+
h4,
|
169
|
+
h5,
|
170
|
+
h6 {
|
171
|
+
margin: 0;
|
172
|
+
font-family: inherit;
|
173
|
+
font-weight: bold;
|
174
|
+
color: inherit;
|
175
|
+
text-rendering: optimizelegibility;
|
176
|
+
}
|
177
|
+
h1 small,
|
178
|
+
h2 small,
|
179
|
+
h3 small,
|
180
|
+
h4 small,
|
181
|
+
h5 small,
|
182
|
+
h6 small {
|
183
|
+
font-weight: normal;
|
184
|
+
color: #999999;
|
185
|
+
}
|
186
|
+
h1 {
|
187
|
+
font-size: 30px;
|
188
|
+
line-height: 36px;
|
189
|
+
}
|
190
|
+
h1 small {
|
191
|
+
font-size: 18px;
|
192
|
+
}
|
193
|
+
h2 {
|
194
|
+
font-size: 24px;
|
195
|
+
line-height: 36px;
|
196
|
+
}
|
197
|
+
h2 small {
|
198
|
+
font-size: 18px;
|
199
|
+
}
|
200
|
+
h3 {
|
201
|
+
line-height: 27px;
|
202
|
+
font-size: 18px;
|
203
|
+
}
|
204
|
+
h3 small {
|
205
|
+
font-size: 14px;
|
206
|
+
}
|
207
|
+
h4,
|
208
|
+
h5,
|
209
|
+
h6 {
|
210
|
+
line-height: 18px;
|
211
|
+
}
|
212
|
+
h4 {
|
213
|
+
font-size: 14px;
|
214
|
+
}
|
215
|
+
h4 small {
|
216
|
+
font-size: 12px;
|
217
|
+
}
|
218
|
+
h5 {
|
219
|
+
font-size: 12px;
|
220
|
+
}
|
221
|
+
h6 {
|
222
|
+
font-size: 11px;
|
223
|
+
color: #999999;
|
224
|
+
text-transform: uppercase;
|
225
|
+
}
|
226
|
+
.page-header {
|
227
|
+
padding-bottom: 17px;
|
228
|
+
margin: 18px 0;
|
229
|
+
border-bottom: 1px solid #eeeeee;
|
230
|
+
}
|
231
|
+
.page-header h1 {
|
232
|
+
line-height: 1;
|
233
|
+
}
|
234
|
+
ul,
|
235
|
+
ol {
|
236
|
+
padding: 0;
|
237
|
+
margin: 0 0 9px 25px;
|
238
|
+
}
|
239
|
+
ul ul,
|
240
|
+
ul ol,
|
241
|
+
ol ol,
|
242
|
+
ol ul {
|
243
|
+
margin-bottom: 0;
|
244
|
+
}
|
245
|
+
ul {
|
246
|
+
list-style: disc;
|
247
|
+
}
|
248
|
+
ol {
|
249
|
+
list-style: decimal;
|
250
|
+
}
|
251
|
+
li {
|
252
|
+
line-height: 18px;
|
253
|
+
}
|
254
|
+
ul.unstyled,
|
255
|
+
ol.unstyled {
|
256
|
+
margin-left: 0;
|
257
|
+
list-style: none;
|
258
|
+
}
|
259
|
+
dl {
|
260
|
+
margin-bottom: 18px;
|
261
|
+
}
|
262
|
+
dt,
|
263
|
+
dd {
|
264
|
+
line-height: 18px;
|
265
|
+
}
|
266
|
+
dt {
|
267
|
+
font-weight: bold;
|
268
|
+
line-height: 17px;
|
269
|
+
}
|
270
|
+
dd {
|
271
|
+
margin-left: 9px;
|
272
|
+
}
|
273
|
+
.dl-horizontal dt {
|
274
|
+
float: left;
|
275
|
+
clear: left;
|
276
|
+
width: 120px;
|
277
|
+
text-align: right;
|
278
|
+
}
|
279
|
+
.dl-horizontal dd {
|
280
|
+
margin-left: 130px;
|
281
|
+
}
|
282
|
+
hr {
|
283
|
+
margin: 18px 0;
|
284
|
+
border: 0;
|
285
|
+
border-top: 1px solid #eeeeee;
|
286
|
+
border-bottom: 1px solid #ffffff;
|
287
|
+
}
|
288
|
+
strong {
|
289
|
+
font-weight: bold;
|
290
|
+
}
|
291
|
+
em {
|
292
|
+
font-style: italic;
|
293
|
+
}
|
294
|
+
.muted {
|
295
|
+
color: #999999;
|
296
|
+
}
|
297
|
+
abbr[title] {
|
298
|
+
border-bottom: 1px dotted #ddd;
|
299
|
+
cursor: help;
|
300
|
+
}
|
301
|
+
abbr.initialism {
|
302
|
+
font-size: 90%;
|
303
|
+
text-transform: uppercase;
|
304
|
+
}
|
305
|
+
blockquote {
|
306
|
+
padding: 0 0 0 15px;
|
307
|
+
margin: 0 0 18px;
|
308
|
+
border-left: 5px solid #eeeeee;
|
309
|
+
}
|
310
|
+
blockquote p {
|
311
|
+
margin-bottom: 0;
|
312
|
+
font-size: 16px;
|
313
|
+
font-weight: 300;
|
314
|
+
line-height: 22.5px;
|
315
|
+
}
|
316
|
+
blockquote small {
|
317
|
+
display: block;
|
318
|
+
line-height: 18px;
|
319
|
+
color: #999999;
|
320
|
+
}
|
321
|
+
blockquote small:before {
|
322
|
+
content: '\2014 \00A0';
|
323
|
+
}
|
324
|
+
blockquote.pull-right {
|
325
|
+
float: right;
|
326
|
+
padding-left: 0;
|
327
|
+
padding-right: 15px;
|
328
|
+
border-left: 0;
|
329
|
+
border-right: 5px solid #eeeeee;
|
330
|
+
}
|
331
|
+
blockquote.pull-right p,
|
332
|
+
blockquote.pull-right small {
|
333
|
+
text-align: right;
|
334
|
+
}
|
335
|
+
q:before,
|
336
|
+
q:after,
|
337
|
+
blockquote:before,
|
338
|
+
blockquote:after {
|
339
|
+
content: "";
|
340
|
+
}
|
341
|
+
address {
|
342
|
+
display: block;
|
343
|
+
margin-bottom: 18px;
|
344
|
+
line-height: 18px;
|
345
|
+
font-style: normal;
|
346
|
+
}
|
347
|
+
small {
|
348
|
+
font-size: 100%;
|
349
|
+
}
|
350
|
+
cite {
|
351
|
+
font-style: normal;
|
352
|
+
}
|
353
|
+
.hero-unit {
|
354
|
+
padding: 60px;
|
355
|
+
margin-bottom: 30px;
|
356
|
+
background-color: #eeeeee;
|
357
|
+
-webkit-border-radius: 6px;
|
358
|
+
-moz-border-radius: 6px;
|
359
|
+
border-radius: 6px;
|
360
|
+
}
|
361
|
+
.hero-unit h1 {
|
362
|
+
margin-bottom: 0;
|
363
|
+
font-size: 60px;
|
364
|
+
line-height: 1;
|
365
|
+
color: inherit;
|
366
|
+
letter-spacing: -1px;
|
367
|
+
}
|
368
|
+
.hero-unit p {
|
369
|
+
font-size: 18px;
|
370
|
+
font-weight: 200;
|
371
|
+
line-height: 27px;
|
372
|
+
color: inherit;
|
373
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
/*!
|
2
|
+
* Bootstrap v2.0.2
|
3
|
+
*
|
4
|
+
* Copyright 2012 Twitter, Inc
|
5
|
+
* Licensed under the Apache License v2.0
|
6
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
*
|
8
|
+
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
9
|
+
*/
|
10
|
+
.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
|
11
|
+
.clearfix:after{clear:both;}
|
12
|
+
.hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;}
|
13
|
+
.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
|
14
|
+
article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
|
15
|
+
audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
|
16
|
+
audio:not([controls]){display:none;}
|
17
|
+
html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
|
18
|
+
a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
19
|
+
a:hover,a:active{outline:0;}
|
20
|
+
sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
|
21
|
+
sup{top:-0.5em;}
|
22
|
+
sub{bottom:-0.25em;}
|
23
|
+
img{height:auto;border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;}
|
24
|
+
button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
|
25
|
+
button,input{*overflow:visible;line-height:normal;}
|
26
|
+
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
|
27
|
+
button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
|
28
|
+
input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
|
29
|
+
input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
|
30
|
+
textarea{overflow:auto;vertical-align:top;}
|
31
|
+
body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;}
|
32
|
+
a{color:#0088cc;text-decoration:none;}
|
33
|
+
a:hover{color:#005580;text-decoration:underline;}
|
34
|
+
p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;}
|
35
|
+
.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
|
36
|
+
h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
|
37
|
+
h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
|
38
|
+
h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;}
|
39
|
+
h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;}
|
40
|
+
h4,h5,h6{line-height:18px;}
|
41
|
+
h4{font-size:14px;}h4 small{font-size:12px;}
|
42
|
+
h5{font-size:12px;}
|
43
|
+
h6{font-size:11px;color:#999999;text-transform:uppercase;}
|
44
|
+
.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}
|
45
|
+
.page-header h1{line-height:1;}
|
46
|
+
ul,ol{padding:0;margin:0 0 9px 25px;}
|
47
|
+
ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
|
48
|
+
ul{list-style:disc;}
|
49
|
+
ol{list-style:decimal;}
|
50
|
+
li{line-height:18px;}
|
51
|
+
ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
|
52
|
+
dl{margin-bottom:18px;}
|
53
|
+
dt,dd{line-height:18px;}
|
54
|
+
dt{font-weight:bold;line-height:17px;}
|
55
|
+
dd{margin-left:9px;}
|
56
|
+
.dl-horizontal dt{float:left;clear:left;width:120px;text-align:right;}
|
57
|
+
.dl-horizontal dd{margin-left:130px;}
|
58
|
+
hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
|
59
|
+
strong{font-weight:bold;}
|
60
|
+
em{font-style:italic;}
|
61
|
+
.muted{color:#999999;}
|
62
|
+
abbr[title]{border-bottom:1px dotted #ddd;cursor:help;}
|
63
|
+
abbr.initialism{font-size:90%;text-transform:uppercase;}
|
64
|
+
blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}
|
65
|
+
blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
|
66
|
+
blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
|
67
|
+
q:before,q:after,blockquote:before,blockquote:after{content:"";}
|
68
|
+
address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;}
|
69
|
+
small{font-size:100%;}
|
70
|
+
cite{font-style:normal;}
|
71
|
+
.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;}
|
72
|
+
.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit;}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
5
|
+
<script type="text/javascript" src="https://<%= @env[:browserid_url] %>/provisioning_api.js"></script>
|
6
|
+
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
|
7
|
+
<script type="text/javascript">
|
8
|
+
// an alias
|
9
|
+
var fail = navigator.id.raiseProvisioningFailure;
|
10
|
+
|
11
|
+
// begin provisioning! This both gives us indicated to browserid that we're
|
12
|
+
// a well formed provisioning page and gives us the parameters of the provisioning
|
13
|
+
navigator.id.beginProvisioning(function(email, cert_duration) {
|
14
|
+
// now we have the email address that wishes to be provisioned!
|
15
|
+
// is he authenticated to underpin.no?
|
16
|
+
$.get('<%= @env[:whoami_path] %>')
|
17
|
+
.success(function(r) {
|
18
|
+
email = email.replace('@<%= @env[:server_name] %>', '').toLowerCase();
|
19
|
+
if (email != r.user) {
|
20
|
+
return fail('user is not authenticated as target user');
|
21
|
+
}
|
22
|
+
|
23
|
+
// Awesome! The user is authenticated as who we want to provision. let's
|
24
|
+
// generate a keypair
|
25
|
+
navigator.id.genKeyPair(function(pubkey) {
|
26
|
+
// finally, once we have a public key from the browser, we'll certify it, and
|
27
|
+
// go pass it back
|
28
|
+
$.ajax({
|
29
|
+
url: '<%= @env[:certify_path] %>',
|
30
|
+
data: JSON.stringify({
|
31
|
+
pubkey: pubkey,
|
32
|
+
duration: cert_duration
|
33
|
+
}),
|
34
|
+
type: 'POST',
|
35
|
+
headers: { "Content-Type": 'application/json' },
|
36
|
+
dataType: 'json',
|
37
|
+
success: function(r) {
|
38
|
+
// all done! woo!
|
39
|
+
navigator.id.registerCertificate(r.cert);
|
40
|
+
},
|
41
|
+
error: function(r) {
|
42
|
+
fail("couldn't certify key");
|
43
|
+
}
|
44
|
+
});
|
45
|
+
});
|
46
|
+
})
|
47
|
+
.error(function() {
|
48
|
+
fail('user is not authenticated');
|
49
|
+
});
|
50
|
+
});
|
51
|
+
</script>
|
52
|
+
</head>
|
53
|
+
</html>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "browserid-provider/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "browserid-provider"
|
7
|
+
s.version = BrowserID::VERSION
|
8
|
+
s.authors = ["ringe"]
|
9
|
+
s.email = ["runar@rin.no"]
|
10
|
+
s.homepage = "https://github.com/ringe/browserid-provider"
|
11
|
+
s.summary = %q{Rack-based Mozilla BrowserID Provider}
|
12
|
+
s.description = %q{With the BrowserID provider you enable your users to authenticate themselves across the web using a single authority.}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency "json-jwt"
|
20
|
+
s.add_development_dependency "rack-test"
|
21
|
+
s.add_development_dependency "mocha"
|
22
|
+
s.add_development_dependency "warden"
|
23
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module BrowserID
|
2
|
+
#
|
3
|
+
# authentication_path Where to redirect users for login
|
4
|
+
# defaults to: "/users/sign_in" (Devise default)
|
5
|
+
#
|
6
|
+
# provision_path What HTTP path to deliver provisioning from
|
7
|
+
# defaults to: "/browserid/provision"
|
8
|
+
# certify_path What HTTP path to deliver certifying from
|
9
|
+
# defaults to: "/browserid/certify"
|
10
|
+
# whoami_path What HTTP path to serve user credentials at
|
11
|
+
# defaults to: "/browserid/whoami"
|
12
|
+
#
|
13
|
+
# whoami What function to call for the current user object (must respond to :email method)
|
14
|
+
# defaults to: "@env['warden'].user"
|
15
|
+
#
|
16
|
+
# private_key_path Where is the BrowserID OpenSSL private key located
|
17
|
+
# defaults to: "config/browserid_provider.pem"
|
18
|
+
#
|
19
|
+
# The "/.well-known/browserid" path is required from the BrowserID spec and used here.
|
20
|
+
#
|
21
|
+
# browserid_url Which BrowserID server to use, ca be one of the following:
|
22
|
+
# * dev.diresworb.org for development (default)
|
23
|
+
# * diresworb.org for beta
|
24
|
+
# * browserid.org for production
|
25
|
+
#
|
26
|
+
# server_name The domain name we are providing BrowserID for (default to example.org)
|
27
|
+
#
|
28
|
+
class Config < Hash
|
29
|
+
# Creates an accessor that simply sets and reads a key in the hash:
|
30
|
+
#
|
31
|
+
# class Config < Hash
|
32
|
+
# hash_accessor :login_path
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# config = Config.new
|
36
|
+
# config.login_path = "/users/sign_in"
|
37
|
+
# config[:login_path] #=> "/users/sign_in"
|
38
|
+
#
|
39
|
+
# config[:login_path] = "/login"
|
40
|
+
# config.login_path #=> "/login"
|
41
|
+
#
|
42
|
+
# Thanks to Warden. :)
|
43
|
+
def self.hash_accessor(*names) #:nodoc:
|
44
|
+
names.each do |name|
|
45
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
46
|
+
def #{name}
|
47
|
+
self[:#{name}]
|
48
|
+
end
|
49
|
+
|
50
|
+
def #{name}=(value)
|
51
|
+
self[:#{name}] = value
|
52
|
+
end
|
53
|
+
METHOD
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
hash_accessor :login_path, :provision_path, :whoami, :whoami_path, :certify_path, :private_key_path, :browserid_url, :server_name
|
58
|
+
|
59
|
+
def initialize(other={})
|
60
|
+
merge!(other)
|
61
|
+
self[:login_path] ||= "/users/sign_in"
|
62
|
+
self[:provision_path] ||= "/browserid/provision"
|
63
|
+
self[:certify_path] ||= "/browserid/certify"
|
64
|
+
self[:whoami_path] ||= "/browserid/whoami"
|
65
|
+
self[:whoami] ||= "@env['warden'].user"
|
66
|
+
self[:private_key_path] ||= "config/browserid_provider.pem"
|
67
|
+
self[:browserid_url] ||= "dev.diresworb.org"
|
68
|
+
self[:server_name] ||= "example.org"
|
69
|
+
end
|
70
|
+
|
71
|
+
def urls
|
72
|
+
[ self[:provision_path], self[:certify_path], self[:whoami_path], "/.well-known/browserid" ]
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "openssl"
|
2
|
+
require "json/jwt"
|
3
|
+
|
4
|
+
module BrowserID
|
5
|
+
class Identity
|
6
|
+
attr_accessor :config
|
7
|
+
|
8
|
+
# == Options ==
|
9
|
+
# :key_path where to store the OpenSSL private key
|
10
|
+
def initialize(options = {})
|
11
|
+
@config = BrowserID::Config.new(options)
|
12
|
+
|
13
|
+
keypath = @config.private_key_path
|
14
|
+
|
15
|
+
if File.exists?(keypath)
|
16
|
+
File.open(keypath) {|f| @privkey = OpenSSL::PKey::RSA.new(f.read) }
|
17
|
+
@pubkey = @privkey.public_key
|
18
|
+
else
|
19
|
+
require 'fileutils'
|
20
|
+
FileUtils.mkdir_p keypath.sub(File.basename(keypath),'')
|
21
|
+
@privkey = OpenSSL::PKey::RSA.new(2048)
|
22
|
+
@pubkey = @privkey.public_key
|
23
|
+
File.open(keypath, "w") {|f| f.write(@privkey) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def public_key
|
28
|
+
@pubkey
|
29
|
+
end
|
30
|
+
|
31
|
+
def private_key
|
32
|
+
@privkey
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return BrowserID Identity JSON
|
36
|
+
def to_json
|
37
|
+
{
|
38
|
+
"public-key" => { "algorithm"=> "RS", "n" => public_key.n.to_s, "e" => public_key.e.to_s },
|
39
|
+
"authentication" => @config.login_path,
|
40
|
+
"provisioning" => @config.provision_path
|
41
|
+
}.to_json
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module BrowserID
|
2
|
+
# The BrowserID Provider Rack App
|
3
|
+
#
|
4
|
+
# Default paths are:
|
5
|
+
# GET /users/sign_in
|
6
|
+
# GET /browserid/provision
|
7
|
+
# GET /browserid/whoami
|
8
|
+
# POST /browserid/certify
|
9
|
+
class Provider
|
10
|
+
attr_accessor :config, :env, :req, :identity
|
11
|
+
|
12
|
+
def initialize(app = nil, options = {})
|
13
|
+
@app, @config = app, BrowserID::Config.new(options)
|
14
|
+
@identity = BrowserID::Identity.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Rack enabled!
|
18
|
+
def call(env)
|
19
|
+
@env, @path = env, env["PATH_INFO"], @req = Rack::Request.new(env)
|
20
|
+
env['browserid'] = @config
|
21
|
+
|
22
|
+
# Return Not found or send call back to middleware stack unless the URL is captured here
|
23
|
+
return (@app ? @app.call(env) : not_found) unless @config.urls.include? @path
|
24
|
+
|
25
|
+
case @path
|
26
|
+
when "/.well-known/browserid"
|
27
|
+
@req.get? ? well_known_browserid : not_found
|
28
|
+
when config.whoami_path
|
29
|
+
@req.get? ? whoami : not_found
|
30
|
+
when config.provision_path
|
31
|
+
@req.get? ? provision : not_found
|
32
|
+
when config.certify_path
|
33
|
+
@req.post? ? certify : not_found
|
34
|
+
else not_found
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def well_known_browserid
|
40
|
+
[ 200, {"Content-Type" => "application/json"}, [@identity.to_json] ]
|
41
|
+
end
|
42
|
+
|
43
|
+
def whoami
|
44
|
+
email = current_user_email
|
45
|
+
[ 200, {"Content-Type" => "application/json"}, [{ user: email ? email.sub(/@.*/,'') : nil }.to_json] ]
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# The certify function will be called by BrowserID with a public key and duration as parameters. The
|
50
|
+
# request is a POST with JSON data that looks like this:
|
51
|
+
#
|
52
|
+
# {
|
53
|
+
# "pubkey" :
|
54
|
+
# {
|
55
|
+
# "algorithm":"DS",
|
56
|
+
# "y":"62b0ea6936a7ab30c95d8ffbbc77438a342faed99b6fc643a58f28d9ed2017177354f9f1d1d7e6b9e1c543780c3517953a124e66bc409fcaaa671d87a39cf897b32f47aaaffb7a3d297b89f9e116870a2182e2b2f84d68a7bc21a3f7934727e45e50a083e71a965d0cc320062598e407463f0c31cc2c20ed74d9bda98b21c902",
|
57
|
+
# "p":"ff600483db6abfc5b45eab78594b3533d550d9f1bf2a992a7a8daa6dc34f8045ad4e6e0c429d334eeeaaefd7e23d4810be00e4cc1492cba325ba81ff2d5a5b305a8d17eb3bf4a06a349d392e00d329744a5179380344e82a18c47933438f891e22aeef812d69c8f75e326cb70ea000c3f776dfdbd604638c2ef717fc26d02e17",
|
58
|
+
# "q":"e21e04f911d1ed7991008ecaab3bf775984309c3",
|
59
|
+
# "g":"c52a4a0ff3b7e61fdf1867ce84138369a6154f4afa92966e3c827e25cfa6cf508b90e5de419e1337e07a2e9e2a3cd5dea704d175f8ebf6af397d69e110b96afb17c7a03259329e4829b0d03bbc7896b15b4ade53e130858cc34d96269aa89041f409136c7242a38895c9d5bccad4f389af1d7a4bd1398bd072dffa896233397a"
|
60
|
+
# },
|
61
|
+
# "duration":3600
|
62
|
+
# }
|
63
|
+
#
|
64
|
+
# We're going to certify that public key for the currently logged in user.
|
65
|
+
#
|
66
|
+
def certify
|
67
|
+
email = current_user_email
|
68
|
+
return err "No user is logged in." unless email
|
69
|
+
|
70
|
+
# Get params from Rails' ActionDispatch or from Rack Request
|
71
|
+
params = env["action_dispatch.request.request_parameters"] ? env["action_dispatch.request.request_parameters"] : @req.params
|
72
|
+
return err "Missing a required parameter (duration, pubkey)" if params.keys.sort != ["duration", "pubkey"]
|
73
|
+
|
74
|
+
expiration = (Time.now.strftime("%s").to_i + params["duration"].to_i) * 1000
|
75
|
+
issue = { "iss" => @config.server_name,
|
76
|
+
"exp" => expiration,
|
77
|
+
"public-key" => params["pubkey"],
|
78
|
+
"principal" => { "email"=> email }
|
79
|
+
}
|
80
|
+
jwt = JSON::JWT.new(issue)
|
81
|
+
jws = jwt.sign(@identity.private_key, :RS256)
|
82
|
+
|
83
|
+
return [ 200, {"Content-Type" => "application/json"}, [{ "cert" => jws.to_s }.to_json] ]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Something went wrong.
|
87
|
+
def err(message)
|
88
|
+
[ 403, {"Content-Type" => "text/plain"}, [message] ]
|
89
|
+
end
|
90
|
+
|
91
|
+
# Return the provision iframe content.
|
92
|
+
def provision
|
93
|
+
[200, {"Content-Type" => "text/html"}, BrowserID::Template.render("provision", @config)]
|
94
|
+
end
|
95
|
+
|
96
|
+
# This middleware doesn't find what you are looking for.
|
97
|
+
def not_found
|
98
|
+
[404, {"Content-Type" => "text/html"}, BrowserID::Template.render("404", @env)]
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return the email of the user logged in currently, or nil
|
102
|
+
def current_user_email
|
103
|
+
begin
|
104
|
+
current_user = eval config.whoami
|
105
|
+
current_user ? current_user.email : nil
|
106
|
+
rescue NoMethodError
|
107
|
+
raise NoMethodError, "The function provided in BrowserID::Config.whoami doesn't exist."
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'erb'
|
2
|
+
module BrowserID
|
3
|
+
class Template
|
4
|
+
PATH = File.expand_path(File.join(File.dirname(__FILE__), "../..", "app", "assets", "browserid"))
|
5
|
+
|
6
|
+
def initialize(env)
|
7
|
+
@env = env
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_binding
|
11
|
+
binding
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.render(template, env)
|
15
|
+
rhtml = ERB.new File.read(PATH + "/" + template + ".html.erb")
|
16
|
+
view = BrowserID::Template.new(env)
|
17
|
+
[rhtml.result(view.get_binding)]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.css_styles
|
21
|
+
File.read(PATH + "/bootstrap.css")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module BrowserId
|
2
|
+
module ViewHelpers
|
3
|
+
|
4
|
+
# BrowserID JavaScript tags:
|
5
|
+
# - The official BrowserID include.js
|
6
|
+
# - The Devise enabled login and assertion reponse
|
7
|
+
def browserid_authentication_tag
|
8
|
+
javascript_include_tag(browserid_authentication_api_js_url)
|
9
|
+
end
|
10
|
+
|
11
|
+
# JavaScript enable BrowserID authentication for the form with the given #id
|
12
|
+
def enable_browserid_javascript_tag(id)
|
13
|
+
raw "<script type='text/javascript'>$('form##{id}').bind('ajax:success', function(data, status, xhr) { navigator.id.completeAuthentication() })</script>"
|
14
|
+
end
|
15
|
+
|
16
|
+
# The URL to the BrowserID official JavaScript
|
17
|
+
def browserid_authentication_api_js_url
|
18
|
+
"https://#{ request.env['browserid'][:browserid_url] }/authentication_api.js"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "browserid-provider/version"
|
2
|
+
require "browserid-provider/config"
|
3
|
+
require "browserid-provider/identity"
|
4
|
+
require "browserid-provider/provider"
|
5
|
+
require "browserid-provider/template"
|
6
|
+
|
7
|
+
if defined?(Rails)
|
8
|
+
require "browserid-provider/engine"
|
9
|
+
require "browserid-provider/railtie"
|
10
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "json"
|
3
|
+
require "rack"
|
4
|
+
require "rack/test"
|
5
|
+
require "mocha"
|
6
|
+
require "browserid-provider"
|
7
|
+
require "ruby-debug"
|
8
|
+
require "warden"
|
9
|
+
|
10
|
+
class MyTest < Test::Unit::TestCase
|
11
|
+
include Rack::Test::Methods
|
12
|
+
include Warden::Test::Helpers
|
13
|
+
attr_accessor :thisapp
|
14
|
+
|
15
|
+
def app
|
16
|
+
@thisapp = Rack::Builder.new do
|
17
|
+
use Rack::CommonLogger
|
18
|
+
use Rack::Session::Cookie
|
19
|
+
use Warden::Manager do |manager|
|
20
|
+
manager.default_strategies :basic
|
21
|
+
end
|
22
|
+
|
23
|
+
run BrowserID::Provider.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_get_root
|
28
|
+
get "/"
|
29
|
+
assert last_response.status == 404, "BrowserID Provider should not respond to root"
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_get_well_known_browserid
|
33
|
+
get "/.well-known/browserid"
|
34
|
+
|
35
|
+
assert last_response.ok?
|
36
|
+
assert_json_response
|
37
|
+
|
38
|
+
# Test the JSON output
|
39
|
+
json = JSON.parse(last_response.body)
|
40
|
+
assert json.keys == ["public-key", "authentication", "provisioning"], "Malformed JSON response, see https://eyedee.me/.well-known/browserid for example data"
|
41
|
+
assert json["public-key"].keys == ["algorithm","n","e"], "Invalid public key provided, see https://wiki.mozilla.org/Identity/BrowserID"
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_get_whoami
|
45
|
+
fake_user "mormor@example.org"
|
46
|
+
get "/browserid/whoami"
|
47
|
+
assert last_response.ok?
|
48
|
+
assert_json_response
|
49
|
+
assert last_response.body == '{"user":"mormor"}', "The whoami_path should return JSON with user name only, without domain"
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_get_provision
|
53
|
+
get "/browserid/provision"
|
54
|
+
assert last_response.ok?
|
55
|
+
assert last_response.body.include?("https://dev.diresworb.org/provisioning_api.js"), "The default provisions_api.js must be provided, see https://developer.mozilla.org/en/BrowserID/Primary/Developer_tips"
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_post_certify
|
59
|
+
get "/browserid/certify"
|
60
|
+
assert last_response.status == 404, "Should only allow POST to /browserid/certify"
|
61
|
+
|
62
|
+
fake_user "mormor@example.org"
|
63
|
+
post "/browserid/certify", params = { "duration" => 2500, "pubkey" => "aasdcasd" }
|
64
|
+
assert_json_response
|
65
|
+
assert last_response.body =~ /\{"cert":.*\}/, "The certify_path should return JSON with a signed certificate"
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def fake_user(email)
|
70
|
+
Warden.on_next_request do |proxy|
|
71
|
+
u = mock()
|
72
|
+
u.stubs(:email).returns(email).at_least_once
|
73
|
+
proxy.set_user(u)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def assert_json_response
|
78
|
+
assert last_response.content_type == "application/json", "Content type was #{last_response.content_type} but must be application/json"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: browserid-provider
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- ringe
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json-jwt
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rack-test
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mocha
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: warden
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: With the BrowserID provider you enable your users to authenticate themselves
|
79
|
+
across the web using a single authority.
|
80
|
+
email:
|
81
|
+
- runar@rin.no
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- Gemfile
|
88
|
+
- README.md
|
89
|
+
- Rakefile
|
90
|
+
- app/assets/browserid/404.html.erb
|
91
|
+
- app/assets/browserid/bootstrap.css
|
92
|
+
- app/assets/browserid/bootstrap.min.css
|
93
|
+
- app/assets/browserid/provision.html.erb
|
94
|
+
- browserid-provider.gemspec
|
95
|
+
- lib/browserid-provider.rb
|
96
|
+
- lib/browserid-provider/config.rb
|
97
|
+
- lib/browserid-provider/engine.rb
|
98
|
+
- lib/browserid-provider/identity.rb
|
99
|
+
- lib/browserid-provider/provider.rb
|
100
|
+
- lib/browserid-provider/railtie.rb
|
101
|
+
- lib/browserid-provider/template.rb
|
102
|
+
- lib/browserid-provider/version.rb
|
103
|
+
- lib/browserid-provider/view_helpers.rb
|
104
|
+
- test/browserid-provider.rb
|
105
|
+
homepage: https://github.com/ringe/browserid-provider
|
106
|
+
licenses: []
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ! '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 1.8.22
|
126
|
+
signing_key:
|
127
|
+
specification_version: 3
|
128
|
+
summary: Rack-based Mozilla BrowserID Provider
|
129
|
+
test_files: []
|