From fd4d384dc12ef5e80ac83d52a8524fd753270c16 Mon Sep 17 00:00:00 2001
From: Dan Callahan <dan.callahan@gmail.com>
Date: Mon, 25 Feb 2013 18:10:16 -0600
Subject: [PATCH 1/5] Add Mozilla Persona Omniauth Gem and initializer

---
 Gemfile                         |  1 +
 Gemfile.lock                    | 11 +++++++++++
 config/initializers/omniauth.rb |  3 +++
 3 files changed, 15 insertions(+)

diff --git a/Gemfile b/Gemfile
index 514329c0a..99e71f0b5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -36,6 +36,7 @@ gem "openid-redis-store"
 gem "omniauth-facebook"
 gem "omniauth-twitter"
 gem "omniauth-github"
+gem "omniauth-browserid", :git => "git://github.com/callahad/omniauth-browserid.git", :branch => "observer_api"
 gem 'oj'
 gem 'pbkdf2'
 gem 'pg'
diff --git a/Gemfile.lock b/Gemfile.lock
index a5a628f60..6d9dd2047 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -5,6 +5,16 @@ GIT
     rack-mini-profiler (0.1.23)
       rack (>= 1.1.3)
 
+GIT
+  remote: git://github.com/callahad/omniauth-browserid.git
+  revision: af62d667626c1622de6fe13b60849c3640765ab1
+  branch: observer_api
+  specs:
+    omniauth-browserid (0.0.2)
+      faraday
+      multi_json
+      omniauth (~> 1.0)
+
 GIT
   remote: git://github.com/emberjs/ember-rails.git
   revision: 57bbe3202725e55a8e4eaccba83d663b26bcf024
@@ -490,6 +500,7 @@ DEPENDENCIES
   nokogiri
   oj
   omniauth
+  omniauth-browserid!
   omniauth-facebook
   omniauth-github
   omniauth-openid
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index 838006e8a..5c393bdc2 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -31,4 +31,7 @@ Rails.application.config.middleware.use OmniAuth::Builder do
            SiteSetting.github_client_id,
            SiteSetting.github_client_secret
 
+  provider :browser_id,
+           :name => 'persona'
+
 end

From 81c545539e341e47eeb9663f450efd308bb69ed5 Mon Sep 17 00:00:00 2001
From: Dan Callahan <dan.callahan@gmail.com>
Date: Thu, 28 Feb 2013 18:32:40 -0600
Subject: [PATCH 2/5] Add (ui-only) Persona button to login modal

---
 .../discourse/templates/modal/login.js.handlebars           | 2 ++
 app/assets/stylesheets/components/buttons.css.scss          | 6 ++++++
 app/assets/stylesheets/foundation/variables.scss            | 3 ++-
 config/locales/client.en.yml                                | 3 +++
 4 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/app/assets/javascripts/discourse/templates/modal/login.js.handlebars b/app/assets/javascripts/discourse/templates/modal/login.js.handlebars
index 638db920d..b98ac1d9f 100644
--- a/app/assets/javascripts/discourse/templates/modal/login.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/modal/login.js.handlebars
@@ -16,6 +16,8 @@
     {{#if Discourse.SiteSettings.enable_github_logins}}
       <button class="btn btn-social github" title="{{i18n login.github.title}}" {{action "githubLogin" target="view"}}>{{i18n login.github.title}}</button>
     {{/if}}
+    <br>
+    <button class="btn btn-social persona" title="{{i18n login.persona.title}}" {{action "personaLogin" target="view"}}>{{i18n login.persona.title}}</button>
   </div>
   <h3 style="text-align:center; margin-bottom:10px;">
     {{i18n login.or}}
diff --git a/app/assets/stylesheets/components/buttons.css.scss b/app/assets/stylesheets/components/buttons.css.scss
index 74fdda739..0f0520945 100755
--- a/app/assets/stylesheets/components/buttons.css.scss
+++ b/app/assets/stylesheets/components/buttons.css.scss
@@ -168,6 +168,12 @@
       content: "g";
     }
   }
+  &.persona {
+    background: $persona;
+    &:before {
+      content: "]";
+    }
+  }
 }
 
 // Button Sizes
diff --git a/app/assets/stylesheets/foundation/variables.scss b/app/assets/stylesheets/foundation/variables.scss
index 4407937c8..a6bdf11a9 100644
--- a/app/assets/stylesheets/foundation/variables.scss
+++ b/app/assets/stylesheets/foundation/variables.scss
@@ -124,6 +124,7 @@ $facebook: #3b5998 !default;
 $twitter: #00bced !default;
 $yahoo: #810293 !default;
 $github: #6d6d6d !default;
+$persona: #606060 !default;
 
 // Layout dimensions
 
@@ -174,4 +175,4 @@ $heart: #fa6c8d;
 $attention_bg: #e4f2f8;
 $attention_fg: #1aaae4;
 
