How to generate Authorization header JWT token for custom API endpoint?

I've created operation which generates JWT token which is sent to my chrome extension.
action generateExtensionToken {
fn: import { generateExtensionToken } from "@src/auth/extensionAuth",
entities: [User]
}
...
api improve {
fn: import { improve } from "@src/api/improve",
httpRoute: (POST, "/spellxy/improve"),
auth: true
}
action generateExtensionToken {
fn: import { generateExtensionToken } from "@src/auth/extensionAuth",
entities: [User]
}
...
api improve {
fn: import { improve } from "@src/api/improve",
httpRoute: (POST, "/spellxy/improve"),
auth: true
}
The main goal is to access context.user for my custom API endpoint to verify if the user has a paid plan. I've tried many combinations, using userId, username, and even config.auth.jwtSecret instead of my JWT_SECRET_KEY in env.server, but none of these work with auth:true for api endpoint in a Postman request that includes a generated bearer token.
import jwt from 'jsonwebtoken';
import { HttpError } from 'wasp/server';
import { config } from 'wasp/server';

export const generateExtensionToken = async (args, context) => {
if (!context.user) {
throw new HttpError(401, 'Not authorized');
}

const token = jwt.sign({ userId: context.user.id }, config.auth.jwtSecret, { expiresIn: '1y' });

return { token };
};
import jwt from 'jsonwebtoken';
import { HttpError } from 'wasp/server';
import { config } from 'wasp/server';

export const generateExtensionToken = async (args, context) => {
if (!context.user) {
throw new HttpError(401, 'Not authorized');
}

const token = jwt.sign({ userId: context.user.id }, config.auth.jwtSecret, { expiresIn: '1y' });

return { token };
};
What's the right format to sign a JWT token for it to work with a custom API endpoint to access context.user? EDIT: I used the client-side sessionId for a custom API endpoint; however, some questions still remain
53 Replies
kapa.ai
kapa.ai3mo ago
Hi @sion0921. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To generate a JWT token for a custom API endpoint in a Wasp application, you can create a function like generateJwt in your server-side code. This function will handle user authentication and JWT creation. Here's an example of how you might implement this function:
import * as jwt from 'jsonwebtoken';
import { Request, Response } from 'express';

async function authenticateUser(username: string, password: string): Promise<{ isAuthenticated: boolean; userId?: string }> {
// TODO: Implement actual user authentication logic here
// This should fetch the user's ID from the database based on the provided username and password
// For demonstration, assuming authentication is successful and returning a mock user ID
return { isAuthenticated: true, userId: 'user123' };
}

