Skip to content

Commit 89896f5

Browse files
committed
working login
1 parent e2b4868 commit 89896f5

File tree

11 files changed

+1028
-53
lines changed

11 files changed

+1028
-53
lines changed

app/controllers/auth_controller.rb

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# frozen_string_literal: true
22

33
class AuthController < ApplicationController
4-
skip_before_action :verify_authenticity_token, only: [:get_token, :validate_token]
5-
skip_before_action :require_login, only: [:get_token, :validate_token]
4+
skip_before_action :verify_authenticity_token, only: %i[get_token validate_token callback]
5+
skip_before_action :require_login, only: %i[get_token validate_token callback]
66

77
# GET /auth/get_token
88
# Return user email for authenticated SSO user (for Chrome extensions)
@@ -11,13 +11,31 @@ def get_token
1111
Rails.logger.info "Session user_id: #{session[:user_id]}"
1212
Rails.logger.info "Current user: #{current_user&.email || 'NOT AUTHENTICATED'}"
1313
Rails.logger.info "Request path: #{request.fullpath}"
14+
Rails.logger.info "Redirect URI param: #{params[:redirect_uri]}"
1415

15-
# User is authenticated via SSO session, return email
16+
# User is authenticated via SSO session
1617
if current_user
1718
Rails.logger.info "✅ User authenticated successfully: #{current_user.email}"
18-
# Render the page with current_user available
19+
20+
# Check if redirect_uri is provided (Chrome extension flow)
21+
if params[:redirect_uri].present?
22+
# Chrome extension flow - redirect back with token
23+
redirect_uri = params[:redirect_uri]
24+
token = current_user.email # Using email as the token
25+
26+
# Construct redirect URL with token parameter
27+
separator = redirect_uri.include?('?') ? '&' : '?'
28+
redirect_url = "#{redirect_uri}#{separator}token=#{CGI.escape(token)}"
29+
30+
Rails.logger.info "Redirecting to Chrome extension: #{redirect_url}"
31+
redirect_to redirect_url, allow_other_host: true
32+
else
33+
# Legacy flow - render page with token (for old content script approach)
34+
Rails.logger.info 'No redirect_uri provided, rendering token page'
35+
# Render the page with current_user available
36+
end
1937
else
20-
Rails.logger.info "❌ User not authenticated, redirecting to login"
38+
Rails.logger.info '❌ User not authenticated, redirecting to login'
2139
# User not authenticated, redirect to login with return URL
2240
redirect_to new_session_path(redirect_to: request.fullpath)
2341
end
@@ -27,6 +45,27 @@ def get_token
2745
redirect_to root_path, alert: 'Authentication failed. Please try again.'
2846
end
2947

48+
# GET /auth/callback
49+
# OAuth callback for Chrome extension (development mode)
50+
# This page is used with chrome.identity.launchWebAuthFlow for localhost development
51+
def callback
52+
Rails.logger.info '=== AUTH CALLBACK CALLED ==='
53+
Rails.logger.info "Current user: #{current_user&.email || 'NOT AUTHENTICATED'}"
54+
55+
if current_user
56+
@token = current_user.email
57+
Rails.logger.info "✅ Callback with token: #{@token}"
58+
# Render the callback view
59+
else
60+
Rails.logger.info '❌ User not authenticated in callback'
61+
redirect_to new_session_path(redirect_to: request.fullpath)
62+
end
63+
rescue StandardError => e
64+
Rails.logger.error "Callback error: #{e.message}"
65+
Rails.logger.error e.backtrace.join("\n")
66+
redirect_to root_path, alert: 'Authentication callback failed. Please try again.'
67+
end
68+
3069
# GET /auth/validate
3170
# Validate current token and return user info
3271
def validate_token