-$header-item-highlight: #ecf8f6;
\ No newline at end of file
+$header-item-highlight: #ecf8f6;
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index ae45c0087..4bd3c6954 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -251,6 +251,9 @@ en:
       github:
         title: "Log In with Github"
         message: "Authenticating with Github (make sure pop up blockers are not enabled)"
+      persona:
+        title: "Log In with Any Email Address (Mozilla Persona)"
+        message: "Authenticating with Persona (make sure pop up blockers are not enabled)"
 
     composer:
       saving_draft_tip: "saving"

From ef8cf2f734a06c814ee2f80f37fc3bd5d9be40d2 Mon Sep 17 00:00:00 2001
From: Dan Callahan <dan.callahan@gmail.com>
Date: Fri, 1 Mar 2013 09:23:21 -0600
Subject: [PATCH 3/5] Add basic Persona functionality

1. No session integration yet, so automatic login/logout events are suppressed.

2. Popup blockers must be disabled: submits form to target="_blank"
---
 .../discourse/views/modal/login_view.js       |  4 +++
 .../users/omniauth_callbacks_controller.rb    | 34 +++++++++++++++++++
 app/views/common/_persona_javascript.html.erb | 25 ++++++++++++++
 app/views/layouts/application.html.erb        |  1 +
 4 files changed, 64 insertions(+)
 create mode 100644 app/views/common/_persona_javascript.html.erb

