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
8 changes: 5 additions & 3 deletions src/phoenix/trust_ring.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,18 @@ function _selectKeys() {
return generateRandomKeyAndIV();
}

const CRED_KEY_API = "API_KEY";
const CRED_KEY_ENTITLEMENTS = "ENTITLEMENTS_GRANT_KEY";
const CRED_KEY_API = Phoenix.isTestWindow ? "API_KEY_TEST" : "API_KEY";
const CRED_KEY_PROMO = Phoenix.isTestWindow ? "PROMO_GRANT_KEY_TEST" : "PROMO_GRANT_KEY";
const SIGNATURE_SALT_KEY = Phoenix.isTestWindow ? "SIGNATURE_SALT_KEY_TEST" : "SIGNATURE_SALT_KEY";
const { key, iv } = _selectKeys();
// this key is set at boot time as a truct base for all the core components before any extensions are loaded.
// just before extensions are loaded, this key is blanked. This can be used by core modules to talk with other
// core modules securely without worrying about interception by extensions.
// KernalModeTrust should only be available within all code that loads before the first default/any extension.
window.KernalModeTrust = {
CRED_KEY_API,
CRED_KEY_ENTITLEMENTS,
CRED_KEY_PROMO,
SIGNATURE_SALT_KEY,
aesKeys: { key, iv },
setCredential,
getCredential,
Expand Down
53 changes: 37 additions & 16 deletions src/services/login-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ define(function (require, exports, module) {
const ERR_INVALID = "invalid";
const ERR_NOT_LOGGED_IN = "not_logged_in";

// save a copy of window.fetch so that extensions wont tamper with it.
let fetchFn = window.fetch;

/**
* Resolve browser session using cookies
* @return {Promise<Object>} A promise resolving to user profile or error object
Expand All @@ -110,7 +113,7 @@ define(function (require, exports, module) {
return {err: ERR_RETRY_LATER};
}
try {
const response = await fetch(resolveURL, {
const response = await fetchFn(resolveURL, {
method: 'GET',
credentials: 'include', // Include cookies
headers: {
Expand Down Expand Up @@ -148,6 +151,12 @@ define(function (require, exports, module) {
PreferencesManager.stateManager.set(PREF_USER_PROFILE_VERSION, crypto.randomUUID());
}

/**
* Calls remote resolveBrowserSession endpoint to verify login status. should not be used frequently.
* @param silentCheck
* @returns {Promise<void>}
* @private
*/
async function _verifyBrowserLogin(silentCheck = false) {
console.log("Verifying browser login status...");

Expand All @@ -158,28 +167,36 @@ define(function (require, exports, module) {
isLoggedInUser = true;
ProfileMenu.setLoggedIn(userProfile.profileIcon.initials, userProfile.profileIcon.color);
console.log("Browser login verified for:", userProfile.email);
Metrics.countEvent(Metrics.EVENT_TYPE.AUTH, "browser", "OKLogin");
return;
}

// User is not logged in or error occurred
// User is not logged in or error occurred if here
if(resolveResponse.err === ERR_NOT_LOGGED_IN) {
console.log("No browser session found. Not logged in");
// Only reset UI state if this is not a silent background check
if (!silentCheck) {
_resetBrowserLogin();
} else {
// For silent checks, just update the internal state
isLoggedInUser = false;
userProfile = null;
}
Metrics.countEvent(Metrics.EVENT_TYPE.AUTH, "browser", "NotLoggedIn");
_handleLoginError(silentCheck);
return;
}

if (resolveResponse.err === ERR_INVALID) {
console.log("Invalid auth token, resetting login state");
Metrics.countEvent(Metrics.EVENT_TYPE.AUTH, "browser", "invalidLogin");
_handleLoginError(silentCheck);
return;
}

// Other errors (network, retry later, etc.)
console.log("Browser login verification failed:", resolveResponse.err);
console.log("Browser login verification failed (temporary):", resolveResponse.err);
Metrics.countEvent(Metrics.EVENT_TYPE.AUTH, "browser", "RetryLogin");
// Don't reset login state for temporary errors, regardless of silent check
}

function _handleLoginError(silentCheck) {
if (!silentCheck) {
_resetBrowserLogin();
} else {
// For silent checks, just update the internal state
isLoggedInUser = false;
userProfile = null;
}
Expand Down Expand Up @@ -316,7 +333,7 @@ define(function (require, exports, module) {
async function signOutBrowser() {
const logoutURL = `${_getAccountBaseURL()}/signOut`;
try {
const response = await fetch(logoutURL, {
const response = await fetchFn(logoutURL, {
method: 'POST',
credentials: 'include', // Include cookies
headers: {
Expand Down Expand Up @@ -376,13 +393,15 @@ define(function (require, exports, module) {
return;
}

// Always verify login on browser app start (silent check to avoid closing popups)
_verifyBrowserLogin(true).catch(console.error);
// Always verify login on browser app start
_verifyBrowserLogin().catch(console.error);

// Watch for profile changes from other windows/tabs
const pref = PreferencesManager.stateManager.definePreference(PREF_USER_PROFILE_VERSION, 'string', '0');
pref.watchExternalChanges();
pref.on('change', _verifyBrowserLogin);
pref.on('change', ()=>{
_verifyBrowserLogin(true).catch(console.error);
});

// Note: We don't do automatic verification on page focus to avoid server overload.
// Automatic checks are only done during the login waiting dialog period.
Expand All @@ -399,7 +418,9 @@ define(function (require, exports, module) {
LoginService.signInToAccount = signInToBrowser;
LoginService.signOutAccount = signOutBrowser;
LoginService.getProfile = getProfile;
LoginService.verifyLoginStatus = () => _verifyBrowserLogin(false);
// verifyLoginStatus Calls remote resolveBrowserSession endpoint to verify. should not be used frequently.
// All users are required to use isLoggedIn API instead.
LoginService._verifyLoginStatus = () => _verifyBrowserLogin(false);
LoginService.getAccountBaseURL = _getAccountBaseURL;
init();
}
Expand Down
11 changes: 7 additions & 4 deletions src/services/login-desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ define(function (require, exports, module) {
let userProfile = null;
let isLoggedInUser = false;

// save a copy of window.fetch so that extensions wont tamper with it.
let fetchFn = window.fetch;

// just used as trigger to notify different windows about user profile changes
const PREF_USER_PROFILE_VERSION = "userProfileVersion";

Expand Down Expand Up @@ -98,7 +101,7 @@ define(function (require, exports, module) {
return {err: ERR_RETRY_LATER};
}
try {
const response = await fetch(resolveURL);
const response = await fetchFn(resolveURL);
if (response.status === 400 || response.status === 404) {
// 404 api key not found and 400 Bad Request, eg: verification code mismatch
return {err: ERR_INVALID};
Expand Down Expand Up @@ -192,7 +195,7 @@ define(function (require, exports, module) {
const resolveURL = `${Phoenix.config.account_url}getAppAuthSession?autoAuthPort=${authPortURL}&appName=${appName}`;
// {"isSuccess":true,"appSessionID":"a uuid...","validationCode":"SWXP07"}
try {
const response = await fetch(resolveURL);
const response = await fetchFn(resolveURL);
if (response.ok) {
const {appSessionID, validationCode} = await response.json();
if(!appSessionID || !validationCode) {
Expand Down Expand Up @@ -345,7 +348,7 @@ define(function (require, exports, module) {
appSessionID: userProfile.apiKey
};

const response = await fetch(resolveURL, {
const response = await fetchFn(resolveURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
Expand Down Expand Up @@ -412,7 +415,7 @@ define(function (require, exports, module) {
LoginService.signInToAccount = signInToAccount;
LoginService.signOutAccount = signOutAccount;
LoginService.getProfile = getProfile;
LoginService.verifyLoginStatus = () => _verifyLogin(false);
LoginService._verifyLoginStatus = () => _verifyLogin(false);
LoginService.getAccountBaseURL = getAccountBaseURL;
init();
}
Expand Down
15 changes: 14 additions & 1 deletion src/services/login-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ define(function (require, exports, module) {
const MS_IN_DAY = 10 * 24 * 60 * 60 * 1000;
const TEN_MINUTES = 10 * 60 * 1000;

// save a copy of window.fetch so that extensions wont tamper with it.
let fetchFn = window.fetch;

const KernalModeTrust = window.KernalModeTrust;
if(!KernalModeTrust){
// integrated extensions will have access to kernal mode, but not external extensions
Expand Down Expand Up @@ -107,7 +110,7 @@ define(function (require, exports, module) {
fetchOptions.credentials = 'include';
}

const response = await fetch(url, fetchOptions);
const response = await fetchFn(url, fetchOptions);

if (response.ok) {
const result = await response.json();
Expand Down Expand Up @@ -334,6 +337,16 @@ define(function (require, exports, module) {
LoginService.clearEntitlements = clearEntitlements;
LoginService.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;

// Test-only exports for integration testing
if (Phoenix.isTestWindow) {
window._test_login_service_exports = {
LoginService,
setFetchFn: function _setFetchFn(fn) {
fetchFn = fn;
}
};
}

// Start the entitlements monitor timer
startEntitlementsMonitor();
});
13 changes: 12 additions & 1 deletion src/services/pro-dialogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ define(function (require, exports, module) {
proUpgradeHTML = require("text!./html/pro-upgrade.html"),
proEndedHTML = require("text!./html/promo-ended.html");

// save a copy of window.fetch so that extensions wont tamper with it.
let fetchFn = window.fetch;

function showProUpgradeDialog(trialDays) {
const title = StringUtils.format(Strings.PROMO_UPGRADE_TITLE, proTitle);
const message = StringUtils.format(Strings.PROMO_UPGRADE_MESSAGE, trialDays);
Expand Down Expand Up @@ -113,7 +116,7 @@ define(function (require, exports, module) {

try {
const configURL = `${brackets.config.promotions_url}app/config.json`;
const response = await fetch(configURL);
const response = await fetchFn(configURL);
if (!response.ok) {
_showLocalProEndedDialog();
return;
Expand All @@ -130,6 +133,14 @@ define(function (require, exports, module) {
}
}

if (Phoenix.isTestWindow) {
window._test_pro_dlg_login_exports = {
setFetchFn: function _setDdateNowFn(fn) {
fetchFn = fn;
}
};
}

exports.showProUpgradeDialog = showProUpgradeDialog;
exports.showProEndedDialog = showProEndedDialog;
});
2 changes: 1 addition & 1 deletion src/services/profile-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ define(function (require, exports, module) {
// Set flag to indicate this is a background refresh
isBackgroundRefresh = true;

KernalModeTrust.loginService.verifyLoginStatus().then(() => {
KernalModeTrust.loginService._verifyLoginStatus().then(() => {
// Clear the background refresh flag
isBackgroundRefresh = false;

Expand Down
Loading
Loading