browserid-provider 0.4.3
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/.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: []
|