Use sorcery for logins

This gives us more flexibility and allows us to use password authentication
later. Also we don't need to build the login functionality ourself.
This commit is contained in:
bumi 2020-04-13 14:59:07 +02:00
parent 73c184a4a0
commit c478cfc7af
12 changed files with 642 additions and 62 deletions

View File

@ -29,6 +29,7 @@ gem 'google-api-client'
gem 'rack-cors' gem 'rack-cors'
gem 'sentry-raven' gem 'sentry-raven'
gem 'sequenced' gem 'sequenced'
gem 'sorcery'
group :development, :test do group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console # Call 'byebug' anywhere in the code to stop execution and get a debugger console

View File

@ -74,6 +74,7 @@ GEM
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sigv4 (1.1.0) aws-sigv4 (1.1.0)
aws-eventstream (~> 1.0, >= 1.0.2) aws-eventstream (~> 1.0, >= 1.0.2)
bcrypt (3.1.13)
bootsnap (1.4.6) bootsnap (1.4.6)
msgpack (~> 1.0) msgpack (~> 1.0)
builder (3.2.4) builder (3.2.4)
@ -133,10 +134,18 @@ GEM
minitest (5.14.0) minitest (5.14.0)
msgpack (1.3.3) msgpack (1.3.3)
multi_json (1.14.1) multi_json (1.14.1)
multi_xml (0.6.0)
multipart-post (2.1.1) multipart-post (2.1.1)
nio4r (2.5.2) nio4r (2.5.2)
nokogiri (1.10.9) nokogiri (1.10.9)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
oauth (0.5.4)
oauth2 (1.4.4)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
os (1.1.0) os (1.1.0)
pg (1.2.3) pg (1.2.3)
public_suffix (4.0.4) public_suffix (4.0.4)
@ -205,6 +214,10 @@ GEM
faraday (>= 0.17.3, < 2.0) faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0) jwt (>= 1.5, < 3.0)
multi_json (~> 1.10) multi_json (~> 1.10)
sorcery (0.14.0)
bcrypt (~> 3.1)
oauth (~> 0.4, >= 0.4.4)
oauth2 (~> 1.0, >= 0.8.0)
spring (2.1.0) spring (2.1.0)
spring-watcher-listen (2.0.1) spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0) listen (>= 2.7, < 4.0)
@ -254,6 +267,7 @@ DEPENDENCIES
sass-rails sass-rails
sentry-raven sentry-raven
sequenced sequenced
sorcery
spring spring
spring-watcher-listen spring-watcher-listen
turbolinks turbolinks

View File

@ -37,4 +37,9 @@ $light: #fff;
body { body {
min-height: 100vh; min-height: 100vh;
}
.button.google {
background: rgb(225, 98, 89);
color: white;
} }

View File

@ -0,0 +1,34 @@
class OauthsController < ApplicationController
# Sends the user on a trip to the provider,
# and after authorizing there back to the callback url.
def oauth
login_at(params[:provider])
end
def callback
provider = params[:provider]
if @user = login_from(provider)
redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
else
begin
@user = create_from(provider)
if authentication = @user.authentications.find_by(provider: provider)
authentication.update({
access_token: @access_token.token,
refresh_token: @access_token.refresh_token,
expires_at: Time.at(@access_token.expires_at)
})
end
reset_session
auto_login(@user)
redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
rescue
Rails.logger.error("Failed to login from #{provider}")
redirect_to root_path, :alert => "Failed to login from #{provider.titleize}!"
end
end
end
end

View File

@ -1,29 +1,7 @@
require 'google/apis/oauth2_v2'
class SessionsController < ApplicationController class SessionsController < ApplicationController
def new def new
reset_session reset_session
redirect_to auth_client.authorization_uri.to_s
end
def auth
reset_session
if params[:error]
flash[:error] = 'Login failed'
redirect_to root_url
else
auth_client.code = params[:code]
auth_client.fetch_access_token!
@user, @authentication = User.find_by_oauth_info(auth_client)
if @user.persisted? && @authentication.persisted?
session[:user_id] = @user.id.to_s
redirect_to forms_url
else
flash[:error] = 'Login failed'
redirect_to root_url
end
end
end end
def destroy def destroy
@ -31,18 +9,4 @@ class SessionsController < ApplicationController
redirect_to root_url redirect_to root_url
end end
private
def auth_client
@auth_client ||= CLIENT_SECRETS.to_authorization.tap do |c|
c.update!(
scope: 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/spreadsheets',
redirect_uri: auth_url,
additional_parameters: {
'access_type' => 'offline', # offline access
'include_granted_scopes' => 'true' # incremental auth
}
)
end
end
end end