diff --git a/app/assets/javascripts/discourse/views/modal/login_view.js b/app/assets/javascripts/discourse/views/modal/login_view.js
index f11712ab3..57b88dca1 100644
--- a/app/assets/javascripts/discourse/views/modal/login_view.js
+++ b/app/assets/javascripts/discourse/views/modal/login_view.js
@@ -109,6 +109,10 @@ Discourse.LoginView = Discourse.ModalBodyView.extend({
     return window.open("/auth/github", "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
   },
 
+  personaLogin: function() {
+      navigator.id.request();
+  },
+
   authenticationComplete: function(options) {
     if (options.awaiting_approval) {
       this.flash(Em.String.i18n('login.awaiting_approval'), 'success');
diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb
index f876fcdde..50350b36a 100644
--- a/app/controllers/users/omniauth_callbacks_controller.rb
+++ b/app/controllers/users/omniauth_callbacks_controller.rb
@@ -21,6 +21,8 @@ class Users::OmniauthCallbacksController < ApplicationController
       create_or_sign_on_user_using_openid(auth_token)
     when "github"
       create_or_sign_on_user_using_github(auth_token)
+    when "persona"
+      create_or_sign_on_user_using_persona(auth_token)
     end
   end
 
@@ -169,6 +171,7 @@ class Users::OmniauthCallbacksController < ApplicationController
         openid_url: identity_url
       }
     end
+
   end
 
   def create_or_sign_on_user_using_github(auth_token)
@@ -200,6 +203,37 @@ class Users::OmniauthCallbacksController < ApplicationController
     else
       @data[:name] = screen_name
     end
+
+  end
+
+  def create_or_sign_on_user_using_persona(auth_token)
+
+    email = auth_token[:info][:email]
+
+    user = User.find_by_email(email)
+
+    if user
+      if SiteSetting.must_approve_users? and !user.approved?
+        @data = {awaiting_approval: true}
+      else
+        log_on_user(user)
+        @data = {authenticated: true}
+      end
+    else
+      @data = {
+        email: email,
+        email_valid: true,
+        name: User.suggest_name(email),
+        username: User.suggest_username(email),
+        auth_provider: params[:provider].try(:capitalize)
+      }
+
+      session[:authentication] = {
+        email: email,
+        email_valid: true,
+      }
+    end
+
   end
 
 end
diff --git a/app/views/common/_persona_javascript.html.erb b/app/views/common/_persona_javascript.html.erb
new file mode 100644
index 000000000..f6448e28a
--- /dev/null
+++ b/app/views/common/_persona_javascript.html.erb
@@ -0,0 +1,25 @@
+<script src="https://login.persona.org/include.js"></script>
+<form id="persona_assertion_form" action="/auth/persona/callback" method="post" target="_blank">
+  <input type='hidden' name='assertion'/>
+</form>
+<script>
+  (function() {
+    var readyCalled = false;
+    navigator.id.watch({
+      onlogin: function(assertion) {
+        if (readyCalled) {
+          $('#persona_assertion_form input[name=assertion]').val(assertion);
+          $('#persona_assertion_form').submit();
+        }
+      },
+      onlogout: function() {
+        if (readyCalled) {
+          Discourse.logout();
+        }
+      },
+      onready: function() {
+        readyCalled = true;
+      }
+    });
+  }());
+</script>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index e1b44ca5b..a3fd6e0a9 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -58,6 +58,7 @@
     <footer id='bottom'></footer>
 
     <%= render :partial => "common/discourse_javascript" %>
+    <%= render :partial => "common/persona_javascript" %>
     <%= render_google_analytics_code %>
 
     <!-- Discourse Version: <%= Discourse::VERSION::STRING %> -->

From 23d812a4ab0506d4b588b13abf82ebe192a47fac Mon Sep 17 00:00:00 2001
From: Dan Callahan <dan.callahan@gmail.com>
Date: Fri, 1 Mar 2013 13:22:54 -0600
Subject: [PATCH 4/5] Use AJAX for submitting Persona credentials.

Fixes issue with needing to unblock popups.
---
 .../users/omniauth_callbacks_controller.rb        |  5 +++++
 app/views/common/_persona_javascript.html.erb     | 15 ++++++++++-----
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb
index 50350b36a..9a57c58eb 100644
--- a/app/controllers/users/omniauth_callbacks_controller.rb
+++ b/app/controllers/users/omniauth_callbacks_controller.rb
@@ -24,6 +24,11 @@ class Users::OmniauthCallbacksController < ApplicationController
     when "persona"
       create_or_sign_on_user_using_persona(auth_token)
     end
+
+    respond_to do |format|
+      format.html
+      format.json { render :json => @data }
+    end
   end
 
   def failure
diff --git a/app/views/common/_persona_javascript.html.erb b/app/views/common/_persona_javascript.html.erb
index f6448e28a..48d654f7a 100644
--- a/app/views/common/_persona_javascript.html.erb
+++ b/app/views/common/_persona_javascript.html.erb
@@ -1,15 +1,20 @@
 <script src="https://login.persona.org/include.js"></script>
-<form id="persona_assertion_form" action="/auth/persona/callback" method="post" target="_blank">
-  <input type='hidden' name='assertion'/>
-</form>
 <script>
   (function() {
     var readyCalled = false;
     navigator.id.watch({
       onlogin: function(assertion) {
         if (readyCalled) {
-          $('#persona_assertion_form input[name=assertion]').val(assertion);
-          $('#persona_assertion_form').submit();
+
+          $.ajax({
+            type: 'POST',
+            url: '/auth/persona/callback',
+            data: { 'assertion': assertion },
+            success: function(data, textStatus, jqXHR) {
+              Discourse.authenticationComplete(data);
+            },
+            dataType: 'json'
+          });
         }
       },
       onlogout: function() {

From 7c87359e02925e14ec43d04bf6ef570a99e71359 Mon Sep 17 00:00:00 2001
From: Dan Callahan <dan.callahan@gmail.com>
Date: Fri, 1 Mar 2013 13:51:00 -0600
Subject: [PATCH 5/5] Make Persona configurable

---
 .../javascripts/discourse/templates/modal/login.js.handlebars   | 2 ++
 app/models/site_setting.rb                                      | 2 ++
 app/views/layouts/application.html.erb                          | 2 +-
 config/locales/server.en.yml                                    | 2 ++
 4 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/app/assets/javascripts/discourse/templates/modal/login.js.handlebars b/app/assets/javascripts/discourse/templates/modal/login.js.handlebars
index b98ac1d9f..3546063dc 100644
--- a/app/assets/javascripts/discourse/templates/modal/login.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/modal/login.js.handlebars
@@ -16,8 +16,10 @@
     {{#if Discourse.SiteSettings.enable_github_logins}}
       <button class="btn btn-social github" title="{{i18n login.github.title}}" {{action "githubLogin" target="view"}}>{{i18n login.github.title}}</button>
     {{/if}}
+    {{#if Discourse.SiteSettings.enable_persona_logins}}
     <br>
     <button class="btn btn-social persona" title="{{i18n login.persona.title}}" {{action "personaLogin" target="view"}}>{{i18n login.persona.title}}</button>
+    {{/if}}
   </div>
   <h3 style="text-align:center; margin-bottom:10px;">
     {{i18n login.or}}
diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb
index 8eee42695..1cd0067a3 100644
--- a/app/models/site_setting.rb
+++ b/app/models/site_setting.rb
@@ -124,6 +124,8 @@ class SiteSetting < ActiveRecord::Base
   setting(:github_client_id, '')
   setting(:github_client_secret, '')
 
+  client_setting(:enable_persona_logins, true)
+
   setting(:enforce_global_nicknames, true)
   setting(:discourse_org_access_key, '')
   setting(:enable_s3_uploads, false)
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index a3fd6e0a9..81fdd1c6c 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -58,7 +58,7 @@
     <footer id='bottom'></footer>
 
     <%= render :partial => "common/discourse_javascript" %>
-    <%= render :partial => "common/persona_javascript" %>
+    <%= render :partial => "common/persona_javascript" if SiteSetting.enable_persona_logins %>
     <%= render_google_analytics_code %>
 
     <!-- Discourse Version: <%= Discourse::VERSION::STRING %> -->
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 9c2433324..3fc39816f 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -339,6 +339,8 @@ en:
     github_client_id: "Client id for Github authentication, registered at https://github.com/settings/applications"
     github_client_secret: "Client secret for Github authentication, registered at https://github.com/settings/applications"
 
+    enable_persona_logins: "Enable email-based authentication with Mozilla Persona"
+
     allow_import: "Allow import, which can replace ALL site data; leave false unless you plan to do data imports"
 
     active_user_rate_limit_secs: "How frequently we update the 'last_seen_at' field, in seconds"