browserid-rails 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +22 -0
- data/README.md +141 -0
- data/app/assets/javascripts/browserid.js.coffee +84 -0
- data/app/views/layouts/_browserid.html.erb +12 -0
- data/lib/browserid-rails.rb +41 -0
- data/lib/browserid/rails/base.rb +130 -0
- data/lib/browserid/rails/helpers.rb +62 -0
- data/lib/browserid/rails/version.rb +5 -0
- data/lib/browserid/verifier/persona.rb +85 -0
- metadata +76 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Greg Look
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# BrowserID::Rails
|
2
|
+
|
3
|
+
This gem provides a simple authentication structure to a Rails application
|
4
|
+
based on Mozilla's BrowserID protocol and Persona service. Users are uniquely
|
5
|
+
authenticated by email address using public-key cryptography. The advantage of
|
6
|
+
this is that the rails application does not need to worry about storing or
|
7
|
+
securing user passwords.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'browserid-rails'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install browserid-rails
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
To use this gem once it is installed, it must be integrated into your Rails
|
26
|
+
application. The following sections cover the gem configuration, controller
|
27
|
+
integration, and view integration.
|
28
|
+
|
29
|
+
### Configuration
|
30
|
+
|
31
|
+
There are several configuration options available. There are a number of default
|
32
|
+
assumptions about the application, which may be overridden as needed.
|
33
|
+
Configuration settings are properties of `config.browserid`.
|
34
|
+
|
35
|
+
* `user_model` - The name of the ActiveModel class for application users.
|
36
|
+
The default is `"User"`.
|
37
|
+
* `email_field` - The name of the attribute on the user model which contains
|
38
|
+
the user's email. The default is `"email"`.
|
39
|
+
* `session_variable` - The location the authenticated email is stored in the
|
40
|
+
client's session. The default is `:browserid_email`.
|
41
|
+
* `verifier` - The type of verifier to use to authenticate client BrowserID
|
42
|
+
assertions. The default is `:persona`, which sends the request to Mozilla's
|
43
|
+
Persona verification service. In the future, `:local` will enable local
|
44
|
+
verification code. Alternately, this configuration option may be set to any
|
45
|
+
class which responds to `#verify(assertion)` with the verified email and
|
46
|
+
identity provider on success and raises an error on failure.
|
47
|
+
* `audience` - The BrowserID audience to authenticate to. This should consist
|
48
|
+
of a URI string containing the scheme (protocol), authority, and port of the
|
49
|
+
service (e.g., `"https://app.example.com:443"`). By default, the audience is
|
50
|
+
not hardcoded and the properties of the request object are used to construct
|
51
|
+
it dynamically. This gives greater flexibility while developing, but is also
|
52
|
+
a minor security risk. In production, this should be configured to a fixed
|
53
|
+
value.
|
54
|
+
|
55
|
+
### Controller Integration
|
56
|
+
|
57
|
+
The `BrowserID::Rails::Base` module makes several controller methods available
|
58
|
+
to interact with the authentication system. To access information, use one of:
|
59
|
+
|
60
|
+
* `browserid_email` - Returns the BrowserID-authenticated email address, if any.
|
61
|
+
* `current_user` - Retrieves the model for the currently authenticated user, if
|
62
|
+
there is an authenticated email and a matching user exists.
|
63
|
+
* `authenticated?` - Returns true if there is a current user.
|
64
|
+
|
65
|
+
These methods are also available in views as helpers.
|
66
|
+
|
67
|
+
To control authentication, the app should have a `SessionsController` which
|
68
|
+
connects the in-browser authentication code to the server. The gem provides
|
69
|
+
these methods:
|
70
|
+
|
71
|
+
* `login_browserid` - Sets the given string as the authenticated email.
|
72
|
+
* `logout_browserid` - Clears the current authenticated email.
|
73
|
+
* `verify_browserid` - Uses the configured verifier to confirm a BrowserID
|
74
|
+
assertion is correct for the service audience.
|
75
|
+
* `respond_to_browserid` - Wraps `verify_browserid` in logging and error
|
76
|
+
handling logic and generates controller responses to a `POST` assertion.
|
77
|
+
|
78
|
+
Implementing the required methods for `SessionsController` is straightforward:
|
79
|
+
|
80
|
+
# POST /login
|
81
|
+
def create
|
82
|
+
respond_to_browserid
|
83
|
+
end
|
84
|
+
|
85
|
+
# POST /logout
|
86
|
+
def destroy
|
87
|
+
logout_browserid
|
88
|
+
head :ok
|
89
|
+
end
|
90
|
+
|
91
|
+
TODO: write generator to create routes and session controller
|
92
|
+
|
93
|
+
### Layout Integration
|
94
|
+
|
95
|
+
The BrowserID javascript library needs to be loaded on your application pages.
|
96
|
+
There are two steps to accomplish this:
|
97
|
+
|
98
|
+
First, the coffeescript asset file needs to be loaded. In the
|
99
|
+
`app/assets/javascripts/application.js` manifest, add the following line:
|
100
|
+
|
101
|
+
//= require browserid
|
102
|
+
|
103
|
+
Second, the scripts need to be setup in your pages' `<head>` section. The
|
104
|
+
`setup_browserid` helper method takes care of this for you and gives a couple
|
105
|
+
of ways to control its behavior:
|
106
|
+
|
107
|
+
<!-- Perform basic BrowserID setup in the head section -->
|
108
|
+
<%= setup_browserid %>
|
109
|
+
|
110
|
+
<!-- Setup BrowserID with alert debugging -->
|
111
|
+
<%= setup_browserid debug: true %>
|
112
|
+
|
113
|
+
<!-- Setup BrowserID with a custom handler -->
|
114
|
+
<%= setup_browserid do %>
|
115
|
+
browserid.onLogin = function (data, status, xhr) {
|
116
|
+
// ...
|
117
|
+
}
|
118
|
+
<% end %>
|
119
|
+
|
120
|
+
Once that's accomplished, the app is ready to use BrowserID for authentication.
|
121
|
+
To add login and logout links to the site, use the `login_link` and
|
122
|
+
`logout_link` helpers. These accept optional link text and targets as parameters:
|
123
|
+
|
124
|
+
<%= login_link "Login with Persona" %>
|
125
|
+
|
126
|
+
<%= login_link "Login", auth_path %>
|
127
|
+
|
128
|
+
If the path is not provided, the link helpers will use `login_path` and
|
129
|
+
`logout_path` if they are available, otherwise the link targets will be `#`.
|
130
|
+
The coffeescript assets add on-click handlers to the links which trigger the
|
131
|
+
Persona code to request new assertions or destroy existing ones.
|
132
|
+
|
133
|
+
TODO: include Persona branding assets
|
134
|
+
|
135
|
+
## Contributing
|
136
|
+
|
137
|
+
1. Fork it
|
138
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
139
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
140
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
141
|
+
5. Create new Pull Request
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# BrowserID javascript functions
|
2
|
+
|
3
|
+
@browserid = browserid =
|
4
|
+
|
5
|
+
### PROPERTIES ###
|
6
|
+
|
7
|
+
# Public: Path used to verify browserID authentication assertions. Assertions
|
8
|
+
# are POSTed to this path.
|
9
|
+
loginPath: '/login'
|
10
|
+
|
11
|
+
# Public: Path used to unset persisted authentication state when logging out.
|
12
|
+
# This should clear the currently-logged-in user email.
|
13
|
+
logoutPath: '/logout'
|
14
|
+
|
15
|
+
# Internal: Debugging toggle - if true, the results of logins will be alert
|
16
|
+
# dialogs instead of page refreshes. This is useful to set if the application
|
17
|
+
# starts going into a refresh loop.
|
18
|
+
debug: false
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
### HANDLERS ###
|
23
|
+
|
24
|
+
# Public: This method is called when a user successfully authenticates. By
|
25
|
+
# default, it reloads the current page.
|
26
|
+
onLogin: (data, status, xhr) ->
|
27
|
+
if @debug
|
28
|
+
alert("Login result: #{status} #{data}")
|
29
|
+
else
|
30
|
+
window.location.reload()
|
31
|
+
|
32
|
+
# Public: This method is called when a user fails to authenticate.
|
33
|
+
onLoginError: (xhr, status, err) ->
|
34
|
+
alert("Login #{err} - #{xhr.responseText}")
|
35
|
+
|
36
|
+
# Public: This method is called when a user clears their authentication. By
|
37
|
+
# default, it reloads the current page.
|
38
|
+
onLogout: (data, status, xhr) ->
|
39
|
+
if @debug
|
40
|
+
alert("Logout result: #{status} #{data}")
|
41
|
+
else
|
42
|
+
window.location.reload()
|
43
|
+
|
44
|
+
# Public: This method is called when a user fails to clear their
|
45
|
+
# authentication.
|
46
|
+
onLogoutError: (xhr, status, err) ->
|
47
|
+
alert("Logout #{err} - #{xhr.responseText}")
|
48
|
+
|
49
|
+
|
50
|
+
### INITIALIZATION ###
|
51
|
+
|
52
|
+
# Public: Watches the authentication state and takes action when the user
|
53
|
+
# logs in or logs out. This method MUST be called on every page of the
|
54
|
+
# application.
|
55
|
+
setup: (currentUser = null) ->
|
56
|
+
navigator.id.watch
|
57
|
+
loggedInUser: currentUser
|
58
|
+
onlogin: (assertion) =>
|
59
|
+
$.ajax
|
60
|
+
type: 'POST'
|
61
|
+
url: @loginPath
|
62
|
+
data: { assertion: assertion }
|
63
|
+
success: (data, status, xhr) => @onLogin(data, status, xhr)
|
64
|
+
error: (xhr, status, err) => @onLoginError(xhr, status, err)
|
65
|
+
onlogout: =>
|
66
|
+
$.ajax
|
67
|
+
type: 'POST'
|
68
|
+
url: @logoutPath
|
69
|
+
success: (data, status, xhr) => @onLogout(data, status, xhr)
|
70
|
+
error: (xhr, status, err) => @onLogoutError(xhr, status, err)
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
### Behavior Binding ###
|
75
|
+
|
76
|
+
jQuery ->
|
77
|
+
$('.browserid_login').click ->
|
78
|
+
navigator.id.request()
|
79
|
+
false
|
80
|
+
|
81
|
+
jQuery ->
|
82
|
+
$('.browserid_logout').click ->
|
83
|
+
navigator.id.logout()
|
84
|
+
false
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<script src="https://login.persona.org/include.js"></script>
|
2
|
+
<script type="text/javascript">
|
3
|
+
<% if options[:login_path] %>browserid.loginPath = "<%= options[:login_path] %>";<% end %>
|
4
|
+
<% if options[:logout_path] %>browserid.logoutPath = "<%= options[:logout_path] %>";<% end %>
|
5
|
+
<% if options[:debug] %>browserid.debug = true;<% end %>
|
6
|
+
<%= yield :browserid_setup %>
|
7
|
+
<% if browserid_email %>
|
8
|
+
browserid.setup("<%= browserid_email %>");
|
9
|
+
<% else %>
|
10
|
+
browserid.setup();
|
11
|
+
<% end %>
|
12
|
+
</script>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'browserid/rails/base'
|
2
|
+
require 'browserid/rails/helpers'
|
3
|
+
require 'browserid/rails/version'
|
4
|
+
|
5
|
+
module BrowserID
|
6
|
+
module Rails
|
7
|
+
# This class defines a Rails engine which extends the base controller with
|
8
|
+
# the library methods. The presence of this engine also causes assets to
|
9
|
+
# be included when the gem is added as a dependency.
|
10
|
+
class Engine < ::Rails::Engine
|
11
|
+
config.before_configuration do
|
12
|
+
BrowserIDConfig = Struct.new :user_model, :email_field, :session_variable, :verifier, :audience
|
13
|
+
|
14
|
+
config.browserid = BrowserIDConfig.new
|
15
|
+
config.browserid.user_model = 'User'
|
16
|
+
config.browserid.email_field = 'email'
|
17
|
+
config.browserid.session_variable = :browserid_email
|
18
|
+
config.browserid.verifier = :persona
|
19
|
+
# config.browserid.audience should only be set in production
|
20
|
+
end
|
21
|
+
|
22
|
+
initializer "browserid-rails.extend" do |app|
|
23
|
+
ActionController::Base.send :include, BrowserID::Rails::Base
|
24
|
+
ActionView::Base.send :include, BrowserID::Rails::Helpers
|
25
|
+
end
|
26
|
+
|
27
|
+
config.after_initialize do
|
28
|
+
cfg = config.browserid
|
29
|
+
|
30
|
+
# Replace type symbol with constructed verifier.
|
31
|
+
if cfg.verifier == :persona
|
32
|
+
cfg.verifier = BrowserID::Verifier::Persona.new
|
33
|
+
elsif cfg.verifier == :local
|
34
|
+
raise "Local BrowserID verification is not supported yet" # TODO
|
35
|
+
elsif !cfg.verifier.respond_to?(:verify)
|
36
|
+
raise "Unknown BrowserID verifier type #{cfg.verifier}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'browserid/verifier/persona'
|
2
|
+
|
3
|
+
module BrowserID
|
4
|
+
module Rails
|
5
|
+
# Public: Base module for inclusion into a controller. This module includes
|
6
|
+
# methods for dealing with BrowserID user authentication.
|
7
|
+
module Base
|
8
|
+
|
9
|
+
##### INTERNAL METHODS #####
|
10
|
+
|
11
|
+
# Internal: Modifies the controller this module is included in to provide
|
12
|
+
# authentication-related helper methods
|
13
|
+
#
|
14
|
+
# base - The Class this module is being included in.
|
15
|
+
def self.included(base)
|
16
|
+
base.send :helper_method, :browserid_email, :current_user, :authenticated?
|
17
|
+
end
|
18
|
+
|
19
|
+
# Internal: Gets the application configuration for this gem.
|
20
|
+
#
|
21
|
+
# Returns the app config structure.
|
22
|
+
def browserid_config
|
23
|
+
::Rails.application.config.browserid
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
##### AUTHENTICATION METHODS #####
|
29
|
+
|
30
|
+
# Public: Sets the given email address as the currently-authenticated user.
|
31
|
+
# The address is saved in the client's session.
|
32
|
+
#
|
33
|
+
# email - The String email address to consider authenticated.
|
34
|
+
def login_browserid(email)
|
35
|
+
session[browserid_config.session_variable] = email
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Clears the saved email address for the currently-authenticated
|
39
|
+
# user. It is important to note that this does not remove the BrowserID
|
40
|
+
# assertion in the client's browser.
|
41
|
+
def logout_browserid
|
42
|
+
session[browserid_config.session_variable] = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Uses the configured verifier to check that a provided assertion
|
46
|
+
# is correct for the site audience.
|
47
|
+
#
|
48
|
+
# Returns the verified email, identity issuer, and audience on success.
|
49
|
+
# Raises an error with a failure message if the client was not
|
50
|
+
# successfully authenticated.
|
51
|
+
#
|
52
|
+
# Examples
|
53
|
+
#
|
54
|
+
# verify_browserid(assertion)
|
55
|
+
# # => "user@example.com", "persona.mozilla.com", "https://app.example.com:443"
|
56
|
+
#
|
57
|
+
def verify_browserid(assertion)
|
58
|
+
audience = browserid_config.audience
|
59
|
+
audience ||= "%s%s:%d" % [request.protocol, request.host, request.port]
|
60
|
+
browserid_config.verifier.verify(assertion, audience)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Public: Handles a POST-ed BrowserID assertion, responding appropriately
|
64
|
+
# to the request. If successful, this logs-in the authenticated email and
|
65
|
+
# returns an OK status. If unsuccessful, it returns FORBIDDEN and an
|
66
|
+
# error message in the response body.
|
67
|
+
#
|
68
|
+
# Returns nothing.
|
69
|
+
#
|
70
|
+
# Examples
|
71
|
+
#
|
72
|
+
# # POST /login
|
73
|
+
# def create
|
74
|
+
# respond_to_browserid
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
def respond_to_browserid
|
78
|
+
if params[:assertion].blank?
|
79
|
+
head :bad_request
|
80
|
+
else
|
81
|
+
email, issuer, audience = verify_browserid params[:assertion]
|
82
|
+
logger.info "Verified BrowserID assertion for #{email} issued by #{issuer} on #{audience}"
|
83
|
+
login_browserid email
|
84
|
+
head :ok
|
85
|
+
end
|
86
|
+
rescue StandardError => e
|
87
|
+
logger.warn "Failed to verify BrowserID assertion: #{e.message}"
|
88
|
+
render status: :forbidden, text: e.message
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
##### HELPER METHODS #####
|
94
|
+
|
95
|
+
# Public: Gets the email address of the currently-authenticated user.
|
96
|
+
#
|
97
|
+
# Returns the authenticated email address String.
|
98
|
+
def browserid_email
|
99
|
+
session[browserid_config.session_variable]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Public: Retrieves the user for the authenticated email address. This
|
103
|
+
# method uses the `browserid.user_model` and `browserid.email_field`
|
104
|
+
# config settings, which default to `User` and `email`.
|
105
|
+
#
|
106
|
+
# Returns the current authenticated user, or nil if no user exists.
|
107
|
+
def current_user
|
108
|
+
if browserid_email.nil?
|
109
|
+
nil
|
110
|
+
elsif @current_user
|
111
|
+
@current_user
|
112
|
+
else
|
113
|
+
config = browserid_config
|
114
|
+
user_model = config.user_model.constantize
|
115
|
+
find_method = "find_by_#{config.email_field}".intern
|
116
|
+
|
117
|
+
@current_user = user_model.send find_method, browserid_email
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Public: Determines whether the current client is authenticated as a
|
122
|
+
# registered User.
|
123
|
+
#
|
124
|
+
# Returns true if the client is authenticated and registered.
|
125
|
+
def authenticated?
|
126
|
+
!current_user.nil?
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module BrowserID
|
2
|
+
module Rails
|
3
|
+
# Public: Rails view helpers for use with BrowserID code.
|
4
|
+
module Helpers
|
5
|
+
# Public: Renders a layout partial which initializes the BrowserID
|
6
|
+
# system. This should be called in the head of the application layout.
|
7
|
+
#
|
8
|
+
# options - Hash used to adjust the browserid asset setup (default: {}).
|
9
|
+
# :login_path - String giving the path to POST assertions to
|
10
|
+
# for verification.
|
11
|
+
# :logout_path - String giving the path to POST logout
|
12
|
+
# notifications to.
|
13
|
+
# :debug - Boolean determining whether the browserid
|
14
|
+
# javascript will refresh the page or show an
|
15
|
+
# alert dialog.
|
16
|
+
# block - An optional block which can be used to provide additional
|
17
|
+
# content to be rendered inside the browserid setup script tag.
|
18
|
+
#
|
19
|
+
# Examples
|
20
|
+
#
|
21
|
+
# <!-- Perform basic BrowserID setup in the head section -->
|
22
|
+
# <%= setup_browserid %>
|
23
|
+
#
|
24
|
+
# <!-- Setup BrowserID with alert debugging -->
|
25
|
+
# <%= setup_browserid debug: true %>
|
26
|
+
#
|
27
|
+
# <!-- Setup BrowserID with a custom handler -->
|
28
|
+
# <%= setup_browserid do %>
|
29
|
+
# browserid.onLogin = function (data, status, xhr) {
|
30
|
+
# // ...
|
31
|
+
# }
|
32
|
+
# <% end %>
|
33
|
+
#
|
34
|
+
def setup_browserid(options={}, &block)
|
35
|
+
content_for :browserid_setup, capture(&block) if block_given?
|
36
|
+
render 'layouts/browserid', options: options
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Renders a login link which will request a new authentication
|
40
|
+
# assertion from the BrowserID javascript code.
|
41
|
+
#
|
42
|
+
# text - String to use as link text (default: 'Login').
|
43
|
+
# path - String path to link to. If not provided, the `login_path` helper
|
44
|
+
# will be used if it exists. Otherwise, the link will be to '#'.
|
45
|
+
def login_link(text="Login", path=nil)
|
46
|
+
target = path || respond_to?(:login_path) && login_path || '#'
|
47
|
+
link_to text, target, class: :browserid_login
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: Renders a logout link which will clear the current BrowserID
|
51
|
+
# authentication status.
|
52
|
+
#
|
53
|
+
# text - String to use as link text (default: 'Logout').
|
54
|
+
# path - String path to link to. If not provided, the `logout_path` helper
|
55
|
+
# will be used if it exists. Otherwise, the link will be to '#'.
|
56
|
+
def logout_link(text="Logout", path=nil)
|
57
|
+
target = path || respond_to?(:logout_path) && logout_path || '#'
|
58
|
+
link_to text, target, class: :browserid_logout
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
module BrowserID
|
5
|
+
module Verifier
|
6
|
+
# Public: This class sends the assertion to Mozilla's Persona server for
|
7
|
+
# verification.
|
8
|
+
class Persona
|
9
|
+
attr_accessor :server, :path
|
10
|
+
|
11
|
+
# Public: String defining the endpoint of the server to perform Persona
|
12
|
+
# verifications against.
|
13
|
+
VERIFICATION_SERVER = 'verifier.login.persona.org'
|
14
|
+
|
15
|
+
# Public: String defining the normal path to POST assertion verifications
|
16
|
+
# to.
|
17
|
+
VERIFICATION_PATH = '/verify'
|
18
|
+
|
19
|
+
# Public: Constructs a new Persona verifier.
|
20
|
+
#
|
21
|
+
# server - Domain String of the server to send assertions to for
|
22
|
+
# verifications (default: VERIFICATION_SERVER).
|
23
|
+
# path - Path String to POST to on the server (default:
|
24
|
+
# VERIFICATION_PATH).
|
25
|
+
#
|
26
|
+
def initialize(server=VERIFICATION_SERVER, path=VERIFICATION_PATH)
|
27
|
+
@server = server
|
28
|
+
@path = path
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Verifies a Persona assertion for a given audience.
|
32
|
+
#
|
33
|
+
# assertion - Persona authentication assertion.
|
34
|
+
# audience - Audience String to verify assertion against. This should be
|
35
|
+
# the URI of the service with scheme, authority, and port.
|
36
|
+
#
|
37
|
+
# Returns the authenticated email address String and the issuing domain
|
38
|
+
# if the assertion is valid.
|
39
|
+
# Raises an exception with a failure message if the client was not
|
40
|
+
# successfully authenticated.
|
41
|
+
#
|
42
|
+
# Examples
|
43
|
+
#
|
44
|
+
# verify(assertion, "https://app.example.com:443")
|
45
|
+
# # => "user@example.com", "persona.mozilla.com"
|
46
|
+
#
|
47
|
+
def verify(assertion, audience)
|
48
|
+
http = Net::HTTP.new(@server, 443)
|
49
|
+
http.use_ssl = true
|
50
|
+
|
51
|
+
verification = Net::HTTP::Post.new(@path)
|
52
|
+
verification.set_form_data(assertion: assertion, audience: audience)
|
53
|
+
|
54
|
+
response = http.request(verification)
|
55
|
+
raise "Unsuccessful response from #{@server}: #{response}" unless response.kind_of? Net::HTTPSuccess
|
56
|
+
authentication = JSON.parse(response.body)
|
57
|
+
|
58
|
+
# Authentication response is a JSON hash which must contain a 'status'
|
59
|
+
# of "okay" or "failure".
|
60
|
+
status = authentication['status']
|
61
|
+
raise "Unknown authentication status '#{status}'" unless %w{okay failure}.include? status
|
62
|
+
|
63
|
+
# An unsuccessful authentication response should contain a reason string.
|
64
|
+
raise "Assertion failure: #{authentication['reason']}" unless status == "okay"
|
65
|
+
|
66
|
+
# A successful response looks like the following:
|
67
|
+
# {
|
68
|
+
# "status": "okay",
|
69
|
+
# "email": "user@example.com",
|
70
|
+
# "audience": "https://service.example.com:443",
|
71
|
+
# "expires": 1234567890,
|
72
|
+
# "issuer": "persona.mozilla.com"
|
73
|
+
# }
|
74
|
+
|
75
|
+
auth_audience = authentication['audience']
|
76
|
+
raise "Persona assertion audience '#{auth_audience}' does not match verifier audience '#{audience}'" unless auth_audience == audience
|
77
|
+
|
78
|
+
expires = authentication['expires'] && Time.at(authentication['expires'].to_i/1000.0)
|
79
|
+
raise "Persona assertion expired at #{expires}" if expires && expires < Time.now
|
80
|
+
|
81
|
+
[authentication['email'], authentication['issuer']]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: browserid-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Greg Look
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: railties
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.1'
|
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: '3.1'
|
30
|
+
description:
|
31
|
+
email:
|
32
|
+
- greg@mvxcvi.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- app/views/layouts/_browserid.html.erb
|
38
|
+
- app/assets/javascripts/browserid.js.coffee
|
39
|
+
- lib/browserid/rails/helpers.rb
|
40
|
+
- lib/browserid/rails/version.rb
|
41
|
+
- lib/browserid/rails/base.rb
|
42
|
+
- lib/browserid/verifier/persona.rb
|
43
|
+
- lib/browserid-rails.rb
|
44
|
+
- LICENSE
|
45
|
+
- README.md
|
46
|
+
homepage: https://github.com/mvxcvi/browserid-rails
|
47
|
+
licenses: []
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
hash: -488449078399574217
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
hash: -488449078399574217
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.8.24
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Rails authentication framework using Mozilla Persona and the BrowserID protocol.
|
76
|
+
test_files: []
|