View File

@ -1,6 +1,8 @@
class Authentication < ApplicationRecord class Authentication < ApplicationRecord
belongs_to :user belongs_to :user
scope :for, -> (provider) { where(provider: provider) }
encrypts :access_token encrypts :access_token
encrypts :refresh_token encrypts :refresh_token
@ -9,6 +11,7 @@ class Authentication < ApplicationRecord
end end
def google_authorization def google_authorization
return nil unless provider == 'google'
@google_authorization ||= CLIENT_SECRETS.to_authorization.tap do |c| @google_authorization ||= CLIENT_SECRETS.to_authorization.tap do |c|
c.access_token = self.access_token c.access_token = self.access_token
c.refresh_token = self.refresh_token c.refresh_token = self.refresh_token

View File

@ -1,40 +1,18 @@
class User < ApplicationRecord class User < ApplicationRecord
authenticates_with_sorcery!
has_many :authentications, dependent: :destroy has_many :authentications, dependent: :destroy
has_many :forms, dependent: :destroy has_many :forms, dependent: :destroy
def self.find_by_oauth_info(auth_client)
oauth = Google::Apis::Oauth2V2::Oauth2Service.new
oauth.authorization = auth_client
user_info = oauth.get_userinfo
if user = User.find_by(google_id: user_info.id)
authentication = user.authentications.last
authentication.access_token = auth_client.access_token if auth_client.access_token.present?
authentication.refresh_token = auth_client.refresh_token if auth_client.refresh_token.present?
authentication.expires_at = Time.at(auth_client.expires_at) if auth_client.expires_at.present?
authentication.save
return user, authentication
else
user = User.create(name: user_info.name, email: user_info.email, google_id: user_info.id)
authentication = user.authentications.create(
access_token: auth_client.access_token,
refresh_token: auth_client.refresh_token,
expires_at: Time.at(auth_client.expires_at)
)
return user, authentication
end
end
def deactivate!(reason = nil) def deactivate!(reason = nil)
# currently we only use deactivate if we get an authentication exception appending data to a spreadsheet # currently we only use deactivate if we get an authentication exception appending data to a spreadsheet
authentications.last&.update(expires_at: Time.current) authentications.last&.update(expires_at: Time.current)
end end
def active? def active?
authentications.last.present? && !authentications.last.expired? authentications.any? { |a| !a.expired? }
end end
def google_authorization def google_authorization
authentications.last.google_authorization authentications.for(:google).last.google_authorization
end end
end end

View File

@ -0,0 +1,22 @@
<div class="columns">
<div class="column">
</div>
<div class="column">
<div class="container has-text-centered">
<h1 class="title has-text-centered">
Login
</h1>
<div class="button google">
<svg viewBox="0 0 15 15" class="googleLogo" style="width: 14px; height: 14px; display: block; fill: currentcolor; flex-shrink: 0; backface-visibility: hidden; margin-right: 6px;"><path d="M 7.28571 6.4125L 7.28571 9L 11.3929 9C 11.2143 10.0875 10.1429 12.225 7.28571 12.225C 4.78571 12.225 2.78571 10.0875 2.78571 7.5C 2.78571 4.9125 4.82143 2.775 7.28571 2.775C 8.71429 2.775 9.64286 3.4125 10.1786 3.9375L 12.1429 1.9875C 10.8929 0.75 9.25 0 7.28571 0C 3.25 0 0 3.3375 0 7.5C 0 11.6625 3.25 15 7.28571 15C 11.5 15 14.25 11.9625 14.25 7.6875C 14.25 7.2 14.2143 6.825 14.1429 6.45L 7.28571 6.45L 7.28571 6.4125Z"></path></svg>
Login with Google
</div>
<hr>
<p>
As tinyforms builds on Google Sheets.<br>
Simply login with your Google account.
</p>
</div>
</div>
<div class="column">
</div>
</div>

View File

