Summary
Add automatic token forwarding from the OAuth broker to the PinShare frontend using the postMessage API. This will streamline the OAuth flow for both hosted (share.episkopos.community) and self-hosted instances by eliminating the manual copy/paste step, while maintaining full backward compatibility.
Current Implementation
OAuth Broker Flow (oauth-broker/main.go):
- User clicks "Get Token from OAuth Broker" in frontend
- Opens broker URL in new tab:
https://oauth.episkopos.community
- User authorizes with Google
- Broker displays JSON token in browser
- User manually copies entire JSON token
- User pastes into textarea in PinShare UI
- Frontend validates and POSTs to
/api/google-drive/set-token
Pain Points:
- Manual copy/paste is tedious and error-prone
- Not user-friendly for non-technical users
- Token briefly visible in browser (though ephemeral)
- Extra steps reduce conversion
Proposed Solution
postMessage-based Auto-Forward
Use the browser's postMessage API to securely send the token from the OAuth broker popup directly to the frontend window.
New Flow:
- Frontend opens broker URL in popup window (not new tab)
- User authorizes with Google
- Broker detects popup context via
window.opener
- Broker uses
window.opener.postMessage() to send token to frontend
- Frontend receives message, validates origin, auto-submits token to backend
- Popup closes automatically
- Frontend shows "Token received!" and proceeds
Fallback Flow (backward compatible):
- If popup blocked → show instructions + copy/paste option
- If
window.opener unavailable → show copy/paste UI
- If postMessage fails → show copy/paste UI (existing behavior)
- Old broker version → copy/paste works as today
Why postMessage?
✅ Works for both cloud-hosted and self-hosted:
- Cloud frontend → Cloud broker (cross-origin)
- Self-hosted frontend → Cloud broker (cross-origin)
- Self-hosted frontend → Self-hosted broker (same-origin)
✅ Secure:
- Origin validation on both sender and receiver
- Token never exposed in URL
- No CORS configuration changes needed
✅ Backward compatible:
- Copy/paste flow remains available
- Works with old frontend or old broker versions
- Self-hosted instances without frontend UI still work
Implementation Details
1. OAuth Broker Changes (oauth-broker/main.go)
Modify renderSuccess() function (lines ~276-380):
Add JavaScript to success page:
// Detect if opened as popup
if (window.opener && !window.opener.closed) {
// Attempt postMessage
const message = {
type: 'PINSHARE_OAUTH_TOKEN',
token: tokenData, // { access_token, refresh_token, expiry, token_type }
source: 'pinshare-oauth-broker'
};
// Send to opener (frontend)
window.opener.postMessage(message, '*'); // Will be validated by receiver
// Wait for acknowledgment (5 second timeout)
let acknowledged = false;
window.addEventListener('message', (event) => {
if (event.data.type === 'PINSHARE_TOKEN_RECEIVED') {
acknowledged = true;
// Show brief success message
document.body.innerHTML = '<h2>✓ Token sent! Closing...</h2>';
setTimeout(() => window.close(), 1000);
}
});
setTimeout(() => {
if (!acknowledged) {
// Fallback to copy/paste UI
showCopyPasteUI();
}
}, 5000);
} else {
// Not a popup - show copy/paste UI immediately
showCopyPasteUI();
}
Message format:
{
"type": "PINSHARE_OAUTH_TOKEN",
"token": {
"access_token": "ya29.a0...",
"token_type": "Bearer",
"refresh_token": "1//0g...",
"expiry": "2024-11-17T23:45:00Z"
},
"source": "pinshare-oauth-broker"
}
2. Frontend Changes (pinshare-ui/src/pages/GoogleDriveImport.jsx)
Update "Get Token from OAuth Broker" button handler (currently ~line 314):
const handleGetTokenFromBroker = () => {
const brokerUrl = `${OAUTH_BASE}/authorize`;
// Open in popup instead of new tab
const popup = window.open(
brokerUrl,
'pinshare-oauth',
'width=600,height=700,scrollbars=yes'
);
if (!popup || popup.closed) {
// Popup blocked - show instructions
setMessage('Please allow popups for automatic token flow, or use the manual copy/paste method below.');
return;
}
// Set up postMessage listener
const handleMessage = async (event) => {
// Validate origin
const allowedOrigin = new URL(OAUTH_BASE).origin;
if (event.origin !== allowedOrigin) {
console.warn('Received message from unexpected origin:', event.origin);
return;
}
// Validate message structure
if (event.data?.type !== 'PINSHARE_OAUTH_TOKEN' ||
event.data?.source !== 'pinshare-oauth-broker') {
return;
}
// Send acknowledgment
popup.postMessage({ type: 'PINSHARE_TOKEN_RECEIVED' }, allowedOrigin);
// Clean up listener
window.removeEventListener('message', handleMessage);
clearTimeout(timeoutId);
// Auto-submit token
try {
const response = await fetch('/api/google-drive/set-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event.data.token)
});
if (response.ok) {
setMessage('✓ Token received and authenticated!');
checkAuthStatus(); // Refresh auth status
popup.close();
} else {
throw new Error('Token validation failed');
}
} catch (error) {
setMessage('Error storing token: ' + error.message);
}
};
window.addEventListener('message', handleMessage);
// Timeout after 30 seconds
const timeoutId = setTimeout(() => {
window.removeEventListener('message', handleMessage);
setMessage('Token auto-forward timed out. Please use the manual copy/paste method.');
}, 30000);
};
3. Security Considerations
Origin Validation:
- Frontend validates message origin matches
VITE_OAUTH_BASE
- Broker sends to
* but frontend validates (postMessage API design pattern)
- Message structure validation (type, source fields)
Token Exposure:
- Token never in URL (unlike redirect-based flows)
- Only in memory during postMessage transfer
- Same security as existing copy/paste (clipboard also in memory)
CORS:
- No CORS changes needed (postMessage works cross-origin by design)
- Current
PS_ALLOWED_ORIGINS configuration unchanged
Popup Blockers:
- Graceful degradation if popup blocked
- User-initiated action (button click) usually allows popups
- Fallback instructions if blocked
4. UI/UX Flow Diagram
Happy Path (Automatic):
User clicks "Get Token from OAuth Broker"
↓
Popup opens → User authorizes Google
↓
Broker sends token via postMessage
↓
Frontend receives token → Auto-submits to backend
↓
Success message → Popup closes → Auth status updates
Fallback Path (Manual):
Popup blocked OR postMessage fails
↓
Show "Please allow popups or use manual flow"
↓
User copies token from broker page
↓
User pastes into textarea → Submits manually
↓
(Existing copy/paste flow)
5. Backward Compatibility Matrix
| Frontend Version |
Broker Version |
Result |
| New |
New |
✅ Automatic postMessage flow |
| New |
Old |
✅ Fallback to copy/paste (timeout) |
| Old |
New |
✅ Copy/paste (no popup listener) |
| Old |
Old |
✅ Copy/paste (existing behavior) |
Edge Cases:
- Self-hosted without frontend UI: Copy/paste works (existing behavior)
- Direct OAuth flow: Unchanged, still supported
- Multiple instances: Each validates its own
VITE_OAUTH_BASE origin
Testing Checklist
Files to Modify
-
oauth-broker/main.go (~line 276-380)
- Update
renderSuccess() function
- Add postMessage JavaScript to success page
- Keep existing copy/paste UI as fallback
-
pinshare-ui/src/pages/GoogleDriveImport.jsx (~line 314+)
- Update "Get Token" button handler
- Change
window.open() from new tab to popup
- Add
message event listener with origin validation
- Add timeout and cleanup logic
- Keep existing manual textarea flow
-
oauth-broker/README.md (optional)
- Document new automatic flow
- Document fallback behavior
Rollout Strategy
-
Phase 1: Deploy updated broker
- Backward compatible (works with old frontends)
- Test in staging with manual copy/paste
-
Phase 2: Deploy updated frontend
- Progressive enhancement (works with old brokers)
- Test automatic flow with both hosted and self-hosted
-
Phase 3: Monitor and iterate
- Track popup blocker frequency
- Collect user feedback
- Add analytics if needed
Benefits
✅ Better UX: No manual copy/paste for 95%+ of users
✅ Works everywhere: Cloud-hosted, self-hosted, cross-origin
✅ Secure: Origin validation, no URL token exposure
✅ Backward compatible: Existing flows unchanged
✅ Low risk: Graceful degradation if anything fails
Related Issues
- Original broker design: Centralized OAuth credentials for self-hosted instances
- CORS configuration: Recent commits
a94ff64 and e353a42
Priority: Medium - Improves UX but existing flow works fine
Effort: Small - ~2-3 hours implementation + testing
Risk: Low - Fallback to existing behavior if anything fails
Summary
Add automatic token forwarding from the OAuth broker to the PinShare frontend using the
postMessageAPI. This will streamline the OAuth flow for both hosted (share.episkopos.community) and self-hosted instances by eliminating the manual copy/paste step, while maintaining full backward compatibility.Current Implementation
OAuth Broker Flow (
oauth-broker/main.go):https://oauth.episkopos.community/api/google-drive/set-tokenPain Points:
Proposed Solution
postMessage-based Auto-Forward
Use the browser's
postMessageAPI to securely send the token from the OAuth broker popup directly to the frontend window.New Flow:
window.openerwindow.opener.postMessage()to send token to frontendFallback Flow (backward compatible):
window.openerunavailable → show copy/paste UIWhy postMessage?
✅ Works for both cloud-hosted and self-hosted:
✅ Secure:
✅ Backward compatible:
Implementation Details
1. OAuth Broker Changes (
oauth-broker/main.go)Modify
renderSuccess()function (lines ~276-380):Add JavaScript to success page:
Message format:
{ "type": "PINSHARE_OAUTH_TOKEN", "token": { "access_token": "ya29.a0...", "token_type": "Bearer", "refresh_token": "1//0g...", "expiry": "2024-11-17T23:45:00Z" }, "source": "pinshare-oauth-broker" }2. Frontend Changes (
pinshare-ui/src/pages/GoogleDriveImport.jsx)Update "Get Token from OAuth Broker" button handler (currently ~line 314):
3. Security Considerations
Origin Validation:
VITE_OAUTH_BASE*but frontend validates (postMessage API design pattern)Token Exposure:
CORS:
PS_ALLOWED_ORIGINSconfiguration unchangedPopup Blockers:
4. UI/UX Flow Diagram
Happy Path (Automatic):
Fallback Path (Manual):
5. Backward Compatibility Matrix
Edge Cases:
VITE_OAUTH_BASEoriginTesting Checklist
Files to Modify
oauth-broker/main.go(~line 276-380)renderSuccess()functionpinshare-ui/src/pages/GoogleDriveImport.jsx(~line 314+)window.open()from new tab to popupmessageevent listener with origin validationoauth-broker/README.md(optional)Rollout Strategy
Phase 1: Deploy updated broker
Phase 2: Deploy updated frontend
Phase 3: Monitor and iterate
Benefits
✅ Better UX: No manual copy/paste for 95%+ of users
✅ Works everywhere: Cloud-hosted, self-hosted, cross-origin
✅ Secure: Origin validation, no URL token exposure
✅ Backward compatible: Existing flows unchanged
✅ Low risk: Graceful degradation if anything fails
Related Issues
a94ff64ande353a42Priority: Medium - Improves UX but existing flow works fine
Effort: Small - ~2-3 hours implementation + testing
Risk: Low - Fallback to existing behavior if anything fails