This code manages the Discord OAuth flow by exchanging a handshake message, authorizing the user, and fetching an access token to authenticate with the Discord API. It uses event listeners to respond to messages from the parent window, sending POST requests and authentication messages to complete the OAuth flow.
npm run import -- "discord client auth code"
let expiry
let result
let initNonce = Math.round(Math.random() * 1000)
window.addEventListener('message', async (evt) => {
document.querySelector('h1').innerHTML = '<pre>' + JSON.stringify(evt.data, null, 2) + '</pre>'
const sourceWindow = () => {
return (window.parent.opener || window.parent)
}
if(evt.data[0] == 3 && typeof evt.data[1].frame_id != 'undefined') {
result = evt.data[1] // get it from page init instead of query params
// send handshake
sourceWindow().postMessage([0, {
v: 1,
encoding: 'json',
client_id: '{CLIENT_ID}',
frame_id: evt.data[1].frame_id,
nonce: initNonce,
transfer: void 0
}], 'https://discord.com' || '*')
}
if(evt.data[0] == 1 && evt.data[1].cmd == 'DISPATCH') {
// start authorization chain
sourceWindow().postMessage([1, {
cmd: 'AUTHORIZE',
args: {
client_id: '{CLIENT_ID}',
prompt: 'none',
response_type: 'code',
scope: ['applications.commands', 'identify', 'guilds.members.read', 'rpc.activities.write'],
state: '',
},
nonce: Math.random() * 1000,
transfer: void 0
}], 'https://discord.com' || '*')
}
if(evt.data[1].cmd == 'AUTHORIZE') {
code = evt.data[1].data.code
const response = await fetch("{BASE_URI}api/token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: 'same-origin',
body: JSON.stringify({
code,
launch_id: result.launch_id,
frame_id: result.frame_id,
}),
});
const { access_token, expires_in, session_id } = await response.json();
window.session_id = session_id
debugger
// TODO: take authenticate commands
sourceWindow().postMessage([1, {
cmd: 'AUTHENTICATE',
args: {
access_token: access_token
},
nonce: Math.random() * 1000,
transfer: void 0
}], 'https://discord.com' || '*')
expiry = Date.now() + expires_in
}
if(evt.data[1].cmd == 'AUTHENTICATE') {
// TODO: some authenticated page update
// trying to solve the cookie problem
if(window.session_id)
window.location = '{BASE_URI}?t=' + Date.now() + '&session=' + window.session_id
}
})
// Constants and variables
const DISCORD_API_URI = 'https://discord.com/api/token';
const COOKIE_NAME ='session';
const COOKIE_EXPIRY_NAME = 'expiry';
let result;
let initNonce = Math.floor(Math.random() * 1000);
// Add event listener to handle messages from the parent window
window.addEventListener('message', async (evt) => {
// Log received message
document.querySelector('h1').innerHTML = `${JSON.stringify(evt.data, null, 2)}
`;
// Get the source window (parent or opener)
const getSourceWindow = () => (window.parent.opener || window.parent);
// Handle message with handshake
if (evt.data[0] === 3 && 'frame_id' in evt.data[1]) {
result = evt.data[1];
// Send handshake to Discord
const handshakeMessage = [
0,
{
v: 1,
encoding: 'json',
client_id: '{CLIENT_ID}',
frame_id: result.frame_id,
nonce: initNonce,
transfer: undefined
}
];
getSourceWindow().postMessage(handshakeMessage, 'https://discord.com');
}
// Handle message with dispatch command
if (evt.data[0] === 1 && evt.data[1].cmd === 'DISPATCH') {
// Start authorization chain
const authorizeMessage = [
1,
{
cmd: 'AUTHORIZE',
args: {
client_id: '{CLIENT_ID}',
prompt: 'none',
response_type: 'code',
scope: ['applications.commands', 'identify', 'guilds.members.read', 'rpc.activities.write'],
state: ''
},
nonce: Math.floor(Math.random() * 1000),
transfer: undefined
}
];
getSourceWindow().postMessage(authorizeMessage, 'https://discord.com');
}
// Handle message with authorize command
if ('cmd' in evt.data[1] && evt.data[1].cmd === 'AUTHORIZE') {
const code = evt.data[1].data.code;
try {
const response = await fetch(DISCORD_API_URI, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials:'same-origin',
body: JSON.stringify({
code,
launch_id: result.launch_id,
frame_id: result.frame_id
})
});
const { access_token, expires_in, session_id } = await response.json();
window.session_id = session_id;
// Store expiry date
const expiryDate = new Date(Date.now() + expires_in * 1000);
Cookies.set(COOKIE_EXPIRY_NAME, expiryDate.toISOString(), { expires: 1 });
Cookies.set(COOKIE_NAME, session_id, { expires: 1 });
// Send authenticate command
const authenticateMessage = [
1,
{
cmd: 'AUTHENTICATE',
args: {
access_token: access_token
},
nonce: Math.floor(Math.random() * 1000),
transfer: undefined
}
];
getSourceWindow().postMessage(authenticateMessage, 'https://discord.com');
} catch (error) {
console.error('Error requesting token:', error);
}
}
// Handle message with authenticate command
if ('cmd' in evt.data[1] && evt.data[1].cmd === 'AUTHENTICATE') {
// Update authenticated page
if (window.session_id) {
const expiryDate = Cookies.get(COOKIE_EXPIRY_NAME);
if (expiryDate) {
const expiryDateObject = new Date(expiryDate);
if (expiryDateObject <= new Date()) {
// Expired, remove session
Cookies.remove(COOKIE_NAME);
Cookies.remove(COOKIE_EXPIRY_NAME);
} else {
// Update location with session ID
window.location = `{BASE_URI}?t=${Date.now()}&session=${window.session_id}`;
}
} else {
// Update location with session ID
window.location = `{BASE_URI}?t=${Date.now()}&session=${window.session_id}`;
}
}
}
});
Overview
This JavaScript code handles messages received from a parent window and performs the following actions:
Breakdown
expiry
: stores the expiration time of the access tokenresult
: stores the result of the page initializationinitNonce
: a random nonce used in the handshake (initialized with Math.round(Math.random() * 1000)
)The code listens for messages received from the parent window using the window.addEventListener
method.
evt.data[0] == 3 && typeof evt.data[1].frame_id!= 'undefined'
, it sends a handshake message to the parent window with the client ID, frame ID, and nonce.evt.data[0] == 1 && evt.data[1].cmd == 'DISPATCH'
, it starts the authorization chain by sending an AUTHORIZE
command to the parent window.evt.data[1].cmd == 'AUTHORIZE'
, it sends a POST request to the Discord API to exchange the authorization code for an access token.The code sends a POST request to the Discord API to fetch an access token, including the following:
code
: the authorization code received from the parent windowlaunch_id
: the launch ID from the page initialization resultframe_id
: the frame ID from the page initialization resultAfter obtaining the access token, it posts a message back to the parent window with the access token to authenticate, including:
cmd
: AUTHENTICATE
args
: { access_token: access_token }
nonce
: a random noncetransfer
: void 0