@ -0,0 +1,532 @@
# The first thing you need to configure is which modules you need in your app.
# The default is nothing which will include only core features (password encryption, login/logout).
#
# Available submodules are: :user_activation, :http_basic_auth, :remember_me,
# :reset_password, :session_timeout, :brute_force_protection, :activity_logging,
# :magic_login, :external
Rails.application.config.sorcery.submodules = [:reset_password, :external, :magic_login]
# Here you can configure each submodule's features.
Rails.application.config.sorcery.configure do |config|
# -- core --
# What controller action to call for non-authenticated users. You can also
# override the 'not_authenticated' method of course.
# Default: `:not_authenticated`
#
# config.not_authenticated_action =
# When a non logged-in user tries to enter a page that requires login, save
# the URL he wants to reach, and send him there after login, using 'redirect_back_or_to'.
# Default: `true`
#
# config.save_return_to_url =
# Set domain option for cookies; Useful for remember_me submodule.
# Default: `nil`
#
# config.cookie_domain =
# Allow the remember_me cookie to be set through AJAX
# Default: `true`
#
# config.remember_me_httponly =
# Set token randomness. (e.g. user activation tokens)
# The length of the result string is about 4/3 of `token_randomness`.
# Default: `15`
#
# config.token_randomness =
# -- session timeout --
# How long in seconds to keep the session alive.
# Default: `3600`
#
# config.session_timeout =
# Use the last action as the beginning of session timeout.
# Default: `false`
#
# config.session_timeout_from_last_action =
# Invalidate active sessions. Requires an `invalidate_sessions_before` timestamp column
# Default: `false`
#
# config.session_timeout_invalidate_active_sessions_enabled =
# -- http_basic_auth --
# What realm to display for which controller name. For example {"My App" => "Application"}
# Default: `{"application" => "Application"}`
#
# config.controller_to_realm_map =
# -- activity logging --
# Will register the time of last user login, every login.
# Default: `true`
#
# config.register_login_time =
# Will register the time of last user logout, every logout.
# Default: `true`
#
# config.register_logout_time =
# Will register the time of last user action, every action.
# Default: `true`
#
# config.register_last_activity_time =
# -- external --
# What providers are supported by this app
# i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid, :salesforce, :slack, :line].
# Default: `[]`
#
config.external_providers = [:google]
# You can change it by your local ca_file. i.e. '/etc/pki/tls/certs/ca-bundle.crt'
# Path to ca_file. By default use a internal ca-bundle.crt.
# Default: `'path/to/ca_file'`
#
# config.ca_file =
# config.linkedin.key = ""
# config.linkedin.secret = ""
# config.linkedin.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=linkedin"
# config.linkedin.user_info_mapping = {first_name: "firstName", last_name: "lastName"}
# config.linkedin.scope = "r_basicprofile"
#
#
# For information about XING API:
# - user info fields go to https://dev.xing.com/docs/get/users/me
#
# config.xing.key = ""
# config.xing.secret = ""
# config.xing.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=xing"
# config.xing.user_info_mapping = {first_name: "first_name", last_name: "last_name"}
#
#
# Twitter will not accept any requests nor redirect uri containing localhost,
# Make sure you use 0.0.0.0:3000 to access your app in development
#
# config.twitter.key = ""
# config.twitter.secret = ""
# config.twitter.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=twitter"
# config.twitter.user_info_mapping = {:email => "screen_name"}
#
# config.facebook.key = ""
# config.facebook.secret = ""
# config.facebook.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=facebook"
# config.facebook.user_info_path = "me?fields=email"
# config.facebook.user_info_mapping = {:email => "email"}
# config.facebook.access_permissions = ["email"]
# config.facebook.display = "page"
# config.facebook.api_version = "v2.3"
# config.facebook.parse = :json
#
# config.instagram.key = ""
# config.instagram.secret = ""
# config.instagram.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=instagram"
# config.instagram.user_info_mapping = {:email => "username"}
# config.instagram.access_permissions = ["basic", "public_content", "follower_list", "comments", "relationships", "likes"]
#
# config.github.key = ""
# config.github.secret = ""
# config.github.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=github"
# config.github.user_info_mapping = {:email => "name"}
# config.github.scope = ""
#
# config.paypal.key = ""
# config.paypal.secret = ""
# config.paypal.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=paypal"
# config.paypal.user_info_mapping = {:email => "email"}
#
# config.wechat.key = ""
# config.wechat.secret = ""
# config.wechat.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=wechat"
#
# For Auth0, site is required and should match the domain provided by Auth0.
#
# config.auth0.key = ""
# config.auth0.secret = ""
# config.auth0.callback_url = "https://0.0.0.0:3000/oauth/callback?provider=auth0"
# config.auth0.site = "https://example.auth0.com"
#
config.google.key = ENV['GOOGLE_CLIENT_ID']
config.google.secret = ENV['GOOGLE_CLIENT_SECRET']
config.google.callback_url = "http://localhost:3000/oauth/callback?provider=google"
config.google.user_info_mapping = {:email => "email", :name => "name", :google_id => "id"}
config.google.scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/spreadsheets"
config.google.auth_url = '/o/oauth2/auth?access_type=offline&include_granted_scopes=true'
#
# For Microsoft Graph, the key will be your App ID, and the secret will be your app password/public key.
# The callback URL "can't contain a query string or invalid special characters"
# See: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-v2-limitations#restrictions-on-redirect-uris
# More information at https://graph.microsoft.io/en-us/docs
#
# config.microsoft.key = ""
# config.microsoft.secret = ""
# config.microsoft.callback_url = "http://0.0.0.0:3000/oauth/callback/microsoft"
# config.microsoft.user_info_mapping = {:email => "userPrincipalName", :username => "displayName"}
# config.microsoft.scope = "openid email https://graph.microsoft.com/User.Read"
#
# config.vk.key = ""
# config.vk.secret = ""
# config.vk.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=vk"
# config.vk.user_info_mapping = {:login => "domain", :name => "full_name"}
# config.vk.api_version = "5.71"
#
# config.slack.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=slack"
# config.slack.key = ''
# config.slack.secret = ''
# config.slack.user_info_mapping = {email: 'email'}
#
# To use liveid in development mode you have to replace mydomain.com with
# a valid domain even in development. To use a valid domain in development
# simply add your domain in your /etc/hosts file in front of 127.0.0.1
#
# config.liveid.key = ""
# config.liveid.secret = ""
# config.liveid.callback_url = "http://mydomain.com:3000/oauth/callback?provider=liveid"
# config.liveid.user_info_mapping = {:username => "name"}
# For information about JIRA API:
# https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+OAuth+authentication
# To obtain the consumer key and the public key you can use the jira-ruby gem https://github.com/sumoheavy/jira-ruby
# or run openssl req -x509 -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem to obtain the public key
# Make sure you have configured the application link properly
# config.jira.key = "1234567"
# config.jira.secret = "jiraTest"
# config.jira.site = "http://localhost:2990/jira/plugins/servlet/oauth"
# config.jira.signature_method = "RSA-SHA1"
# config.jira.private_key_file = "rsakey.pem"
# For information about Salesforce API:
# https://developer.salesforce.com/signup &
# https://www.salesforce.com/us/developer/docs/api_rest/
# Salesforce callback_url must be https. You can run the following to generate self-signed ssl cert:
# openssl req -new -newkey rsa:2048 -sha1 -days 365 -nodes -x509 -keyout server.key -out server.crt
# Make sure you have configured the application link properly
# config.salesforce.key = '123123'
# config.salesforce.secret = 'acb123'
# config.salesforce.callback_url = "https://127.0.0.1:9292/oauth/callback?provider=salesforce"
# config.salesforce.scope = "full"
# config.salesforce.user_info_mapping = {:email => "email"}
# config.line.key = ""
# config.line.secret = ""
# config.line.callback_url = "http://mydomain.com:3000/oauth/callback?provider=line"
# --- user config ---
config.user_config do |user|
# -- core --
# Specify username attributes, for example: [:username, :email].
# Default: `[:email]`
#
# user.username_attribute_names =
# Change *virtual* password attribute, the one which is used until an encrypted one is generated.
# Default: `:password`
#
# user.password_attribute_name =
# Downcase the username before trying to authenticate, default is false
# Default: `false`
#
# user.downcase_username_before_authenticating =
# Change default email attribute.
# Default: `:email`
#
# user.email_attribute_name =
# Change default crypted_password attribute.
# Default: `:crypted_password`
#
# user.crypted_password_attribute_name =
# What pattern to use to join the password with the salt
# Default: `""`
#
# user.salt_join_token =
# Change default salt attribute.
# Default: `:salt`
#
# user.salt_attribute_name =
# How many times to apply encryption to the password.
# Default: 1 in test env, `nil` otherwise
#
user.stretches = 1 if Rails.env.test?
# Encryption key used to encrypt reversible encryptions such as AES256.
# WARNING: If used for users' passwords, changing this key will leave passwords undecryptable!
# Default: `nil`
#
# user.encryption_key =
# Use an external encryption class.
# Default: `nil`
#
# user.custom_encryption_provider =
# Encryption algorithm name. See 'encryption_algorithm=' for available options.
# Default: `:bcrypt`
#
# user.encryption_algorithm =
# Make this configuration inheritable for subclasses. Useful for ActiveRecord's STI.
# Default: `false`
#
# user.subclasses_inherit_config =
# -- remember_me --
# How long in seconds the session length will be
# Default: `60 * 60 * 24 * 7`
#
# user.remember_me_for =
# When true, sorcery will persist a single remember me token for all
# logins/logouts (to support remembering on multiple browsers simultaneously).
# Default: false
#
# user.remember_me_token_persist_globally =
# -- user_activation --
# The attribute name to hold activation state (active/pending).
# Default: `:activation_state`
#
# user.activation_state_attribute_name =
# The attribute name to hold activation code (sent by email).
# Default: `:activation_token`
#
# user.activation_token_attribute_name =
# The attribute name to hold activation code expiration date.
# Default: `:activation_token_expires_at`
#
# user.activation_token_expires_at_attribute_name =
# How many seconds before the activation code expires. nil for never expires.
# Default: `nil`
#
# user.activation_token_expiration_period =
# REQUIRED:
# User activation mailer class.
# Default: `nil`
#
# user.user_activation_mailer =
# When true, sorcery will not automatically
# send the activation details email, and allow you to
# manually handle how and when the email is sent.
# Default: `false`
#
# user.activation_mailer_disabled =
# Method to send email related
# options: `:deliver_later`, `:deliver_now`, `:deliver`
# Default: :deliver (Rails version < 4.2) or :deliver_now (Rails version 4.2+)
#
# user.email_delivery_method =
# Activation needed email method on your mailer class.
# Default: `:activation_needed_email`
#
# user.activation_needed_email_method_name =
# Activation success email method on your mailer class.
# Default: `:activation_success_email`
#
# user.activation_success_email_method_name =
# Do you want to prevent users who did not activate by email from logging in?
# Default: `true`
#
# user.prevent_non_active_users_to_login =
# -- reset_password --
# Password reset token attribute name.
# Default: `:reset_password_token`
#
# user.reset_password_token_attribute_name =
# Password token expiry attribute name.
# Default: `:reset_password_token_expires_at`
#
# user.reset_password_token_expires_at_attribute_name =
# When was password reset email sent. Used for hammering protection.
# Default: `:reset_password_email_sent_at`
#
# user.reset_password_email_sent_at_attribute_name =
# REQUIRED:
# Password reset mailer class.
# Default: `nil`
#
# user.reset_password_mailer =
# Reset password email method on your mailer class.
# Default: `:reset_password_email`
#
# user.reset_password_email_method_name =
# When true, sorcery will not automatically
# send the password reset details email, and allow you to
# manually handle how and when the email is sent
# Default: `false`
#
# user.reset_password_mailer_disabled =
# How many seconds before the reset request expires. nil for never expires.
# Default: `nil`
#
# user.reset_password_expiration_period =
# Hammering protection: how long in seconds to wait before allowing another email to be sent.
# Default: `5 * 60`
#
# user.reset_password_time_between_emails =
# Access counter to a reset password page attribute name
# Default: `:access_count_to_reset_password_page`
#
# user.reset_password_page_access_count_attribute_name =
# -- magic_login --
# Magic login code attribute name.
# Default: `:magic_login_token`
#
# user.magic_login_token_attribute_name =
# Magic login expiry attribute name.
# Default: `:magic_login_token_expires_at`
#
# user.magic_login_token_expires_at_attribute_name =
# When was magic login email sent — used for hammering protection.
# Default: `:magic_login_email_sent_at`
#
# user.magic_login_email_sent_at_attribute_name =
# REQUIRED:
# Magic login mailer class.
# Default: `nil`
#
# user.magic_login_mailer_class =
# Magic login email method on your mailer class.
# Default: `:magic_login_email`
#
# user.magic_login_email_method_name =
# When true, sorcery will not automatically
# send magic login details email, and allow you to
# manually handle how and when the email is sent
# Default: `true`
#
# user.magic_login_mailer_disabled =
# How many seconds before the request expires. nil for never expires.
# Default: `nil`
#
# user.magic_login_expiration_period =
# Hammering protection: how long in seconds to wait before allowing another email to be sent.
# Default: `5 * 60`
#
# user.magic_login_time_between_emails =
# -- brute_force_protection --
# Failed logins attribute name.
# Default: `:failed_logins_count`
#
# user.failed_logins_count_attribute_name =
# This field indicates whether user is banned and when it will be active again.
# Default: `:lock_expires_at`
#
# user.lock_expires_at_attribute_name =
# How many failed logins are allowed.
# Default: `50`
#
# user.consecutive_login_retries_amount_limit =
# How long the user should be banned, in seconds. 0 for permanent.
# Default: `60 * 60`
#
# user.login_lock_time_period =
# Unlock token attribute name
# Default: `:unlock_token`
#
# user.unlock_token_attribute_name =
# Unlock token mailer method
# Default: `:send_unlock_token_email`
#
# user.unlock_token_email_method_name =
# When true, sorcery will not automatically
# send email with the unlock token
# Default: `false`
#
# user.unlock_token_mailer_disabled = true
# REQUIRED:
# Unlock token mailer class.
# Default: `nil`
#
# user.unlock_token_mailer =
# -- activity logging --
# Last login attribute name.
# Default: `:last_login_at`
#
# user.last_login_at_attribute_name =
# Last logout attribute name.
# Default: `:last_logout_at`
#
# user.last_logout_at_attribute_name =
# Last activity attribute name.
# Default: `:last_activity_at`
#
# user.last_activity_at_attribute_name =
# How long since user's last activity will they be considered logged out?
# Default: `10 * 60`
#
# user.activity_timeout =
# -- external --
# Class which holds the various external provider data for this user.
# Default: `nil`
#
user.authentications_class = Authentication
# User's identifier in the `authentications` class.
# Default: `:user_id`
#
# user.authentications_user_id_attribute_name =
# Provider's identifier in the `authentications` class.
# Default: `:provider`
#
# user.provider_attribute_name =
# User's external unique identifier in the `authentications` class.
# Default: `:uid`
#
# user.provider_uid_attribute_name =
end
# This line must come after the 'user config' block.
# Define which model authenticates with sorcery.
config.user_class = "User"
end

