├── .gitignore ├── config ├── locales │ ├── client.en.yml │ └── server.en.yml └── settings.yml ├── LICENSE.txt ├── assets └── javascripts │ └── auth0.js ├── plugin.rb └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | auto_generated -------------------------------------------------------------------------------- /config/locales/client.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | admin_js: 3 | admin: 4 | site_settings: 5 | categories: 6 | auth0: 'Auth0' 7 | -------------------------------------------------------------------------------- /config/settings.yml: -------------------------------------------------------------------------------- 1 | auth0: 2 | auth0_domain: 3 | client: true 4 | default: 'YOUR-AUTH0-ACCOUNT.auth0.com' 5 | shadowed_by_global: true 6 | auth0_client_id: 7 | client: true 8 | default: '' 9 | shadowed_by_global: true 10 | auth0_client_secret: 11 | default: '' 12 | shadowed_by_global: true 13 | auth0_callback_url: 14 | client: true 15 | default: 'http://your-discourse-url.com/auth/auth0/callback' 16 | shadowed_by_global: true 17 | auth0_connection: 18 | client: true 19 | default: '' 20 | shadowed_by_global: true 21 | -------------------------------------------------------------------------------- /config/locales/server.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | site_settings: 3 | auth0_domain: 'The Auth0 domain, typically .auth0.com' 4 | auth0_client_id: 'The Auth0 client ID. Find this on the list of clients within the Auth0 dashboard.' 5 | auth0_client_secret: 'The corresponding secret for the above Auth0 client ID, find this by visiting the settings for the client within the Auth0 dashboard.' 6 | auth0_callback_url: 'Where Auth0 will redirect to after authentication, typically https:///auth/auth0/callback' 7 | auth0_connection: '(Optional) Use a specific authentication provider from Auth0.' 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Auth0, Inc. (http://auth0.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/javascripts/auth0.js: -------------------------------------------------------------------------------- 1 | /* global Auth0Lock */ 2 | (function () { 3 | function appendScript(src, callback) { 4 | var new_script = document.createElement('script'); 5 | new_script.setAttribute('src',src); 6 | new_script.onload = callback; 7 | document.head.appendChild(new_script); 8 | } 9 | 10 | var lock; 11 | 12 | var script_url = '//cdn.auth0.com/js/lock-9.2.js'; 13 | 14 | appendScript(script_url, function () { 15 | var checkInterval = setInterval(function () { 16 | if (!Discourse.SiteSettings) { 17 | return; 18 | } 19 | 20 | clearInterval(checkInterval); 21 | 22 | if (!Discourse.SiteSettings.auth0_client_id) { 23 | return; 24 | } 25 | 26 | var client_id = Discourse.SiteSettings.auth0_client_id; 27 | var domain = Discourse.SiteSettings.auth0_domain; 28 | 29 | lock = new Auth0Lock(client_id, domain); 30 | 31 | }, 300); 32 | }); 33 | 34 | var LoginController = require('discourse/controllers/login').default; 35 | LoginController.reopen({ 36 | authenticationComplete: function () { 37 | if (lock) { 38 | lock.hide(); 39 | } 40 | return this._super.apply(this, arguments); 41 | } 42 | }); 43 | 44 | var ApplicationRoute = require('discourse/routes/application').default; 45 | ApplicationRoute.reopen({ 46 | actions: { 47 | showLogin: function() { 48 | if (!Discourse.SiteSettings.auth0_client_id || Discourse.SiteSettings.auth0_connection !== '') { 49 | return this._super(); 50 | } 51 | 52 | lock.show({ 53 | popup: true, 54 | responseType: 'code', 55 | callbackURL: Discourse.SiteSettings.auth0_callback_url 56 | }); 57 | 58 | this.controllerFor('login').resetForm(); 59 | }, 60 | showCreateAccount: function () { 61 | if (!Discourse.SiteSettings.auth0_client_id || Discourse.SiteSettings.auth0_connection !== '') { 62 | return this._super(); 63 | } 64 | 65 | var createAccountController = Discourse.__container__.lookup('controller:createAccount'); 66 | 67 | if (createAccountController && createAccountController.accountEmail) { 68 | if (lock) { 69 | lock.hide(); 70 | Discourse.Route.showModal(this, 'createAccount'); 71 | } else { 72 | this._super(); 73 | } 74 | } else { 75 | lock.show({ 76 | mode: 'signup', 77 | popup: true, 78 | responseType: 'code', 79 | callbackURL: Discourse.SiteSettings.auth0_callback_url 80 | }); 81 | } 82 | } 83 | } 84 | }); 85 | 86 | })(); 87 | -------------------------------------------------------------------------------- /plugin.rb: -------------------------------------------------------------------------------- 1 | # name: auth0 2 | # about: Authenticate with auth0 3 | # version: 2.1.1 4 | # authors: Jose Romaniello 5 | 6 | require 'auth/oauth2_authenticator' 7 | 8 | require File.dirname(__FILE__) + "/../../app/models/oauth2_user_info" 9 | 10 | class Auth0Authenticator < ::Auth::OAuth2Authenticator 11 | 12 | def after_authenticate(auth_token) 13 | return super(auth_token) if SiteSetting.auth0_connection != '' 14 | 15 | result = Auth::Result.new 16 | 17 | oauth2_uid = auth_token[:uid] 18 | data = auth_token[:info] 19 | result.email = email = data[:email] 20 | result.name = name = data[:name] 21 | 22 | oauth2_user_info = Oauth2UserInfo.where(uid: oauth2_uid, provider: 'Auth0').first 23 | 24 | result.extra_data = { 25 | uid: oauth2_uid, 26 | provider: 'Auth0', 27 | name: name, 28 | email: email, 29 | } 30 | 31 | result.user = oauth2_user_info.try(:user) 32 | result.email_valid = data[:email] && data[:email_verified] 33 | 34 | if !result.user && !email.blank? && result.email_valid 35 | if result.user = User.find_by_email(email) 36 | Oauth2UserInfo.create({ uid: oauth2_uid, 37 | provider: 'Auth0', 38 | name: name, 39 | email: email, 40 | user_id: result.user.id }) 41 | end 42 | end 43 | 44 | result 45 | end 46 | 47 | def register_middleware(omniauth) 48 | omniauth.provider :auth0, 49 | :setup => lambda { |env| 50 | strategy = env["omniauth.strategy"] 51 | strategy.options[:client_id] = SiteSetting.auth0_client_id 52 | strategy.options[:client_secret] = SiteSetting.auth0_client_secret 53 | strategy.options[:connection] = SiteSetting.auth0_connection 54 | 55 | domain = SiteSetting.auth0_domain 56 | 57 | strategy.options[:domain] = domain 58 | strategy.options[:client_options].site = "https://#{domain}" 59 | strategy.options[:client_options].authorize_url = "https://#{domain}/authorize" 60 | strategy.options[:client_options].token_url = "https://#{domain}/oauth/token" 61 | strategy.options[:client_options].userinfo_url = "https://#{domain}/userinfo" 62 | } 63 | 64 | end 65 | end 66 | 67 | require 'omniauth-oauth2' 68 | class OmniAuth::Strategies::Auth0 < OmniAuth::Strategies::OAuth2 69 | PASSTHROUGHS = %w[ 70 | connection 71 | redirect_uri 72 | ] 73 | 74 | option :name, "auth0" 75 | option :domain, nil 76 | option :provider_ignores_state, true 77 | option :connection, "" 78 | 79 | def authorize_params 80 | super.tap do |param| 81 | param[:connection] = options.connection 82 | PASSTHROUGHS.each do |p| 83 | param[p.to_sym] = request.params[p] if request.params[p] 84 | end 85 | end 86 | end 87 | 88 | uid { raw_info["user_id"] } 89 | 90 | extra do 91 | { :raw_info => raw_info } 92 | end 93 | 94 | info do 95 | { 96 | :name => raw_info["name"], 97 | :email => raw_info["email"], 98 | :nickname => raw_info["nickname"], 99 | :first_name => raw_info["given_name"], 100 | :last_name => raw_info["family_name"], 101 | :location => raw_info["locale"], 102 | :image => raw_info["picture"], 103 | :email_verified => raw_info["email_verified"] 104 | } 105 | end 106 | 107 | def raw_info 108 | @raw_info ||= access_token.get(options.client_options.userinfo_url).parsed 109 | end 110 | end 111 | 112 | register_asset "javascripts/auth0.js" 113 | 114 | auth_provider :title => 'Auth0', 115 | :message => 'Log in via Auth0', 116 | :frame_width => 920, 117 | :frame_height => 800, 118 | :authenticator => Auth0Authenticator.new('auth0', trusted: true) 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | Please note that this plugin is no longer officially supported. We recommend using the [official Discourse OAuth2 plugin](https://github.com/discourse/discourse-oauth2-basic) instead. 4 | 5 | 6 | Discourse + Auth0 7 | ================= 8 | 9 | This is a [Discourse](http://discourse.org) plugin to do Single Sign On using Auth0. 10 | 11 | ### Demo: https://auth0.com/forum/ 12 | 13 | ![discourse login](https://dl.dropboxusercontent.com/u/21665105/discourse-login.gif) 14 | 15 | ## What do I get by using Auth0? 16 | 17 | * Support for Active Directory / LDAP (see [animated gif](#adding-active-directory--ldap)) 18 | * No matter if Discourse is on the cloud or on-prem, it will work transparently 19 | * Support for Kerberos too (configured by IP ranges) 20 | * Support for other enterprise logins like SAML Protocol, Windows Azure AD, Google Apps, Salesforce, etc. All supported here: https://docs.auth0.com/identityproviders. 21 | * Support for social providers without having to add OmniAuth strategies by hand. Just turn on/off social providers (see [animated gif](#adding-social-providers)) 22 | * Support for Single Sign On with other Discourse instances and any other application in your account (see [animated gif](#single-sign-on-between-multiple-discourse-forums). 23 | 24 | ## Installation 25 | 26 | - Create an account on [Auth0](https://auth0.com) and open the application settings. 27 | 28 | - Install Discourse. [You can use this guide to install Discourse on any platform](https://github.com/discourse/discourse/blob/master/docs/INSTALL-digital-ocean.md) 29 | 30 | - Edit your `containers/app.yml` to include this under `hooks > after_code > exec > cmd`: 31 | 32 | - git clone https://github.com/auth0/discourse-plugin.git auth0 33 | 34 | - Follow the rest of the tutorial 35 | 36 | - Login as an administrator using a discourse account (not auth0 yet) 37 | 38 | - Configure your settings as shown in this image 39 | 40 | ̇ 41 | 42 | Enjoy! 43 | 44 | ## Email verification 45 | 46 | In order to login to discourse the email of the user should be verified either at the Auth0 service level or in Discourse itself. 47 | 48 | Some Social Providers already verify the email but others not. If the user hasn't verified the email it will receive two emails the first one from Auth0 and the second one from Discourse. This can be confusing for the end-user, a simple fix is to only allow verified users to sign in to Discourse by using an Auth0 Rule like this: 49 | 50 | ```javascript 51 | function (user, context, callback) { 52 | if (!user.email_verified && context.clientID === 'introduce-discourse-client-id') { 53 | return callback(new UnauthorizedError('Please verify your email and sign in again.')); 54 | } 55 | return callback(null, user, context); 56 | } 57 | ``` 58 | 59 | ---- 60 | 61 | #### Adding Active Directory / LDAP 62 | 63 | ![active directory config](https://dl.dropboxusercontent.com/u/21665105/ad-connection.gif) 64 | 65 | #### Adding Social Providers 66 | 67 | ![social providers config](https://dl.dropboxusercontent.com/u/21665105/social-connections.gif) 68 | 69 | #### Single Sign On Between multiple Discourse forums 70 | 71 | ![single sign on](https://dl.dropboxusercontent.com/u/21665105/sso-discourse.gif) 72 | 73 | #### Single Sign On with Windows Authentication 74 | 75 | ![windows auth](https://s3.amazonaws.com/blog.auth0.com/login_discourse_kerberos-2.gif) 76 | 77 | ### Using Discourse Login Dialog instead of Auth0 78 | 79 | You can keep using Discourse Login dialog and integrate only a specific connection from Auth0. It will show up as another button like the social providers. 80 | 81 | Go to admin site settings for Auth0 and change the `auth0_connection` with the connection name you want to use from Auth0. 82 | 83 | ![](https://s3.amazonaws.com/blog.auth0.com/login_discourse_ad.gif) 84 | 85 | ### Give admin rights to an email 86 | 87 | ``` 88 | $ RAILS_ENV=production bundle exec rails c 89 | $ u = User.find_by_email('the-email-you-want-to-make-admin@whatever.com') 90 | $ u.admin = true 91 | $ u.save! 92 | ``` 93 | ## What is Auth0? 94 | 95 | Auth0 helps you to: 96 | 97 | * Add authentication with [multiple authentication sources](https://docs.auth0.com/identityproviders), either social like **Google, Facebook, Microsoft Account, LinkedIn, GitHub, Twitter, Box, Salesforce, amont others**, or enterprise identity systems like **Windows Azure AD, Google Apps, Active Directory, ADFS or any SAML Identity Provider**. 98 | * Add authentication through more traditional **[username/password databases](https://docs.auth0.com/mysql-connection-tutorial)**. 99 | * Add support for **[linking different user accounts](https://docs.auth0.com/link-accounts)** with the same user. 100 | * Support for generating signed [Json Web Tokens](https://docs.auth0.com/jwt) to call your APIs and **flow the user identity** securely. 101 | * Analytics of how, when and where users are logging in. 102 | * Pull data from other sources and add it to the user profile, through [JavaScript rules](https://docs.auth0.com/rules). 103 | 104 | ## Create a free Auth0 Account 105 | 106 | 1. Go to [Auth0](https://auth0.com) and click Sign Up. 107 | 2. Use Google, GitHub or Microsoft Account to login. 108 | 109 | ## Issue Reporting 110 | 111 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 112 | 113 | ## Author 114 | 115 | [Auth0](https://auth0.com) 116 | 117 | ## License 118 | 119 | This project is licensed under the MIT license. See the [LICENSE](LICENSE.txt) file for more info. 120 | --------------------------------------------------------------------------------