Call custom api externally
I created a custom API that sends data to a database, and I want to call it from another application. I used fetch() to post the data, and got a 401 (unauthorized) as expected. I know I need to send a token or something in the header. How do I do that?
17 Replies
Hi @Kynetix. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
To call a custom API in Wasp from an external application, you need to handle authentication properly. The 401 error you're receiving indicates that your API endpoint is protected and requires authentication.
Adding Authentication to External API Calls
When you make a request to a Wasp API endpoint that has
auth: true
set, you need to include a valid JWT token in the Authorization header. Here's how to do it:
1. First, make sure your API is properly configured in your main.wasp
file:
2. To call this API externally, you need to include the JWT token in your fetch request:
Getting a JWT Token
You have a few options to get a valid JWT token:
1. Create a login endpoint: You can create a custom API endpoint that authenticates users and returns a JWT token. This is similar to what's shown in the Discord thread where someone implemented a generateJwt
endpoint in this Discord thread.
2. Use an existing auth method: If you're using one of Wasp's built-in auth methods (username/password, email, Google, GitHub), you can have users authenticate through those flows first to get a token.
Alternative: Make the API Public
If you don't need authentication for this specific endpoint, you can set auth: false
in your API declaration:
This would allow you to call the API without authentication, but be careful with this approach if your API performs sensitive operations or accesses protected data.
For more information about custom HTTP API endpoints in Wasp, check out the Custom HTTP API Endpoints documentation.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @Kynetix, 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?@kapa.ai I'm using the built-in authentication. How can I retrieve the token after the user logs in?
Wohooo @Kynetix, you just became a Waspeteer level 4!
@kapa.ai I'm using the built-in authentication. How can I retrieve the token after the user logs in?
Let me check the knowledge base for you again. One moment! :books:
----------------------
If you're using Wasp's built-in authentication, you should know that Wasp has moved from using JWT tokens to using sessions for authentication. This is an important distinction that affects how you would access authentication information.
Current Authentication System in Wasp
Wasp now uses sessions rather than JWT tokens for authentication:
Wasp's auth uses sessions to keep track of the logged-in user. The session is stored in localStorage
on the client and in the database on the server. Under the hood, Wasp uses the excellent Lucia Auth v3 library for session management.
Auth Overview - Sessions
This means that you don't directly access a token as you might expect. Instead, Wasp handles the session management for you behind the scenes.
Accessing the Authenticated User
To access the authenticated user in your client code, you can use the useAuth
hook:
For External Applications
If you're trying to access the authentication from an external application (like your previous question suggested), this becomes more complex because Wasp doesn't currently expose a direct API for authenticating users from external applications.
As noted in a Discord conversation:
Unfortunately, Wasp doesn't yet expose the API for authenticating users (the idea is to use its internal RPC, which doesn't make sense for your use case).Discord support thread For external applications, you might need to create a custom API endpoint that generates a JWT token specifically for external use, similar to what was discussed in another Discord thread about Chrome extensions. If you absolutely need to implement this for an external application, you would need to create a custom API endpoint that generates a JWT token based on the authenticated user's session. Would you like more information about any specific aspect of authentication in Wasp? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Kynetix, 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?
@kapa.ai please show me the implementation steps and an example custom API endpointsnfor generating JWT tokens
Let me check the knowledge base for you again. One moment! :books:
----------------------
I'll show you how to implement a custom API endpoint for generating JWT tokens in Wasp. This approach is useful when you need to provide authentication for external applications.
Implementation Steps
1. Declare the API in your main.wasp file
First, you need to declare your custom API endpoint in your main.wasp file:
2. Implement the API function
Next, create the implementation file for your API endpoint:
- JavaScript
- TypeScript
3. Set up your environment variables
Make sure to set up your JWT secret key in your environment variables. You can add it to your
.env.server
file:
4. Using the JWT token in external applications
Once you have your JWT token, you can use it in external applications by including it in the Authorization header:
Testing Your API
You can test your API using curl or Postman:
This implementation provides a basic JWT generation endpoint. Remember to replace the mock authentication function with your actual authentication logic that verifies credentials against your database.
Custom HTTP API Endpoints documentation provides more details on how to work with custom APIs in Wasp.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @Kynetix, 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?Hey @Kynetix, how are are you using wasp with external API? Public API rework is something that is going to be done in future, so feedback can help us with shaping new API.
As for your specific question, @miho knows the most about our auth possibilities.
hi @franjo , I embedded a Flowise chatbot that queries the user, and the answers are http-POSTed to the external API (which saves the data to a Supabase db).
The alternative would've been to call Supabase Edge functions, but I'm not too familiar with them (especially in terms of security)
@kapa.ai I've written the following for creating a token:
// BEGIN function
export const createToken: CreateToken<{}, { status: string; data: string }> = (req, res, context) => {
if (!context.user) {
throw new HttpError(401);
}
try {
// Create a JWT token with user id and email, expires in 1 hour
const payload = {
id: context.user.id,
email: context.user.email,
username: context.user.username,
};
const secret = process.env.JWT_SECRET || 'zigozi_default_secret';
const token = jwt.sign(payload, secret, { expiresIn: '6h' });
res.json({ status: 'success', data:token });
} catch (error: unknown) {
let errorMessage = 'Error creating token';
if (error instanceof Error) {
errorMessage = error.message;
}
res.status(500).json({ status: 'error', data: errorMessage });
}
};
// END createToken
and the generated token, I used it in the header of a fetch() call to the api like this
// begin HEADER
{
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + $vars.jwtToken
},
// end HEADER
But it responds with
{
"message": "Invalid credentials",
"data": {}
}
Why is it saying "invalid credentials"? How can I rectify this?
Let me check the knowledge base for you again. One moment! :books:
----------------------
I can see you're having an issue with your JWT token authentication in your Wasp application. The "Invalid credentials" error suggests there's a problem with how the token is being validated on the server side.
Possible Causes and Solutions
1. JWT Secret Mismatch:
The most common cause of this issue is that the secret used to sign the token is different from the one used to verify it.
Make sure your
JWT_SECRET
environment variable is properly set and consistent across your application. Wasp reads this from the environment, and if it's not set or is different between token creation and verification, you'll get this error.
2. Token Verification Process:
In Wasp, when you set auth: true
on an API endpoint, it expects the token to be in a specific format that matches its internal authentication system. Since Wasp has moved from JWT tokens to sessions for its built-in authentication, your custom JWT might not be compatible with Wasp's auth middleware.
From the Discord support thread, we can see that custom JWT implementations need to be handled carefully.
3. API Port Issue:
Make sure you're calling the correct port for your API. As mentioned in the Discord support thread, Wasp runs the server on port 3001, not 3000:
Recommended Fixes
1. Check your JWT_SECRET environment variable:
Make sure it's properly set in your .env.server
file and that you're not relying on the default fallback value.
2. Implement a custom token verification middleware:
Instead of relying on Wasp's built-in auth middleware, you might need to create your own API endpoint that doesn't use auth: true
but instead manually verifies the JWT token:
3. Check token format:
Make sure your token is being sent correctly in the Authorization header. The format should be exactly:
(with no extra spaces or characters)
4. Debug the token verification:
Add some logging to see what's happening during token verification:
These steps should help you identify and fix the issue with your JWT token authentication.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @Kynetix, 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?@kapa.ai Since wasp has moved from JWT to sessions, how can I implement this session for external API calling? How can I convert this JWT approach I was implementing to a session based approach? Or do you have a better suggestion?
Let me check the knowledge base for you again. One moment! :books:
----------------------
I understand you're looking to adapt your authentication approach for external API calls given Wasp's move from JWT to sessions. Let me explain how you can work with Wasp's current session-based authentication system.
Understanding Wasp's Current Auth System
Wasp now uses sessions for authentication instead of JWTs:
Wasp's auth uses sessions to keep track of the logged-in user. The session is stored in localStorage
on the client and in the database on the server. Under the hood, Wasp uses the excellent Lucia Auth v3 library for session management.
Auth Overview - Sessions
Options for External API Authentication
You have a few options for handling authentication from external applications:
Option 1: Continue Using Custom JWT (Recommended)
Even though Wasp internally uses sessions, you can still implement your custom JWT solution for external API access. This is actually a common pattern - using sessions for your web app and JWT for API access.
Your current JWT implementation is a good approach for external API authentication. The issue you're facing is likely related to how the token is being validated rather than the approach itself.
Option 2: Create a Session-Based API Key System
If you want to align with Wasp's session approach:
1. Create an API endpoint that generates a long-lived API key for external applications
2. Store this key in your database associated with the user
3. Have external applications send this key in headers
4. Validate the key on your protected endpoints
Option 3: OAuth-style Flow
Implement an OAuth-style flow where:
1. External app redirects to your Wasp app for authentication
2. After login, redirect back with a temporary code
3. Exchange this code for an access token via a secure backend channel
Implementation Recommendation
Based on the knowledge sources and your needs, I recommend sticking with your JWT approach but fixing the validation issues:
1. Create a separate middleware for JWT validation that doesn't rely on Wasp's built-in auth:
2. Use this middleware in your API endpoints:
3. In your main.wasp file, set auth: false
for these endpoints since you're handling auth manually:
This approach gives you full control over the authentication process for external applications while still allowing Wasp to use its session-based auth for the web application.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @Kynetix, 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?So you're sending information from wasp application to external API.
Are you sure that the token you need to send is not defined in that external services settings somewhere? If so, you could include it in your project via environment variables and just include it in fetch header.
@franjo the chatbot embedded in Wasp is kinda sandboxed - it can't access the applications variables & stuff, (except via some vars defined within). So it's as good as sending the data from a different application.
Basically, I generate the token in Wasp and pass it to the chatbot, then when the chatbot calls the Wasp API, it passes the token (Authorization: Bearer XXXXXX) in the header.
In this situation, I would recommend setting up a dedicated access token specifically for the chatbot.
You can pre-generate a random token and inject it into your Wasp application via environment variables.
Then, you would expose a custom API route with
auth: false
, since we’ll handle authentication manually inside the route handler. The idea is to check whether the bearer token from the incoming request matches the token stored in your environment variables.
Here’s a quick, minimal working example:
.env
main.wasp
src/apis/chatbots.ts
This is a minimal example that uses a basic string comparison for the token check.
Not really production grade quality, but it should help you get started.
Of course instead of returning Chatbot handshake successful
you would actually do what you want in here. Hope it helps. 😁Thanks, that works great man!
Great. Glad we got it.