Skip to content

Commit bdf3b77

Browse files
committed
fix doc
1 parent 0955829 commit bdf3b77

File tree

5 files changed

+306
-719
lines changed

5 files changed

+306
-719
lines changed
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
# Chrome Extension Authentication
2+
3+
This document describes how the Chrome extension authenticates with the Rails API.
4+
5+
## Overview
6+
7+
The extension uses a **tab-based authentication flow** with SSO (SAML). This approach:
8+
- ✅ Works in all environments (development, staging, production)
9+
- ✅ Simple and reliable
10+
- ✅ Uses standard Rails SSO authentication
11+
- ✅ No complex OAuth configuration needed
12+
13+
## Authentication Flow
14+
15+
```
16+
1. User clicks "Authenticate with SSO" in extension
17+
18+
2. Extension opens /auth/get_token in new tab (background.js)
19+
20+
3. If not authenticated → Rails redirects to SSO login
21+
22+
4. User completes SSO authentication
23+
24+
5. Rails renders auth page with token in DOM (#token-display element)
25+
26+
6. Extension's content script (auth-content.js) reads token from page
27+
28+
7. Content script sends token to background via chrome.runtime.sendMessage()
29+
30+
8. Background script stores token and closes auth tab
31+
32+
9. Extension uses token for all API calls
33+
```
34+
35+
## Implementation
36+
37+
### 1. Manifest (manifest.json)
38+
39+
```json
40+
{
41+
"permissions": [
42+
"storage", // Store API token
43+
"tabs", // Create/close auth tabs
44+
"scripting" // Inject content script
45+
],
46+
"host_permissions": [
47+
"http://localhost:3000/*"
48+
],
49+
"optional_host_permissions": [
50+
"http://*/*", // User can grant access to any HTTP domain
51+
"https://*/*" // User can grant access to any HTTPS domain
52+
]
53+
}
54+
```
55+
56+
**Note:** Optional permissions are requested dynamically when user configures their server URL.
57+
58+
### 2. Background Script (background.js)
59+
60+
Opens auth tab and listens for token:
61+
62+
```javascript
63+
async authenticateWithTab() {
64+
return new Promise((resolve) => {
65+
const authUrl = `${this.baseUrl}/auth/get_token`;
66+
67+
chrome.tabs.create({ url: authUrl, active: true }, (tab) => {
68+
const authTabId = tab.id;
69+
70+
// Inject content script when page loads
71+
chrome.tabs.onUpdated.addListener(function listener(tabId, info) {
72+
if (tabId === authTabId && info.status === 'complete') {
73+
chrome.scripting.executeScript({
74+
target: { tabId: authTabId },
75+
files: ['auth-content.js']
76+
});
77+
}
78+
});
79+
80+
// Listen for token message from content script
81+
chrome.runtime.onMessage.addListener((message, sender) => {
82+
if (sender.tab?.id === authTabId &&
83+
message.type === 'FACK_AUTH_TOKEN') {
84+
85+
// Store token
86+
this.token = message.token;
87+
chrome.storage.local.set({ apiToken: this.token });
88+
89+
// Close auth tab
90+
chrome.tabs.remove(authTabId);
91+
resolve({ success: true, token: this.token });
92+
}
93+
});
94+
});
95+
});
96+
}
97+
```
98+
99+
### 3. Content Script (auth-content.js)
100+
101+
Extracts token from page and sends to background:
102+
103+
```javascript
104+
// Find token in DOM
105+
const tokenDisplay = document.getElementById('token-display');
106+
const token = tokenDisplay.getAttribute('data-token');
107+
108+
// Send to background script
109+
chrome.runtime.sendMessage({
110+
type: 'FACK_AUTH_TOKEN',
111+
success: true,
112+
token: token
113+
});
114+
```
115+
116+
### 4. Rails Controller (auth_controller.rb)
117+
118+
```ruby
119+
class AuthController < ApplicationController
120+
skip_before_action :verify_authenticity_token, only: [:get_token]
121+
skip_before_action :require_login, only: [:get_token]
122+
123+
def get_token
124+
if current_user
125+
# Render page with token (extension reads it from DOM)
126+
else
127+
# Redirect to SSO login
128+
redirect_to new_session_path(redirect_to: request.fullpath)
129+
end
130+
end
131+
end
132+
```
133+
134+
### 5. Auth View (get_token.html.erb)
135+
136+
```erb
137+
<div id="token-display" data-token="<%= current_user&.email %>">
138+
<%= current_user&.email %>
139+
</div>
140+
141+
<script>
142+
// Broadcast token via custom event for content script
143+
window.dispatchEvent(new CustomEvent('fackAuthToken', {
144+
detail: { token: '<%= j current_user&.email %>' }
145+
}));
146+
</script>
147+
```
148+
149+
## Configuration
150+
151+
### User Setup
152+
153+
1. Install extension
154+
2. Open sidepanel → Configuration
155+
3. Enter server URL (e.g., `https://fack.internal.salesforce.com`)
156+
4. Click "Save Settings" → Chrome asks for permission
157+
5. Click "Allow" to grant access to that domain
158+
6. Click "Authenticate with SSO"
159+
7. Complete SSO login
160+
8. Extension captures token automatically
161+
162+
### Dynamic Permissions
163+
164+
The extension requests permissions **dynamically** when users configure their server URL:
165+
166+
```javascript
167+
// When user saves base URL in settings
168+
async updateConfiguration(baseUrl) {
169+
const urlPattern = `${new URL(baseUrl).origin}/*`;
170+
171+
// Request permission for this URL
172+
const granted = await chrome.permissions.request({
173+
origins: [urlPattern]
174+
});
175+
176+
if (granted) {
177+
this.baseUrl = baseUrl;
178+
await chrome.storage.local.set({ baseUrl });
179+
} else {
180+
throw new Error('Permission denied');
181+
}
182+
}
183+
```
184+
185+
This allows the extension to work with **any deployment** without hardcoding domains in the manifest.
186+
187+
## Security Features
188+
189+
1. **SSO Authentication**: Uses existing enterprise SSO
190+
2. **Dynamic Permissions**: Users explicitly grant access to each domain
191+
3. **Token Storage**: Tokens stored in Chrome's encrypted local storage
192+
4. **Isolated Extension**: Each extension instance has isolated storage
193+
5. **Automatic Cleanup**: Auth tabs close automatically after token capture
194+
195+
## Development
196+
197+
### Local Setup
198+
199+
```bash
200+
# Start Rails server
201+
rails server
202+
203+
# Load extension
204+
1. Go to chrome://extensions/
205+
2. Enable "Developer mode"
206+
3. Click "Load unpacked"
207+
4. Select chrome-extension folder
208+
209+
# Test authentication
210+
1. Open extension sidepanel
211+
2. Base URL should be http://localhost:3000 (default)
212+
3. Click "Authenticate with SSO"
213+
4. Complete login
214+
5. Token captured automatically
215+
```
216+
217+
### Console Logs
218+
219+
**Background console (chrome://extensions → Service Worker):**
220+
```
221+
🔐 Using tab-based authentication
222+
📑 Auth tab created with ID: 1234567890
223+
📄 Page loaded in auth tab: http://localhost:3000/auth/get_token
224+
✅ Script injected successfully
225+
📨 Background received message: FACK_AUTH_TOKEN from tab: 1234567890
226+
✅ Received auth token from correct tab: [email protected]
227+
💾 Token saved to storage, closing auth tab
228+
```
229+
230+
**Auth page console:**
231+
```
232+
🔧 Auth content script loaded for: http://localhost:3000/auth/get_token
233+
✅ This is an auth page, will look for token
234+
🔍 Looking for token in page...
235+
✅ Found valid token: [email protected]
236+
🚀 Sending token to service worker: [email protected]
237+
```
238+
239+
## Troubleshooting
240+
241+
### Token not captured
242+
243+
**Check:**
244+
1. Is `auth-content.js` injecting? Look for logs in auth page console
245+
2. Does page have `#token-display` element with `data-token` attribute?
246+
3. Is background script receiving the message? Check background console
247+
4. Are tab IDs matching? Compare in logs
248+
249+
### "Permission denied" when saving URL
250+
251+
**User needs to:**
252+
1. Click "Allow" when Chrome shows permission dialog
253+
2. If denied, try saving URL again - dialog will reappear
254+
3. Check chrome://extensions for granted permissions
255+
256+
### Auth window doesn't close
257+
258+
**Possible causes:**
259+
1. Content script not injecting - check manifest has `scripting` permission
260+
2. Message not reaching background - check tab IDs in logs
261+
3. JavaScript error - check auth page console
262+
263+
## Production Deployment
264+
265+
1. Deploy Rails app with SSO configured
266+
2. Users install extension from Chrome Web Store (or load unpacked)
267+
3. Users configure production URL in extension settings
268+
4. Grant permission when prompted
269+
5. Authenticate via SSO
270+
271+
**No code changes needed** - the extension detects the URL and works automatically!
272+
273+
## API Usage
274+
275+
After authentication, all API calls include the token:
276+
277+
```javascript
278+
async makeAPICall(endpoint, options) {
279+
const response = await fetch(`${this.baseUrl}/api/v1${endpoint}`, {
280+
headers: {
281+
'Authorization': `Bearer ${this.token}`,
282+
'Content-Type': 'application/json'
283+
},
284+
...options
285+
});
286+
return response.json();
287+
}
288+
```
289+
290+
## Why This Approach?
291+
292+
We chose tab-based authentication over `chrome.identity.launchWebAuthFlow()` because:
293+
294+
✅ **Simpler**: No OAuth configuration needed
295+
✅ **Works everywhere**: Localhost, staging, production
296+
✅ **Easier to debug**: Can inspect auth page like any web page
297+
✅ **Flexible**: Works with any SSO provider
298+
✅ **Open-source friendly**: No hardcoded domains in manifest
299+
300+
For open-source tools, this approach is ideal since users can point it at any server.
301+

docs/chrome-extension-integration.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
# Chrome Extension Integration Guide
1+
# Chrome Extension Integration Guide (DEPRECATED)
2+
3+
⚠️ **This document is outdated.** See [chrome-extension-authentication.md](./chrome-extension-authentication.md) for current implementation.
4+
5+
---
26

37
This guide explains how to integrate your Chrome extension with the Rails API authentication system using SSO.
48

0 commit comments

Comments
 (0)