View File

@ -13,6 +13,9 @@ Rails.application.routes.draw do
# short URL for form page # short URL for form page
get '/s/:id/form' => 'forms#form', as: :form_submitter get '/s/:id/form' => 'forms#form', as: :form_submitter
get 'oauth/callback', to: 'oauths#callback'
get 'oauth/:provider', to: 'oauths#oauth', as: :auth_at_provider
get '/signup' => 'sessions#new', as: :signup # TODO: add proper signup page get '/signup' => 'sessions#new', as: :signup # TODO: add proper signup page
get '/login' => 'sessions#new', as: :login get '/login' => 'sessions#new', as: :login
get '/logout' => 'sessions#destroy', as: :logout get '/logout' => 'sessions#destroy', as: :logout

View File

@ -0,0 +1,15 @@
class SorceryCore < ActiveRecord::Migration[6.0]
def change
add_column :users, :crypted_password, :string
add_column :users, :salt, :string
add_column :users, :magic_login_token, :string
add_column :users, :magic_login_token_expires_at, :datetime
add_column :users, :magic_login_email_sent_at, :datetime
add_column :authentications, :provider, :string, null: false
add_column :authentications, :uid, :string, null: false
add_index :users, :magic_login_token
add_index :users, :email, unique: true
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_04_12_214304) do ActiveRecord::Schema.define(version: 2020_04_13_101920) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -45,6 +45,8 @@ ActiveRecord::Schema.define(version: 2020_04_12_214304) do
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false
t.text "access_token_ciphertext" t.text "access_token_ciphertext"
t.text "refresh_token_ciphertext" t.text "refresh_token_ciphertext"
t.string "provider"
t.string "uid"
end end
create_table "forms", force: :cascade do |t| create_table "forms", force: :cascade do |t|
@ -74,6 +76,13 @@ ActiveRecord::Schema.define(version: 2020_04_12_214304) do
t.string "google_id" t.string "google_id"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false
t.string "crypted_password"
t.string "salt"
t.string "magic_login_token"
t.datetime "magic_login_token_expires_at"
t.datetime "magic_login_email_sent_at"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["magic_login_token"], name: "index_users_on_magic_login_token"
end end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"