app/views/auth/callback.html.erb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<% content_for :title, "Authentication Callback" %>
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+
<div class="text-5xl text-blue-500 mb-5 animate-pulse"></div>
6+
<h1 class="text-gray-800 mb-5 text-2xl font-semibold">Authentication Successful</h1>
7+
<p class="text-gray-600">
8+
Completing authentication...<br>
9+
<span class="text-sm mt-2 inline-block">This window will close automatically.</span>
10+
</p>
11+
12+
<!-- Debug info (remove in production) -->
13+
<div class="bg-gray-100 border border-gray-300 rounded p-3 mt-5 text-xs text-gray-600">
14+
<strong>Debug:</strong> Token ready for Chrome extension capture
15+
</div>
16+
</div>
17+
</div>
18+
19+
<script>
20+
// For chrome.identity.launchWebAuthFlow to work, we need to redirect to
21+
// the same URL with the token as a query parameter
22+
(function() {
23+
const currentUrl = new URL(window.location.href);
24+
25+
// Check if token is already in URL (to prevent redirect loop)
26+
if (currentUrl.searchParams.has('token')) {
27+
console.log('Token already in URL, Chrome extension should capture this');
28+
// chrome.identity.launchWebAuthFlow will intercept and close the window
29+
// If window doesn't close after 2 seconds, show a message
30+
setTimeout(() => {
31+
document.body.innerHTML = '<div class="flex items-center justify-center py-12"><div class="text-center"><p class="text-gray-600">You can close this window now.</p></div></div>';
32+
}, 2000);
33+
return;
34+
}
35+
36+
// Token not in URL yet, add it and redirect once
37+
const token = '<%= j @token %>';
38+
if (token) {
39+
console.log('Adding token to URL for Chrome extension capture');
40+
currentUrl.searchParams.set('token', token);
41+
window.location.href = currentUrl.toString();
42+
} else {
43+
console.error('No token available');
44+
}
45+
})();
46+
</script>
47+

app/views/auth/get_token.html.erb

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,52 @@
22

33
<div class="flex items-center justify-center py-12 px-4">
44
<div class="bg-white p-10 rounded-xl shadow-lg text-center max-w-lg w-full">
5-
<div class="text-5xl text-green-500 mb-5"></div>
6-
<h1 class="text-gray-800 mb-5 text-2xl font-semibold">Authentication Successful</h1>
7-
8-
<p class="mb-5">Your user email for the Chrome extension:</p>
9-
10-
<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">
11-
<div id="token-display" data-token="<%= current_user&.email %>" class="text-sky-600 font-semibold">
12-
<%= current_user&.email || 'No email available. Please try again.' %>
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>
1324
</div>
14-
</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 %>
1532

16-
<div class="text-gray-600 text-sm leading-relaxed mt-5">
17-
<strong>Instructions:</strong><br>
18-
Your email above will be automatically captured by your Chrome extension.<br>
19-
You can safely close this window after the email is captured.
20-
</div>
2133
<!-- Debug info (remove in production) -->
2234
<div class="bg-gray-100 border border-gray-300 rounded p-3 mt-3 text-xs text-gray-600">
2335
<strong>Debug:</strong> User: <%= current_user&.email || 'NOT AUTHENTICATED' %> |
24-
Session: <%= session[:user_id] || 'NO SESSION' %>
36+
Session: <%= session[:user_id] || 'NO SESSION' %> |
37+
Redirect URI: <%= params[:redirect_uri].present? ? 'Yes' : 'No' %>
2538
</div>
2639
</div>
2740
</div>
2841

42+
<% unless params[:redirect_uri].present? %>
2943
<script>
3044
console.log('Auth page loaded - email should be captured by Chrome extension content script');
3145
console.log('Email in data-token:', document.getElementById('token-display')?.getAttribute('data-token'));
3246

33-
// Auto-close window after extension captures token
47+
// Redirect to root after extension captures token (5 seconds)
3448
setTimeout(() => {
35-
console.log('Auto-closing window...');
36-
if (window.opener || window.parent !== window) {
37-
try {
38-
window.close();
39-
} catch (e) {
40-
console.log('Could not close window automatically');
41-
}
42-
}
43-
}, 5000); // Reduced to 5 seconds
49+
console.log('Redirecting to root...');
50+
window.location.href = '/';
51+
}, 5000);
4452
</script>
53+
<% end %>

chrome-extension/README.md

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,21 @@ A Chrome extension that provides a persistent AI chat interface through a side p
4040

4141
### 2. Authentication
4242

43+
The extension uses Chrome's secure `chrome.identity.launchWebAuthFlow()` API for authentication:
44+
4345
1. Configure API URLs if needed (defaults to localhost:3000)
4446
2. Click "🔐 Authenticate with SSO"
45-
3. Complete SSO login in the opened tab
46-
4. Extension automatically captures and stores your API token
47-
5. Authentication section disappears, showing clean chat interface
47+
3. Complete SSO login in a secure Chrome authentication window
48+
4. Extension automatically captures and stores your API token via redirect
49+
5. Authentication window closes automatically
50+
6. Authentication section disappears, showing clean chat interface
51+
52+
**Authentication Flow:**
53+
- Extension generates a secure Chrome extension redirect URI
54+
- Opens your Rails SSO login page with the redirect URI
55+
- After successful authentication, Rails redirects to the extension URI with the token
56+
- Chrome automatically captures the token and closes the auth window
57+
- More secure than tab-based authentication (no content script required)
4858
4959
### 3. Chat with AI
5060
@@ -68,10 +78,13 @@ The extension makes calls to these endpoints:
6878
6979
## Security Features
7080
81+
- **Chrome Identity API**: Uses secure `chrome.identity.launchWebAuthFlow()` for authentication
7182
- **Secure Token Storage**: Uses Chrome's encrypted local storage
72-
- **Origin Verification**: Validates message origins during authentication
83+
- **No Content Script Injection**: Token capture happens via secure redirect (no DOM access needed)
84+
- **Automatic Window Closure**: Auth window closes automatically after token capture
7385
- **Automatic Token Cleanup**: Clears invalid/expired tokens
7486
- **HTTPS Support**: Ready for production HTTPS deployment
87+
- **Legacy Fallback**: Maintains backward compatibility with content script flow
7588

