Class: Bible270::SessionsController

Inherits:
ApplicationController show all
Defined in:
app/controllers/bible270/sessions_controller.rb

Overview

Built-in OmniAuth sign-in.

The OmniAuth middleware (registered by the host app) handles the request and callback phases; by the time #create runs, request.env is populated and we just need to turn it into a Reader and remember them.

NOTE: OmniAuth 2.0+ only permits POST to its request-phase routes, so all sign-in controls in these views are forms/buttons rather than links.

Instance Method Summary collapse

Instance Method Details

#createObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'app/controllers/bible270/sessions_controller.rb', line 20

def create
  auth = request.env['omniauth.auth']
  redirect_to(, alert: "Sign in didn't complete. Please try again.") and return unless auth

  if enrollment_closed? && !Reader.omniauth_reader_exists?(auth['provider'], auth['uid'])
    redirect_to(, alert: enrollment_closed_message) and return
  end

  reader = Reader.from_omniauth(auth)
  redirect_to(, alert: "We couldn't set up your reader profile.") and return unless reader&.persisted?

  destination = safe_origin(request.env['omniauth.origin']) || 

  # Rotate the session id to avoid session fixation, then sign in.
  reset_session
  session[:bible270_reader_id] = reader.id
  remember_reader!(reader)
  @current_reader = reader

  # first_name is filled in for every route now, but a bridged host user may
  # still arrive without one.
  redirect_to destination, notice: "Welcome #{reader.first_name.presence || reader.display_name}."
end

#destroyObject



44
45
46
47
48
49
50
51
52
# File 'app/controllers/bible270/sessions_controller.rb', line 44

def destroy
  # Clear the long-lived cookie too, or the next request would sign them
  # straight back in. The stored token is left alone so their other devices
  # stay signed in; Reader#forget! is what revokes those.
  forget_reader!
  reset_session
  @current_reader = nil
  redirect_to(after_sign_out_path, notice: 'Signed out.')
end

#email_callbackObject

GET: the reader clicked the link.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'app/controllers/bible270/sessions_controller.rb', line 100

def email_callback
  token = SignInToken.claim!(params[:token])
  if token.nil?
    redirect_to(,
                alert: 'That link has expired or was already used. Please request a new one.') and return
  end

  if enrollment_closed? && !Reader.email_reader_exists?(token.email)
    redirect_to(, alert: enrollment_closed_message) and return
  end

  reader = Reader.from_email(token.email, first_name: token.first_name,
                                          last_name: token.last_name, display_name: token.display_name)
  redirect_to(, alert: "We couldn't set up your reader profile.") and return unless reader&.persisted?

  destination = safe_origin(params[:origin]) || 

  reset_session
  session[:bible270_reader_id] = reader.id
  remember_reader!(reader)
  @current_reader = reader

  if name_needed?(reader)
    redirect_to(profile_path,
                notice: 'Welcome. Please add your name, so others know who they are reading with.')
    return
  end

  redirect_to destination, notice: "Welcome #{reader.first_name}."
end

POST: accept an address and send a one-time link.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'app/controllers/bible270/sessions_controller.rb', line 62

def email_link
  unless Bible270.config.email_sign_in?
    redirect_to(, alert: "Email sign-in isn't enabled here.") and return
  end

  origin  = safe_origin(params[:origin])
  address = EmailSignIn.normalize_email(params[:email])
  if address.nil?
    redirect_to((origin: origin),
                alert: "That doesn't look like an email address — please check and try again.") and return
  end

  # Names are optional here. Requiring them only for unknown addresses would
  # make the response differ, which tells a stranger whether an address
  # already has an account. A new reader is asked for their name after they
  # click the link instead (see email_callback).
  names = Names.normalize(params[:first_name], params[:last_name])

  _record, raw = SignInToken.issue!(address, first_name: names&.dig(:first_name),
                                             last_name: names&.dig(:last_name))

  if raw && enrollment_closed? && !Reader.email_reader_exists?(address)
    Rails.logger.info("[bible270] enrolment closed; no link sent to #{address}")
  elsif raw
    (address, (token: raw, origin: origin))
  else
    # Silent to the reader by design; say so in the log or this is undebuggable.
    Rails.logger.warn("[bible270] no sign-in link issued for #{address} " \
                      '(rate limited — see config.email_sign_in_max_per_window)')
  end

  # Deliberately identical whether or not a link was actually sent, so this
  # reveals neither who has an account nor that a limit was hit.
  redirect_to ,
              notice: "Check your inbox — if that address is valid we've sent a sign-in link to #{address}."
end

#failureObject



54
55
56
57
# File 'app/controllers/bible270/sessions_controller.rb', line 54

def failure
  message = params[:message].presence || 'unknown error'
  redirect_to , alert: "Sign in failed (#{message.to_s.tr('_', ' ')})."
end

#newObject



13
14
15
16
17
18
# File 'app/controllers/bible270/sessions_controller.rb', line 13

def new
  redirect_to(, notice: "You're already signed in.") and return if signed_in?

  @providers = Bible270.config.omniauth_providers
  @origin = safe_origin(params[:origin])
end