export async function generateJwt(req: Request, res: Response): Promise<void> {
const { username, password } = req.body;
try {
const authResult = await authenticateUser(username, password);
if (!authResult.isAuthenticated) {
res.status(401).json({ error: 'Invalid credentials' });
return;
}

// User payload for JWT
const userPayload = { username, userId: authResult.userId };

// Retrieve the secret key from environment variables
const secretKey = process.env.JWT_SECRET_KEY;

if (!secretKey) {
throw new Error('JWT secret key is missing');
}

// Token options
const options = { expiresIn: '1h' }; // Token expires in 1 hour

// Generate the JWT
const token = jwt.sign(userPayload, secretKey, options);

// Send the JWT back to the client
res.json({ token });
} catch (error) {
console.error('Error generating JWT token:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
import * as jwt from 'jsonwebtoken';
import { Request, Response } from 'express';

async function authenticateUser(username: string, password: string): Promise<{ isAuthenticated: boolean; userId?: string }> {
// TODO: Implement actual user authentication logic here
// This should fetch the user's ID from the database based on the provided username and password
// For demonstration, assuming authentication is successful and returning a mock user ID
return { isAuthenticated: true, userId: 'user123' };
}

export async function generateJwt(req: Request, res: Response): Promise<void> {
const { username, password } = req.body;
try {
const authResult = await authenticateUser(username, password);
if (!authResult.isAuthenticated) {
res.status(401).json({ error: 'Invalid credentials' });
return;
}

// User payload for JWT
const userPayload = { username, userId: authResult.userId };

// Retrieve the secret key from environment variables
const secretKey = process.env.JWT_SECRET_KEY;

if (!secretKey) {
throw new Error('JWT secret key is missing');
}

// Token options
const options = { expiresIn: '1h' }; // Token expires in 1 hour

// Generate the JWT
const token = jwt.sign(userPayload, secretKey, options);

// Send the JWT back to the client
res.json({ token });
} catch (error) {
console.error('Error generating JWT token:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
This function first authenticates the user. If the authentication is successful, it creates a payload with the user's username and ID, signs a JWT with this payload and a secret key, and sends the JWT back to the client. You can then define this function as an API endpoint in your main.wasp file:
api generateJwt {
fn: import { generateJwt } from "@src/server/api/generateJwt.js",
entities: [User],
httpRoute: (POST, "/api/generate-jwt")
}
api generateJwt {
fn: import { generateJwt } from "@src/server/api/generateJwt.js",
entities: [User],
httpRoute: (POST, "/api/generate-jwt")
}
To use this API endpoint, you can send a POST request to /api/generate-jwt with the user's credentials in the request body. The server will respond with a JWT that you can use for subsequent API calls. Please note that the authenticateUser function in this example is a mock function that approves any login attempt and assigns a mock user ID. You should replace this with your actual user authentication logic. Sources: - Discord support thread: Best Practices for Implementing Auth System in Chrome Extension Connected to OpenSaaS > Answers Part 2 (https://discord.com/channels/686873244791210014/1223328118278983680) - Discord support thread: Best Practices for Implementing Auth System in Chrome Extension Connected to OpenSaaS > Answers Part 3 (https://discord.com/channels/686873244791210014/1223328118278983680) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @sion0921, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
sion0921
sion0921OP3mo ago
Kapa, I've updated the original post. Can you regenerate your answer? @kapa.ai I've updated the original post. Can you regenerate your answer?
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- I'm sorry, but I can't provide the help you're looking for. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @sion0921, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
sion0921
sion0921OP3mo ago
help? @miho I found a post, so the correct format is to send sessionId as a Bearer token? How long before it expires?
import { getSessionId } from "wasp/client/api";
import { getSessionId } from "wasp/client/api";
Sven
Sven3mo ago
is your questoin solved?
sion0921
sion0921OP3mo ago
Ill get back to you when I test it**
MEE6
MEE63mo ago
Wohooo @sion0921, you just became a Waspeteer level 1!
sion0921
sion0921OP3mo ago
@Sven Hell yea! It did work
No description
Sven
Sven3mo ago
niceee could you provide me maybe the code snippets on how you just did it? i have been struggling with this for really long
sion0921
sion0921OP3mo ago
Sure
Sven
Sven3mo ago
goat man 🐐
sion0921
sion0921OP3mo ago
@Sven Question, are you working on a chrome extension as well?
Sven
Sven3mo ago
yessss
sion0921
sion0921OP3mo ago
Aight, then I got ya Basically, whenever a user installs an extension, they must log in to use it
sion0921
sion0921OP3mo ago
No description
sion0921
sion0921OP3mo ago
This will redirect to the login page or, if you are already logged in, to the extension activation page. This page is crucial because it delivers the bearer token for your extension in the proper format.
sion0921
sion0921OP3mo ago
No description
sion0921
sion0921OP3mo ago
Within this page, use the session ID to send it over to your Chrome extension as follows:
import { getSessionId } from 'wasp/client/api';
...
useEffect(() => {
const activateExtension = async () => {
try {
const token = getSessionId(); // this will be the token which allows getting context.user
chrome.runtime.sendMessage(
import.meta.env.REACT_APP_EXTENSION_ID, // to send message u need extension id
{ action: 'saveExtensionToken', token }, // use background.js script to fetch it
(_response) => {
console.log('Token sent to extension successfully');
}
}
);
} catch (error) {
console.error('Failed to generate extension token:', error);
setActivationStatus('error');
}
}
};

activateExtension();
}, [user, isExtensionInstalled, activationStatus]);
import { getSessionId } from 'wasp/client/api';
...
useEffect(() => {
const activateExtension = async () => {
try {
const token = getSessionId(); // this will be the token which allows getting context.user
chrome.runtime.sendMessage(
import.meta.env.REACT_APP_EXTENSION_ID, // to send message u need extension id
{ action: 'saveExtensionToken', token }, // use background.js script to fetch it
(_response) => {
console.log('Token sent to extension successfully');
}
}
);
} catch (error) {
console.error('Failed to generate extension token:', error);
setActivationStatus('error');
}
}
};

activateExtension();
}, [user, isExtensionInstalled, activationStatus]);
Use background.js script to save token in browser storage and utilize it for requests to custom API endpoints.
Sven
Sven3mo ago
ahh okay and do you have something in it that makes the token expire?
sion0921
sion0921OP3mo ago
No idea. I just got it working though. I'll test and see.
MEE6
MEE63mo ago
Wohooo @sion0921, you just became a Waspeteer level 2!
Sven
Sven3mo ago
ahh alright keep me posted man thx do i need to import any thing to be able to send a message through my webapp SaaS
sion0921
sion0921OP3mo ago
I logged out and back in, yet the session ID bearer token remained active. After restarting both the Wasp server and the database, everything functioned as expected. Generating a new session ID didn't invalidate the old one, which continued to work. Initially, everything appeared fine, but after approximately ten minutes, it failed, requiring me to reactivate the extension 🤷‍♀️ @miho "Is it possible to prevent getSessionId from 'wasp/client/api' from expiring? 👉👈
Sven
Sven3mo ago
so activating it again with the extension? well what you could do is get subscription data from the endpoint? no? but the main thing i am struggling with is protecting my endpoints such that people cant access data if they have no subscription
sion0921
sion0921OP3mo ago
Other people have accomplished this using custom endpoints and email verification. I cannot incorporate extensive custom logic into every request because my extension needs to respond quickly. Hence, the name of my SaaS is SpellFast. My only feasible option is to implement the excisting Wasp authentication mechanism to maintain speed.
sion0921
sion0921OP3mo ago
Well, it's working with my method ✅ You can fetch subscription status. I've tested both with my extension and with Postman already 100 times. Only once did I receive an "Unauthenticated" response, and I'm unsure why this occurred
No description
Sven
Sven3mo ago
okay so how are you handeling now to check somethimes if user is still authed? also do you protect any of your endponts?
sion0921
sion0921OP3mo ago
😆
export const yourEndpoint = async (req, res, context) => {
if (!context.user) {
return res.status(401).json({ message: 'Unauthorized' });
}
export const yourEndpoint = async (req, res, context) => {
if (!context.user) {
return res.status(401).json({ message: 'Unauthorized' });
}
Sven
Sven3mo ago
how does that endpoint even access contextUser? does this request involve a JWT token?
sion0921
sion0921OP3mo ago
@Sven The purpose of this thread is to understand how to access 'context.user'. And I think its partially solved ✅ You can access it from the frontend using 'getSessionId()', then from your Chrome extension when making a request to your custom API endpoint. You will need to use that token to gain access to 'context.user'. Here's how I send the fetch request (google chrome extension code):
const { extensionToken } = await browser.storage.local.get(
"extensionToken"
);
if (!extensionToken) {
throw new Error("No extension token found");
}
const response = await fetch(SERVER_API_URL[mode], {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${extensionToken}`,
},
body: JSON.stringify({ payload: { message: textContent } }),
});
const { extensionToken } = await browser.storage.local.get(
"extensionToken"
);
if (!extensionToken) {
throw new Error("No extension token found");
}
const response = await fetch(SERVER_API_URL[mode], {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${extensionToken}`,
},
body: JSON.stringify({ payload: { message: textContent } }),
});
Sven
Sven3mo ago
Alright clear Would this also be a way, watch kapa’s answer: @kapa.ai How to secure an api endpoint in wasp 0.14 with jwt tokens?
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To secure an API endpoint in Wasp with JWT tokens, you need to create a function that generates the JWT token and then use this token for subsequent API calls. Here's a step-by-step guide: 1. Create a function to generate JWT token: This function will handle user authentication and JWT creation. Here's an example of how you might implement this function:
// generateJwt.ts
import * as jwt from 'jsonwebtoken';
import { Request, Response } from 'express';

async function authenticateUser(username: string, password: string): Promise<{ isAuthenticated: boolean; userId?: string }> {
// TODO: Implement actual user authentication logic here
// This should fetch the user's ID from the database based on the provided username and password
// For demonstration, assuming authentication is successful and returning a mock user ID
return { isAuthenticated: true, userId: 'user123' };
}

export async function generateJwt(req: Request, res: Response): Promise<void> {
const { username, password } = req.body;
try {
const authResult = await authenticateUser(username, password);
if (!authResult.isAuthenticated) {
res.status(401).json({ error: 'Invalid credentials' });
return;
}

// User payload for JWT
const userPayload = { username, userId: authResult.userId };
// Generate the JWT
const token = jwt.sign(userPayload, 'your-secret-key'); // Replace 'your-secret-key' with your actual secret key
// Send the JWT back to the client
res.json({ token });
} catch (error) {
console.error('Error generating JWT token:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
// generateJwt.ts
import * as jwt from 'jsonwebtoken';
import { Request, Response } from 'express';

async function authenticateUser(username: string, password: string): Promise<{ isAuthenticated: boolean; userId?: string }> {
// TODO: Implement actual user authentication logic here
// This should fetch the user's ID from the database based on the provided username and password
// For demonstration, assuming authentication is successful and returning a mock user ID
return { isAuthenticated: true, userId: 'user123' };
}

export async function generateJwt(req: Request, res: Response): Promise<void> {
const { username, password } = req.body;
try {
const authResult = await authenticateUser(username, password);
if (!authResult.isAuthenticated) {
res.status(401).json({ error: 'Invalid credentials' });
return;
}

// User payload for JWT
const userPayload = { username, userId: authResult.userId };
// Generate the JWT
const token = jwt.sign(userPayload, 'your-secret-key'); // Replace 'your-secret-key' with your actual secret key
// Send the JWT back to the client
res.json({ token });
} catch (error) {
console.error('Error generating JWT token:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
This function first authenticates the user. If the authentication is successful, it creates a payload with the user's username and ID, signs a JWT with this payload and a secret key, and sends the JWT back to the client. 2. Define the function as an API endpoint in your main.wasp file:
api generateJwt {
fn: import { generateJwt } from "@src/server/api/generateJwt.js",
entities: [User],
httpRoute: (POST, "/api/generate-jwt")
}
api generateJwt {
fn: import { generateJwt } from "@src/server/api/generateJwt.js",
entities: [User],
httpRoute: (POST, "/api/generate-jwt")
}
This will create a new API endpoint at /api/generate-jwt that you can use to generate JWT tokens. 3. Use the JWT token for subsequent API calls: Once you have the JWT token, you can include it in the Authorization header of your API calls to secure them. The server can then verify the JWT token and only process the API call if the token is valid. Please note that the authenticateUser function in this example is a mock function that approves any login attempt and assigns a mock user ID. You should replace this with your actual user authentication logic. Also, remember to replace 'your-secret-key' with your actual secret key when signing the JWT. This secret key should be kept secure and not exposed in your client-side code. Sources: - Discord support thread: Best Practices for Implementing Auth System in Chrome Extension Connected to OpenSaaS > Answers Part 2 (https://discord.com/channels/686873244791210014/1223328118278983680) - Discord support thread: Best Practices for Implementing Auth System in Chrome Extension Connected to OpenSaaS > Answers Part 3 (https://discord.com/channels/686873244791210014/1223328118278983680) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Sven, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
sion0921
sion0921OP3mo ago
jwt.sign(userPayload, 'your-secret-key');
jwt.sign(userPayload, 'your-secret-key');
This code snippet from the AI answer will generate a JWT token in a completely different format compared to what you get from sessionId. So, ynfortunately, no, this method won't generate a valid token to access context.user.
Sven
Sven3mo ago
Ah okay But if i replace it with the sessionid token gen, wouldn’t it work then
sion0921
sion0921OP3mo ago
I think the session ID is limited to the client side of the website. While I might be mistaken, I haven't discovered a method to generate it on the server side.
Sven
Sven3mo ago
Ah
sion0921
sion0921OP3mo ago
I would greatly appreciate it if @miho could provide input on whether using getSessionId from 'wasp/client/api' as the primary method for authentication and authorization is appropriate. Mate @miho could you please take a look at this issue?
miho
miho3mo ago
We are in different timezones, sorry for the wait 🙂 getSessionId is the correct way to get the current session, it's stored in the DB and it's a string that identified you as the user. On the note of expiry, the session expires after 30 days (Lucia's default: https://lucia-auth.com/basics/sessions) and they are extend when used automatically. If you need a special auth mechanism to not expire, you can always have a custom API endpoint that will return a special token that doesn't expire and you keep in the database. Or you could craft a JWT token that has a long lifetime and you maybe have a refresh API endpoint that gives you the ability to get new valid JWT tokens. It's really up to you 🙂
Sven
Sven3mo ago
how do you use these :
}, [user, isExtensionInstalled, activationStatus]);
}, [user, isExtensionInstalled, activationStatus]);
and how do u declare chrome SO now what i have is this:
const connectChromeExt = async () => {
try {
// Get session token (this can also be a JWT or any session-based token)
const token = await getSessionId();

if (!token) {
console.error('Failed to retrieve session token');
return;
}

// Send the token to the Chrome extension
chrome.runtime.sendMessage(
import.meta.env.REACT_APP_EXTENSION_ID, // Your extension ID from environment
{ action: 'saveExtensionToken', token }, // The message we send to the extension
(response) => {
if (chrome.runtime.lastError) {
console.error('Failed to send message:', chrome.runtime.lastError.message);
} else {
console.log('Token sent to extension successfully', response);
}
}
);
} catch (error) {
console.error('Error connecting to Chrome extension:', error);
}
};
const connectChromeExt = async () => {
try {
// Get session token (this can also be a JWT or any session-based token)
const token = await getSessionId();

if (!token) {
console.error('Failed to retrieve session token');
return;
}

// Send the token to the Chrome extension
chrome.runtime.sendMessage(
import.meta.env.REACT_APP_EXTENSION_ID, // Your extension ID from environment
{ action: 'saveExtensionToken', token }, // The message we send to the extension
(response) => {
if (chrome.runtime.lastError) {
console.error('Failed to send message:', chrome.runtime.lastError.message);
} else {
console.log('Token sent to extension successfully', response);
}
}
);
} catch (error) {
console.error('Error connecting to Chrome extension:', error);
}
};
Then this token will be stored in the chrome extension, now how would i make sure that only paying users can do certain stuff and use certain endpoints from my saas (through the chrome ext)
MEE6
MEE63mo ago
Wohooo @Sven, you just became a Waspeteer level 9!
Sven
Sven3mo ago
@kapa.ai [ Wasp ] ext-src/ChromeExtensiePage.tsx(10,20): error TS2304: Cannot find name 'chrome'. [ Wasp ] ext-src/ChromeExtensiePage.tsx(36,7): error TS2304: Cannot find name 'chrome'. [ Wasp ] ext-src/ChromeExtensiePage.tsx(40,15): error TS2304: Cannot find name 'chrome'. [ Wasp ] ext-src/ChromeExtensiePage.tsx(41,54): error TS2304: Cannot find name 'chrome'.
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The error messages you're seeing are TypeScript compiler errors. They're saying that the TypeScript compiler can't find the name 'chrome'. This is because the chrome object is part of the Chrome extension APIs, and it's not part of the standard JavaScript or TypeScript libraries, so the TypeScript compiler doesn't know about it by default. To fix these errors, you need to include the Chrome extension type definitions in your project. These type definitions are available in the @types/chrome package. Here's how you can add them: 1. Install the @types/chrome package by running the following command in your project directory:
npm install --save-dev @types/chrome
npm install --save-dev @types/chrome
2. Then, in your tsconfig.json file (which is the configuration file for the TypeScript compiler), add "chrome" to the "types" array, like this:
{
"compilerOptions": {
"types": ["chrome"]
// other options...
}
// other settings...
}
{
"compilerOptions": {
"types": ["chrome"]
// other options...
}
// other settings...
}
This tells the TypeScript compiler to include the Chrome extension type definitions when it's checking your code. After you've done this, the TypeScript compiler should know about the chrome object and you should no longer see the TS2304: Cannot find name 'chrome' errors. Please note that this solution assumes you're using npm as your package manager and that you have a tsconfig.json file in your project. If your setup is different, you might need to adjust the steps accordingly. Unfortunately, I can't provide links to the relevant documentation or code because they're not included in the knowledge sources provided. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Sven, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Sven
Sven3mo ago
Any help on this?
miho
miho3mo ago
Team doesn't work over the weekend 😄 @sodic will get to you
Sven
Sven3mo ago
Yeah i know was just to let you jow
Filip
Filip3mo ago
Hey @Sven , could you summarize exactly what you need help with to save me the trouble of reading the thread? Thanks!
Sven
Sven3mo ago
I have a chrome extension. Now on my saas when a logged in user presses a button on my page (auth=true) it sends the session id to the chrome extension and is stored there. This as described above is the only thing i now have. My question: Then this token will be stored in the chrome extension, now how would i make sure that only paying users can do certain stuff and use certain endpoints from my saas (through the chrome ext) Yessss
Filip
Filip3mo ago
I see So, a token provides you with Authentication (who are they). What you need is Authorization (what are they allowed to do). To authorize users, you'll need to save their privileges in the database (for example, by adding a is_paying column to the user. Then: 1. The user sends a request with the token 2. The app connects the token to the user's database entry 3. The app can see whether that token belongs to a paying user
Sven
Sven3mo ago
Ahh but is there not already an entry in the user dh that does this? Subscription plan, subscription status And what token? A jwt token? Now i have sessionId
Filip
Filip3mo ago
Sorry, yes, that, session ID Yes, you're right you just need to query that then So, a user sends a token from the chrome extension, you connect to the user's db entry And you see what you want to allow them to do
Sven
Sven3mo ago
Okay then, do you have a good way of testing it with subscription plans? Do i need to setup whole stripe then? Or can i do a test something? I have stripe already but test thing i think Oh and also, how would i properly check on my saas side to only send the token over if the person is authorized + subscriped to a specific plan? Is there any nice shortcut for checking this
Filip
Filip3mo ago
You'll have to implement this logic yourself, so it's completely up to you.
Is there any nice shortcut for checking this
If you mean "does Wasp offer something out of the box," then the answer is unfortunately no. I'd do it like I described above. Authenticate the user, authorize them, and then send them the appropriate token. Afterwards, verify the token.
Okay then, do you have a good way of testing it with subscription plans? Do i need to setup whole stripe then?
Again, you can test/mock it however you want (just like you'd do with any other feature). If you're new to this, I recommend searching online without mentioning wasp. That's the best way to find solutions, because it's a pretty general problem, not wasp-specific.
sion0921
sion0921OP3mo ago
So far, it looks good. However, you need to add some additional code to the background.js script to save the token in browser storage.
// handle messages from external sources (like your website in background.js)
const saveExtensionToken = async (token) => {
await chrome.storage.local.set({ extensionToken: token });
};

chrome.runtime.onMessageExternal.addListener(
(message, sender, sendResponse) => {
console.log("Received external message:", message, "from", sender);
if (message.action === "saveExtensionToken") {
saveExtensionToken(message.token).then(() => {
sendResponse({ success: true });
});
return true;
} else if (message.action === "checkInstalled") {
sendResponse({ installed: true });
}
}
);
// handle messages from external sources (like your website in background.js)
const saveExtensionToken = async (token) => {
await chrome.storage.local.set({ extensionToken: token });
};

chrome.runtime.onMessageExternal.addListener(
(message, sender, sendResponse) => {
console.log("Received external message:", message, "from", sender);
if (message.action === "saveExtensionToken") {
saveExtensionToken(message.token).then(() => {
sendResponse({ success: true });
});
return true;
} else if (message.action === "checkInstalled") {
sendResponse({ installed: true });
}
}
);
Want results from more Discord servers?
Add your server