7689
## Development
7790

@@ -128,17 +141,14 @@ The AI chat interface provides:
128141
129142
## Troubleshooting
130143
131-
**"window is not defined" Error:**
132-
- This was fixed by replacing `window.open()` with `chrome.tabs.create()` in the service worker
133-
- The extension now opens a new tab instead of a popup window for authentication
134-
- Added dedicated auth content script to capture tokens from the auth page
135-
136144
**Authentication Issues:**
137-
- Check browser console for messages from the auth content script
138-
- Ensure CORS is configured on your Rails server
145+
- Check browser console for Chrome Identity API errors
146+
- Ensure `redirect_uri` is properly handled by Rails controller
139147
- Check that `/auth/get_token` endpoint is accessible
140148
- Verify SSO/SAML configuration is working
141-
- Look for "Auth content script loaded" message in the auth tab's console
149+
- Check for "Chrome Identity Redirect URL" in background service worker console
150+
- Ensure CORS is configured if using cross-origin requests
151+
- If Chrome Identity API fails, extension falls back to legacy tab-based flow
142152
143153
**API Call Failures:**
144154
- Check browser console for network errors

chrome-extension/auth-content.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,68 @@
11
// Content script specifically for the auth/token pages
2-
// This script captures the token from the auth page and forwards it to the service worker
2+
//
3+
// NOTE: This is a LEGACY FALLBACK flow. The primary authentication method now uses
4+
// chrome.identity.launchWebAuthFlow() which is more secure and doesn't require this content script.
5+
//
6+
// This script is kept for backward compatibility and will only run when the auth page
7+
// is accessed WITHOUT the redirect_uri parameter (legacy flow).
8+
//
9+
// New flow (chrome.identity.launchWebAuthFlow):
10+
// 1. Extension calls chrome.identity.getRedirectURL() to get redirect URI
11+
// 2. Opens auth page with redirect_uri parameter
12+
// 3. After auth, Rails redirects to chrome-extension:// URI with token
13+
// 4. launchWebAuthFlow intercepts and extracts token
14+
//
15+
// Legacy flow (this script):
16+
// 1. Opens auth page in a tab without redirect_uri
17+
// 2. This content script captures token from page
18+
// 3. Sends token to service worker via chrome.runtime.sendMessage
319

4-
console.log('Auth content script loaded for:', window.location.href);
20+
console.log('🔵 Auth content script loaded for:', window.location.href);
21+
console.log('🔵 Document ready state:', document.readyState);
522

623
let tokenSent = false;
724

825
// Function to extract token from the page
926
function extractTokenFromPage() {
10-
if (tokenSent) return null;
27+
if (tokenSent) {
28+
console.log('🔵 Token already sent, skipping');
29+
return null;
30+
}
1131

1232
// Check for token in data attribute (this contains the user email)
1333
const tokenDisplay = document.getElementById('token-display');
34+
console.log('🔵 Looking for token-display element:', tokenDisplay);
35+
1436
if (tokenDisplay) {
1537
const token = tokenDisplay.getAttribute('data-token');
38+
console.log('🔵 Token from data-token attribute:', token);
39+
1640
if (token && token.length > 0) {
17-
console.log('Found token in data attribute');
41+
console.log('Found valid token:', token);
1842
return token;
1943
}
2044
}
2145

46+
console.log('❌ No token found on page');
2247
return null;
2348
}
2449

2550
// Function to send token to service worker
2651
function sendTokenToServiceWorker(token) {
27-
if (tokenSent) return;
52+
if (tokenSent) {
53+
console.log('🔵 Token already sent, not sending again');
54+
return;
55+
}
2856

29-
console.log('Sending token to service worker');
57+
console.log('🚀 Sending token to service worker:', token);
3058
tokenSent = true;
3159

3260
chrome.runtime.sendMessage({
3361
type: 'FACK_AUTH_TOKEN',
3462
success: true,
3563
token: token
64+
}, (response) => {
65+
console.log('✅ Message sent to background, response:', response);
3666
});
3767
}
3868

0 commit comments

Comments
 (0)