Skip to content

Commit 028e671

Browse files
Support chrome plugin (#235)
* add basic token creation * basic extension * basic manifest * unused * name * copy * copy paste * formatting * small tweaks * css * improve polling * x * css * increase time * simplify token * css * remove api url * css * remove logout * remove * fix routes * remove unused * fix login redirects * fixing auth redirect * working login * skip * simplify * adjust manifest * fix doc * d --------- Co-authored-by: Vijay Swamidass <[email protected]>
1 parent fcd91f5 commit 028e671

20 files changed

+3253
-6
lines changed

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ gem 'jbuilder'
3232
# Use Redis adapter to run Action Cable in production
3333
gem 'redis', '~> 4.0'
3434

35+
# Enable CORS for Chrome extension support
36+
gem 'rack-cors'
37+
3538
# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]
3639
# gem "kredis"
3740

Gemfile.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,9 @@ GEM
323323
activesupport (>= 3.0.0)
324324
racc (1.8.1)
325325
rack (3.1.18)
326+
rack-cors (3.0.0)
327+
logger
328+
rack (>= 3.0.14)
326329
rack-protection (4.1.1)
327330
base64 (>= 0.1.0)
328331
logger (>= 1.6.0)
@@ -562,6 +565,7 @@ DEPENDENCIES
562565
pgvector
563566
puma (~> 6.4)
564567
pundit (~> 2.3)
568+
rack-cors
565569
rails (~> 7.2.0)
566570
rails-controller-testing
567571
redcarpet

app/controllers/auth_controller.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# frozen_string_literal: true
2+
3+
class AuthController < ApplicationController
4+
skip_before_action :verify_authenticity_token, only: %i[get_token validate_token]
5+
skip_before_action :require_login, only: %i[get_token validate_token]
6+
7+
# GET /auth/get_token
8+
# Return user email for authenticated SSO user (for Chrome extensions)
9+
# The extension's content script reads the token from the page DOM
10+
def get_token
11+
Rails.logger.info '=== AUTH GET_TOKEN CALLED ==='
12+
Rails.logger.info "Session user_id: #{session[:user_id]}"
13+
Rails.logger.info "Current user: #{current_user&.email || 'NOT AUTHENTICATED'}"
14+
Rails.logger.info "Request path: #{request.fullpath}"
15+
16+
# User is authenticated via SSO session, render page with token
17+
if current_user
18+
Rails.logger.info "✅ User authenticated successfully: #{current_user.email}"
19+
# Render the page with current_user available (extension content script will capture it)
20+
else
21+
Rails.logger.info '❌ User not authenticated, redirecting to login'
22+
# User not authenticated, redirect to login with return URL
23+
redirect_to new_session_path(redirect_to: request.fullpath)
24+
end
25+
rescue StandardError => e
26+
Rails.logger.error "Auth error: #{e.message}"
27+
Rails.logger.error e.backtrace.join("\n")
28+
redirect_to root_path, alert: 'Authentication failed. Please try again.'
29+
end
30+
31+
# GET /auth/validate
32+
# Validate current token and return user info
33+
def validate_token
34+
if current_user
35+
render json: {
36+
valid: true,
37+
user: {
38+
id: current_user.id,
39+
email: current_user.email
40+
}
41+
}
42+
else
43+
render json: {
44+
valid: false,
45+
error: 'Invalid or expired token'
46+
}, status: :unauthorized
47+
end
48+
end
49+
end

app/controllers/saml_controller.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
class SamlController < ApplicationController
44
skip_before_action :verify_authenticity_token
55
def init
6+
# Store redirect_to parameter in session since SAML redirects lose URL parameters
7+
session[:saml_redirect_to] = params[:redirect_to] if params[:redirect_to].present?
8+
69
request = OneLogin::RubySaml::Authrequest.new
710
redirect_to(request.create(saml_settings), allow_other_host: true)
811
end
@@ -39,11 +42,16 @@ def consume
3942
# Setting the session logs the user in. Need to make some methods for this.
4043
if user
4144
login_user(user)
45+
46+
# Use stored redirect_to parameter if available, otherwise fallback to root
47+
redirect_url = session[:saml_redirect_to].presence || root_path
48+
session.delete(:saml_redirect_to) # Clean up the session
49+
50+
redirect_to redirect_url, notice:
4251
else
4352
notice = 'Login failed. Please contact an admin for help.'
53+
redirect_to root_path, notice:
4454
end
45-
46-
redirect_to root_path, notice:
4755
else
4856
redirect_to(request.create(saml_settings))
4957
end

app/controllers/sessions_controller.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
class SessionsController < ApplicationController
22
skip_before_action :require_login
33

4-
def new; end
4+
def new
5+
@redirect_to = params[:redirect_to]
6+
end
57

68
def create
79
user = User.find_by_email(params[:session][:email])
10+
811
if user&.authenticate(params[:session][:password])
912
login_user(user)
10-
redirect_back(fallback_location: root_path)
13+
Rails.logger.info "User logged in: #{user.email}"
14+
15+
# Use redirect_to parameter if provided, otherwise fallback to previous page or root
16+
if params[:redirect_to].present?
17+
Rails.logger.info "Redirecting to: #{params[:redirect_to]}"
18+
redirect_to params[:redirect_to]
19+
else
20+
redirect_to(root_path)
21+
end
1122
else
12-
redirect_to new_session_url, notice: 'Error logging in.'
23+
Rails.logger.info "Login failed for: #{params[:session][:email]}"
24+
# Preserve redirect_to parameter on failed login
25+
redirect_params = params[:redirect_to].present? ? { redirect_to: params[:redirect_to] } : {}
26+
redirect_to new_session_url(redirect_params), notice: 'Error logging in.'
1327
end
1428
end
1529

app/views/auth/get_token.html.erb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<% content_for :title, "Chrome Extension Token" %>
2+
3+
<div class="flex items-center justify-center py-12 px-4">
4+
<div class="bg-white p-10 rounded-xl shadow-lg text-center max-w-lg w-full">
5+
<% if params[:redirect_uri].present? %>
6+
<!-- Modern flow: automatic redirect via chrome.identity.launchWebAuthFlow -->
7+
<div class="text-5xl text-blue-500 mb-5 animate-pulse"></div>
8+
<h1 class="text-gray-800 mb-5 text-2xl font-semibold">Redirecting...</h1>
9+
<p class="text-gray-600">
10+
Authentication successful! Redirecting back to your extension...<br>
11+
<span class="text-sm mt-2 inline-block">This window will close automatically.</span>
12+
</p>
13+
<% else %>
14+
<!-- Legacy flow: content script captures token -->
15+
<div class="text-5xl text-green-500 mb-5"></div>
16+
<h1 class="text-gray-800 mb-5 text-2xl font-semibold">Authentication Successful</h1>
17+
18+
<p class="mb-5">Your user email for the Chrome extension:</p>
19+
20+
<div class="bg-gray-50 border-2 border-dashed border-gray-300 rounded-lg p-5 my-5 break-all font-mono text-sm min-h-15 flex items-center justify-center">
21+
<div id="token-display" data-token="<%= current_user&.email %>" class="text-sky-600 font-semibold">
22+
<%= current_user&.email || 'No email available. Please try again.' %>
23+
</div>
24+
</div>
25+
26+
<div class="text-gray-600 text-sm leading-relaxed mt-5">
27+
<strong>Instructions:</strong><br>
28+
Your email above will be automatically captured by your Chrome extension.<br>
29+
You can safely close this window after the email is captured.
30+
</div>
31+
<% end %>
32+
33+
<!-- Debug info (remove in production) -->
34+
<div class="bg-gray-100 border border-gray-300 rounded p-3 mt-3 text-xs text-gray-600">
35+
<strong>Debug:</strong> User: <%= current_user&.email || 'NOT AUTHENTICATED' %> |
36+
Session: <%= session[:user_id] || 'NO SESSION' %> |
37+
Redirect URI: <%= params[:redirect_uri].present? ? 'Yes' : 'No' %>
38+
</div>
39+
</div>
40+
</div>
41+
42+
<% unless params[:redirect_uri].present? %>
43+
<script>
44+
console.log('Auth page loaded - email should be captured by Chrome extension content script');
45+
console.log('Email in data-token:', document.getElementById('token-display')?.getAttribute('data-token'));
46+
47+
// Redirect to root after extension captures token (5 seconds)
48+
setTimeout(() => {
49+
console.log('Redirecting to root...');
50+
window.location.href = '/';
51+
}, 5000);
52+
</script>
53+
<% end %>

app/views/sessions/new.html.erb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
<% if ENV['DISABLE_PASSWORD_LOGIN'] != "true" %>
1717
<%= form_for :session, url: sessions_path, method: :post, class: "mt-8 space-y-6" do |f| %>
1818
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
19+
<% if @redirect_to.present? %>
20+
<input type="hidden" name="redirect_to" value="<%= @redirect_to %>">
21+
<% end %>
1922
<div class="rounded-md shadow-sm -space-y-px">
2023
<div>
2124
<%= f.label :email, class: "sr-only" %>
@@ -41,7 +44,8 @@
4144
<% end %>
4245
<% end %>
4346
<div>
44-
<%= link_to "Login with SSO", "/auth/saml/init", class: "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-sky-800 hover:bg-sky-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-cyan-500" %>
47+
<% sso_url = @redirect_to.present? ? "/auth/saml/init?redirect_to=#{CGI.escape(@redirect_to)}" : "/auth/saml/init" %>
48+
<%= link_to "Login with SSO", sso_url, class: "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-sky-800 hover:bg-sky-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-cyan-500" %>
4549
</div>
4650
</div>
4751
</div>

0 commit comments

Comments
 (0)