Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions serve-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
/* eslint-env node */

const http = require('http');
const https = require('https');
const url = require('url');
const path = require('path');
const fs = require('fs');
const httpProxy = require('http-proxy');

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

// Default configuration
let config = {
Expand Down Expand Up @@ -90,8 +89,6 @@ proxy.on('proxyReq', (proxyReq, req) => {
if (originalOrigin && originalOrigin.includes('localhost:8000')) {
const newOrigin = originalOrigin.replace(/localhost:8000/g, 'phcode.dev');
proxyReq.setHeader('Origin', newOrigin);
} else if (!originalOrigin) {
proxyReq.setHeader('Origin', 'https://phcode.dev');
}

// Ensure HTTPS scheme
Expand Down
3 changes: 2 additions & 1 deletion src/services/html/profile-popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
<div class="user-avatar" style="background-color: {{avatarColor}};">
{{initials}}
</div>
<div class="user-info">
<div class="user-info" style="width: calc(100% - 40px);">
<div class="user-name"><secure-name></secure-name></div>
<div class="user-email"><secure-email></secure-email></div>
<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>
<div class="{{planClass}}">{{planName}}</div>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/services/login-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ define(function (require, exports, module) {
secureExports.signOutAccount = signOutBrowser;
secureExports.getProfile = getProfile;
secureExports.verifyLoginStatus = () => _verifyBrowserLogin(false);
secureExports.getAccountBaseURL = _getAccountBaseURL;
}

// public exports
Expand Down
9 changes: 9 additions & 0 deletions src/services/login-desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ define(function (require, exports, module) {
return userProfile;
}

/**
* Get the account base URL for API calls
* For desktop apps, this directly uses the configured account URL
*/
function getAccountBaseURL() {
return Phoenix.config.account_url.replace(/\/$/, ''); // Remove trailing slash
}

const ERR_RETRY_LATER = "retry_later";
const ERR_INVALID = "invalid";

Expand Down Expand Up @@ -408,6 +416,7 @@ define(function (require, exports, module) {
secureExports.signOutAccount = signOutAccount;
secureExports.getProfile = getProfile;
secureExports.verifyLoginStatus = () => _verifyLogin(false);
secureExports.getAccountBaseURL = getAccountBaseURL;
}

// public exports
Expand Down
76 changes: 76 additions & 0 deletions src/services/profile-menu.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
define(function (require, exports, module) {
const Mustache = require("thirdparty/mustache/mustache"),
PopUpManager = require("widgets/PopUpManager"),
ThemeManager = require("view/ThemeManager"),
Strings = require("strings");

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

/**
* Load user details iframe with secure user information
*/
function _loadUserDetailsIframe() {
if (!Phoenix.isNativeApp && $popup) {
const $iframe = $popup.find("#user-details-frame");
const $secureName = $popup.find(".user-name secure-name");
const $secureEmail = $popup.find(".user-email secure-email");

if ($iframe.length) {
// Get account base URL for iframe using login service
const accountBaseURL = KernalModeTrust.loginService.getAccountBaseURL();
const currentTheme = ThemeManager.getCurrentTheme();
const nameColor = (currentTheme && currentTheme.dark) ? "FFFFFF" : "000000";

// Configure iframe URL with styling parameters
const iframeURL = `${accountBaseURL}/getUserDetailFrame?` +
`includeName=true&` +
`nameFontSize=14px&` +
`emailFontSize=12px&` +
`nameColor=%23${nameColor}&` +
`emailColor=%23666666&` +
`backgroundColor=transparent`;

// Listen for iframe load events
const messageHandler = function(event) {
// Only accept messages from trusted account domain
// Handle proxy case where accountBaseURL is '/proxy/accounts'
let trustedOrigin;
if (accountBaseURL.startsWith('/proxy/accounts')) {
// For localhost with proxy, accept messages from current origin
trustedOrigin = window.location.origin;
} else {
// For production, get origin from account URL
trustedOrigin = new URL(accountBaseURL).origin;
}

if (event.origin !== trustedOrigin) {
return;
}

if (event.data && event.data.loaded) {
// Hide secure DOM elements and show iframe
$secureName.hide();
$secureEmail.hide();
$iframe.show();

// Adjust iframe height based on content
$iframe.css('height', '36px'); // Approximate height for name + email

// Remove event listener
window.removeEventListener('message', messageHandler);
}
};

// Add message listener
window.addEventListener('message', messageHandler);

// Set iframe source to load user details
$iframe.attr('src', iframeURL);

// Fallback timeout - if iframe doesn't load in 5 seconds, keep secure elements
setTimeout(() => {
if ($iframe.is(':hidden')) {
console.log('User details iframe failed to load, keeping secure elements');
window.removeEventListener('message', messageHandler);
}
}, 5000);
}
}
}

/**
* Shows the user profile popup when the user is logged in
*/
Expand Down Expand Up @@ -271,6 +344,9 @@ define(function (require, exports, module) {
});

_setupDocumentClickHandler();

// Load user details iframe for browser apps (after popup is created)
_loadUserDetailsIframe();
}

/**
Expand Down
21 changes: 19 additions & 2 deletions src/services/readme-login-browser-no_dist.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ This document provides comprehensive documentation for integrating with the Phoe

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.

**Key Features:**
- Domain-wide session management using session cookies
- Secure user profile display via iframe integration
- Proxy server support for localhost development

**Key Files:**
- `src/services/login-browser.js` - Main browser login implementation
- `serve-proxy.js` - Proxy server for localhost development
Expand Down Expand Up @@ -122,9 +127,15 @@ The login service provides these key endpoints:

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

### User Profile Display
- `GET /getUserDetailFrame` - Returns HTML iframe with full user details for secure display
- Query parameters for styling: `includeName`, `nameFontSize`, `emailFontSize`, `nameColor`, `emailColor`, `backgroundColor`
- CSP-protected to only allow embedding in trusted domains
- Cross-origin communication via postMessage when loaded

### Session Management
- Session validation through `session` cookie
- Automatic session invalidation on logout
Expand Down Expand Up @@ -187,6 +198,12 @@ Browser (localhost:8000) → /proxy/accounts/* → serve-proxy.js
- Session cookies should have appropriate expiration times
- Logout should properly invalidate sessions on both client and server

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

---

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`.
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`.
Loading