Skip to content
Open
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
12 changes: 6 additions & 6 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "scam",
"version": "1.3.1",
"manifest_version": 2,
"version": "1.4.0",
"manifest_version": 3,
"description": "SoundCloud Account Manager: Quickly switch between multiple SoundCloud accounts.",
"homepage_url": "https://github.com/nihilist/scam",
"icons": {
Expand All @@ -24,11 +24,11 @@
}
],
"background": {
"scripts": ["src/background.js"],
"persistent": false
"service_worker": "src/background.js",
"type": "module"
},
"permissions": [
"cookies",
"permissions": ["cookies", "storage"],
"host_permissions": [
"https://soundcloud.com/*",
"https://api-auth.soundcloud.com/*",
"https://secure.soundcloud.com/*"
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"author": "reid",
"license": "MIT",
"devDependencies": {
"eslint": "^6.5.1",
"eslint": "^8.2.0",
"eslint-config-airbnb": "19.0.4",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.18.2"
"eslint-plugin-import": "^2.25.3"
}
}
121 changes: 78 additions & 43 deletions src/background.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,91 @@
const emitter = chrome.runtime.onMessage ?? browser.runtime.onMessage;
// Self-activate service worker to keep it running when first installed
self.addEventListener('install', () => {
self.skipWaiting();
});

// Make sure the service worker activates immediately
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
});

emitter.addListener((request, sender, sendResponse) => {
// Listen for messages
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.method === 'forceLogout') {
chrome.cookies.remove({
url: 'https://api-auth.soundcloud.com/connect/',
name: '_soundcloud_session',
}, () => sendResponse(true));
chrome.cookies
.remove({
url: 'https://api-auth.soundcloud.com/connect/',
name: '_soundcloud_session',
})
.then(() => sendResponse(true));
} else if (request.method === 'getCookie') {
chrome.cookies.get({
url: 'https://soundcloud.com/',
name: request.data.name,
}, (cookie) => sendResponse(cookie));
chrome.cookies
.get({
url: 'https://soundcloud.com/',
name: request.data.name,
})
.then((cookie) => sendResponse(cookie));
} else if (request.method === 'setCookie') {
chrome.cookies.set({
url: 'https://soundcloud.com/',
name: request.data.name,
value: request.data.value,
secure: true,
expirationDate: Math.floor(+new Date(+new Date() + 31536e6) / 1000), // expiry 1 year from now
}, (cookie) => sendResponse(cookie));
chrome.cookies
.set({
url: 'https://soundcloud.com/',
name: request.data.name,
value: request.data.value,
secure: true,
// expiry 1 year from now
expirationDate: Math.floor(+new Date(+new Date() + 31536e6) / 1000),
})
.then((cookie) => sendResponse(cookie));
} else if (request.method === 'removeCookie') {
chrome.cookies.remove({
url: 'https://soundcloud.com/',
name: request.data.name,
}, (details) => sendResponse(details));
chrome.cookies
.remove({
url: 'https://soundcloud.com/',
name: request.data.name,
})
.then((details) => sendResponse(details));
} else if (request.method === 'validateCookie') {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) sendResponse(xhr.status === 200);
};
xhr.open('POST', 'https://api-auth.soundcloud.com/connect/session');
xhr.send(JSON.stringify({ session: { access_token: request.data.cookie } }));
fetch('https://api-auth.soundcloud.com/connect/session', {
method: 'POST',
body: JSON.stringify({ session: { access_token: request.data.cookie } }),
})
.then((response) => {
sendResponse(response.status === 200);
})
.catch(() => {
sendResponse(false);
});
} else if (request.method === 'refreshCookie') {
const fetchNewCookie = () => {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
sendResponse(JSON.parse(xhr.responseText).session.access_token);
}
};
xhr.open('POST', 'https://api-auth.soundcloud.com/connect/session/token');
xhr.send('null');
fetch('https://api-auth.soundcloud.com/connect/session/token', {
method: 'POST',
body: 'null',
})
.then((response) => {
if (response.status === 200) {
return response.json();
}
throw new Error('Failed to refresh cookie');
})
.then((data) => {
sendResponse(data.session.access_token);
})
.catch(() => {
sendResponse(null);
});
};

if (request.data && request.data.cookie) {
chrome.cookies.set({
url: 'https://api-auth.soundcloud.com/connect/',
name: '_soundcloud_session',
value: request.data.cookie,
secure: true,
SameSiteStatus: 'no_restriction',
}, () => fetchNewCookie());
} else fetchNewCookie();
chrome.cookies
.set({
url: 'https://api-auth.soundcloud.com/connect/',
name: '_soundcloud_session',
value: request.data.cookie,
secure: true,
sameSite: 'no_restriction',
})
.then(() => fetchNewCookie());
} else {
fetchNewCookie();
}
}

return true;
Expand Down
42 changes: 29 additions & 13 deletions src/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,46 @@ const sel = (selector) => document.querySelector(selector);
const MESSAGE_ORIGIN = 'https://soundcloud.com';

let sessions;

const getSession = (username) => sessions[username];

const switchSession = (user) => {
chrome.runtime.sendMessage(
{
method: 'setCookie',
data: { name: 'oauth_token', value: getSession(user).cookie },
}, () => parent.postMessage('_scam_reload', MESSAGE_ORIGIN),
},
() => parent.postMessage('_scam_reload', MESSAGE_ORIGIN),
);
};

const injectLoggedOutSwitcher = () => {
if (sessions) {
if (sessions && Object.keys(sessions).length > 0) {
const publicSignIn = sel('.provider-buttons');
if (!publicSignIn) return;

const scamDiv = document.createElement('div');
const scamBtn = document.createElement('button');
const accountSelector = document.createElement('select');

scamDiv.setAttribute('class', 'form-row');
scamBtn.setAttribute('class', 'provider-button sc-button sc-button-large');
accountSelector.setAttribute('class', 'provider-button sc-button sc-button-large');
accountSelector.setAttribute(
'class',
'provider-button sc-button sc-button-large',
);
accountSelector.setAttribute('style', 'height: 100%');
scamBtn.innerText = 'Saved accounts';

scamDiv.appendChild(scamBtn);
publicSignIn.appendChild(scamDiv);

const firstOption = document.createElement('option');
firstOption.innerText = 'Accounts';
firstOption.disabled = true;
firstOption.selected = true;
accountSelector.appendChild(firstOption);

Object.keys(sessions).forEach((accountName) => {
const accountEl = document.createElement('option');
accountEl.value = accountName;
Expand All @@ -47,15 +59,19 @@ const injectLoggedOutSwitcher = () => {
}
};

window.addEventListener('message', (message) => {
const { origin, data } = message;
if (origin !== MESSAGE_ORIGIN) return;
window.addEventListener(
'message',
(message) => {
const { origin, data } = message;
if (origin !== MESSAGE_ORIGIN) return;

if (Array.isArray(data)) {
const [name, sessionsData] = data;
if (name === '_scam_sessions') {
sessions = sessionsData;
injectLoggedOutSwitcher();
if (Array.isArray(data)) {
const [name, sessionsData] = data;
if (name === '_scam_sessions') {
sessions = sessionsData;
injectLoggedOutSwitcher();
}
}
}
}, false);
},
false,
);
Loading