browserid-rails 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -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,5 @@
1
+ module BrowserID
2
+ module Rails
3
+ VERSION = "0.4.0"
4
+ end
5
+ 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: []