Skip to content

Commit 86a496b

Browse files
committed
feat: add secure iframe user details for browser login
Implement iframe integration to display full user details securely in browser profile popup, avoiding masked data exposure to extensions. Adds consistent getAccountBaseURL API across both login services and proper proxy URL handling for localhost development.
1 parent 7ff493f commit 86a496b

File tree

6 files changed

+109
-5
lines changed

6 files changed

+109
-5
lines changed

serve-proxy.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const fs = require('fs');
88
const httpProxy = require('http-proxy');
99

1010
// Account server configuration - switch between local and production
11-
const ACCOUNT_SERVER = 'https://account.phcode.dev'; // Production
12-
// const ACCOUNT_SERVER = 'http://localhost:5000'; // Local development
11+
//const ACCOUNT_SERVER = 'https://account.phcode.dev'; // Production
12+
const ACCOUNT_SERVER = 'http://localhost:5000'; // Local development
1313

1414
// Default configuration
1515
let config = {

src/services/html/profile-popup.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
<div class="user-avatar" style="background-color: {{avatarColor}};">
55
{{initials}}
66
</div>
7-
<div class="user-info">
7+
<div class="user-info" style="width: calc(100% - 40px);">
88
<div class="user-name"><secure-name></secure-name></div>
99
<div class="user-email"><secure-email></secure-email></div>
10+
<iframe id="user-details-frame" class="user-details-iframe" style="display: none; padding: 0; border: none; background: transparent; width: 100%; height: auto; overflow: hidden;" scrolling="no"></iframe>
1011
<div class="{{planClass}}">{{planName}}</div>
1112
</div>
1213
</div>

src/services/login-browser.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ define(function (require, exports, module) {
413413
secureExports.signOutAccount = signOutBrowser;
414414
secureExports.getProfile = getProfile;
415415
secureExports.verifyLoginStatus = () => _verifyBrowserLogin(false);
416+
secureExports.getAccountBaseURL = _getAccountBaseURL;
416417
}
417418

418419
// public exports

src/services/login-desktop.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ define(function (require, exports, module) {
7474
return userProfile;
7575
}
7676

77+
/**
78+
* Get the account base URL for API calls
79+
* For desktop apps, this directly uses the configured account URL
80+
*/
81+
function getAccountBaseURL() {
82+
return Phoenix.config.account_url.replace(/\/$/, ''); // Remove trailing slash
83+
}
84+
7785
const ERR_RETRY_LATER = "retry_later";
7886
const ERR_INVALID = "invalid";
7987

@@ -408,6 +416,7 @@ define(function (require, exports, module) {
408416
secureExports.signOutAccount = signOutAccount;
409417
secureExports.getProfile = getProfile;
410418
secureExports.verifyLoginStatus = () => _verifyLogin(false);
419+
secureExports.getAccountBaseURL = getAccountBaseURL;
411420
}
412421

413422
// public exports

src/services/profile-menu.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
define(function (require, exports, module) {
22
const Mustache = require("thirdparty/mustache/mustache"),
33
PopUpManager = require("widgets/PopUpManager"),
4+
ThemeManager = require("view/ThemeManager"),
45
Strings = require("strings");
56

67
const KernalModeTrust = window.KernalModeTrust;
@@ -211,6 +212,78 @@ define(function (require, exports, module) {
211212
/* eslint-disable-next-line*/
212213
customElements.define ('secure-name', SecureName); // space is must in define ( to prevent build fail
213214

215+
/**
216+
* Load user details iframe with secure user information
217+
*/
218+
function _loadUserDetailsIframe() {
219+
if (!Phoenix.isNativeApp && $popup) {
220+
const $iframe = $popup.find("#user-details-frame");
221+
const $secureName = $popup.find(".user-name secure-name");
222+
const $secureEmail = $popup.find(".user-email secure-email");
223+
224+
if ($iframe.length) {
225+
// Get account base URL for iframe using login service
226+
const accountBaseURL = KernalModeTrust.loginService.getAccountBaseURL();
227+
const currentTheme = ThemeManager.getCurrentTheme();
228+
const nameColor = (currentTheme && currentTheme.dark) ? "FFFFFF" : "000000";
229+
230+
// Configure iframe URL with styling parameters
231+
const iframeURL = `${accountBaseURL}/getUserDetailFrame?` +
232+
`includeName=true&` +
233+
`nameFontSize=14px&` +
234+
`emailFontSize=12px&` +
235+
`nameColor=%23${nameColor}&` +
236+
`emailColor=%23666666&` +
237+
`backgroundColor=transparent`;
238+
239+
// Listen for iframe load events
240+
const messageHandler = function(event) {
241+
// Only accept messages from trusted account domain
242+
// Handle proxy case where accountBaseURL is '/proxy/accounts'
243+
let trustedOrigin;
244+
if (accountBaseURL.startsWith('/proxy/accounts')) {
245+
// For localhost with proxy, accept messages from current origin
246+
trustedOrigin = window.location.origin;
247+
} else {
248+
// For production, get origin from account URL
249+
trustedOrigin = new URL(accountBaseURL).origin;
250+
}
251+
252+
if (event.origin !== trustedOrigin) {
253+
return;
254+
}
255+
256+
if (event.data && event.data.loaded) {
257+
// Hide secure DOM elements and show iframe
258+
$secureName.hide();
259+
$secureEmail.hide();
260+
$iframe.show();
261+
262+
// Adjust iframe height based on content
263+
$iframe.css('height', '36px'); // Approximate height for name + email
264+
265+
// Remove event listener
266+
window.removeEventListener('message', messageHandler);
267+
}
268+
};
269+
270+
// Add message listener
271+
window.addEventListener('message', messageHandler);
272+
273+
// Set iframe source to load user details
274+
$iframe.attr('src', iframeURL);
275+
276+
// Fallback timeout - if iframe doesn't load in 5 seconds, keep secure elements
277+
setTimeout(() => {
278+
if ($iframe.is(':hidden')) {
279+
console.log('User details iframe failed to load, keeping secure elements');
280+
window.removeEventListener('message', messageHandler);
281+
}
282+
}, 5000);
283+
}
284+
}
285+
}
286+
214287
/**
215288
* Shows the user profile popup when the user is logged in
216289
*/
@@ -271,6 +344,9 @@ define(function (require, exports, module) {
271344
});
272345

273346
_setupDocumentClickHandler();
347+
348+
// Load user details iframe for browser apps (after popup is created)
349+
_loadUserDetailsIframe();
274350
}
275351

276352
/**

src/services/readme-login-browser-no_dist.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ This document provides comprehensive documentation for integrating with the Phoe
66

77
The Phoenix browser application uses a login service to authenticate users across the phcode.dev domain ecosystem. The login service handles user authentication, session management, and provides secure API endpoints for login operations.
88

9+
**Key Features:**
10+
- Domain-wide session management using session cookies
11+
- Secure user profile display via iframe integration
12+
- Proxy server support for localhost development
13+
914
**Key Files:**
1015
- `src/services/login-browser.js` - Main browser login implementation
1116
- `serve-proxy.js` - Proxy server for localhost development
@@ -122,9 +127,15 @@ The login service provides these key endpoints:
122127

123128
### Authentication
124129
- `POST /signOutPost` - Sign out user (new endpoint with proper JSON handling)
125-
- `GET /resolveBrowserSession` - Validate and resolve current session
130+
- `GET /resolveBrowserSession` - Validate and resolve current session (returns masked user data for security)
126131
- `GET /signOut` - Legacy signout endpoint (deprecated for browser use)
127132

133+
### User Profile Display
134+
- `GET /getUserDetailFrame` - Returns HTML iframe with full user details for secure display
135+
- Query parameters for styling: `includeName`, `nameFontSize`, `emailFontSize`, `nameColor`, `emailColor`, `backgroundColor`
136+
- CSP-protected to only allow embedding in trusted domains
137+
- Cross-origin communication via postMessage when loaded
138+
128139
### Session Management
129140
- Session validation through `session` cookie
130141
- Automatic session invalidation on logout
@@ -187,6 +198,12 @@ Browser (localhost:8000) → /proxy/accounts/* → serve-proxy.js
187198
- Session cookies should have appropriate expiration times
188199
- Logout should properly invalidate sessions on both client and server
189200

201+
### User Data Security
202+
- **Masked API Data**: The `resolveBrowserSession` endpoint returns masked user data (e.g., "J***", "j***@g***.com") to prevent exposure to browser extensions
203+
- **Secure iframe Display**: Full user details are displayed via iframe from trusted account server
204+
- **CSP Protection**: iframe is protected by Content Security Policy headers restricting embedding domains
205+
- **Cross-Origin Safety**: iframe communication uses secure postMessage protocol
206+
190207
---
191208

192-
For browser implementation details, see the source code in `src/services/login-browser.js` and related files. For desktop authentication, see `src/services/login-desktop.js` and `readme-login-desktop-no_dist.md`.
209+
For browser implementation details, see the source code in `src/services/login-browser.js` and related files. For desktop authentication, see `src/services/login-desktop.js` and `readme-login-desktop-no_dist.md`.

0 commit comments

Comments
 (0)