Protect routes? For the app itself, no user auth, pages router. Help, desperate.
Hi,
I'm a noob and I've been winging it with Chat GPT
My app is ready, Im using next js 13, now I came to the challenge of protecting the routes, specially because I have routes to create / delete / update.
This has become a problem.
My app has interactions, that call the routes, I might have messed up the architecture, for instance:
This is a button
Then, depending on the action, it will call a route like this
```try {
setLockedRaffle(true);
//await new Promise((r) => setTimeout(r, 1000)); // wait for 1 second
const response = await fetch("/api/updateTicket", {
// replace '/api/update-route' with your route
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization:
Bearer ${process.env.TOKEN}`,
},
body: JSON.stringify({
address: owner,
raffle: raffleID,
updateType: "FROM_NULL",
}),
});````
There is no user authentication, it's the App that decides what and where to update, obvious problem is that I don't want someone else calling these endpoints,
I've now realised that Bearer is undefined, probably because this is being rendered client side?
So how do I solve this? I can't find a solution, every tutorial that I've came across relies on authentication from the user, this is not what I want, the user should never be authorised to call these endpoints, it's the app that decided what to call depending on whats happening, such as buttons being pushed,
Albeit probably subpart and dumb, my "architecture" works as I intended, the only issue is, how do I protect the routes, I'm able to protect them by checking the "origin" but this can be spoofed, I need some sort of token where the route checks for the token and the token is passed on the headers of the interaction that fetches the data, the problem is, I assume, that if the component is client side, then the client would be able to access this token, this is obviously not what I want.
So how do I make a server side component on next js 13, that has buttons on the client side?! I hope this makes sense, I'm desperate with this.
I know how stupid this sounds, my knowledge has gaps, please understand, I'm working on this. Thank you!63 Replies
Trying to make a better explanation:
Gm gm, I'm new here, this was the only place I could think of coming since I'm trying to get into dev thanks to Theo channel. I built my app and now I'm facing an issue that will probably cost me a lot of dev hours.
I can't protect my routes. I don't have user auth, I just need the app to do some crud operations, I created routes for it, but now I can't prevent others from calling these endpoints because, it seems to me, that since these routes are being fetched client side, there's no way to setup some basic auth token for the fetch, since it would be displayed on client side itself. (figured this out cause my bearer token was undefined and I realised that client can't access .env files, which makes total sense)
So I've exhausted my VERY LIMITED noob knowledge of next js / ts / react, I've come to realize probably my solution exists on server side compoenents? But I'm, super confused since the interaction that originates the fetch (ex, press a button) happens on client side.
For the love of God, can someone point me to a simple solution? I'm searching for tutorials but I can only find route protection via next-auth, this is not what I want I dont have user authentication, I just want to protect the app endpoints to only be usable, well, by the app itself.
Maybe I completely missed the point of next js and this is the most stupid question ever 😦
(figured this out cause my bearer token was undefined and I realised that client can't access .env files, which makes total sense)Yes it can. But since you're using Next, you need to have
NEXT_PUBLIC_
prefix on your variables
But you should never authenticate on the client anywaysgotcha! but that would expose the "bearer" token right?
The backend should be the one doing the check
thankfully my "bug" lead me to that conclusion
but I cant find the proper solution now
Yes
if I want to do an API call, whenever someone presses a button, but I want to protect that route, how do I do that?
this is in a nutshell what Im trying to achieve
But I have a question, why do you have endpoints that shouldn't be used by any users?
If I understood correctly, you have endpoints that perform operations that no one else other than you should, right?
like Delete / Create
yes, precisely, I dont want someone calling the endpoint and creating entries in the database, or deleting them
There are many solutions for that
you have buttons on the UI that perform those actions (for you)?
yes I do
like a user clicks the button, then something should happen and updated in the database, right now it works, but anyone who knows the endpoint would be able to do the same, I want to make sure its the "app" thats the one authorized to do it
Honestly, even if you didn't think of that before, there's no big deal in the architecture here
The "correct" way to do it would be to add a field "role" in your User table, which can have the values
"ADMIN" or "USER"
, set yourself as ADMIN and then, on the backend, check if the role is ADMIN, if it isn't, throw an error
the "easy" but wrong way would be to just check if the id of the user === your idit's not an admin access thing, there should be no user, just the app running logic and deciding on what to do by itself
like if it was a node app
well, I probably cant explain myself properly, just going back to youtube hell trying to find a simple crud example with protected routes that don't rely on user authentication
there's no user at all?
there's no users on the database, just references to users
What does that mean
there is ownership, such as a document belongs to an user, because it references the user
but its not user based, there's no authentication, thats why the app itself needs to be the only one able to make changes to the database
otherwise anyone would come in and add themselves as owners of certain documents, or delete documents that belong to someone else
its the server side logic that does some comparisons and decides on what to change, the chain of events is UI action > server side logic > determines which endpoint to post to
so how do I protect that endpoint so that it is only fetched from a direct UI action, such as pressing a button
That's impossible
seems Im missing some server side component that does the auth
If an endpoint exist, it's always accessible through an API injection of any kind
That's why the backend needs to validate or invalidate the request
yes I realize that, something as simple as cors solves this, but that can be spoofed, I need to find a way to do it by passing a bearer token to it
the problem is that the fetch is being done in client side
I guess this is what people call middleware for authentication?
cause the middleware will be server side and do the token auth?
and then the children of the middleware, are client side
and I guess middleware is like context and has the fetch functions and the client just calls those via upstate / props?
I hope this makes sense
But a bearer token would mean you have a way to authenticate the client
im not fully aware of what im saying, just trying to piece it together
it can be just a hardcoded token
the same to all
wait I need to understand something
If there's no users
How do you differentiate yourself (admin) from others?
Like, how do you know if you should render the delete button or not?
there's a simple mechanism, I use some other SDK for web3 wallet login, and then I use that wallet address as the unique reference, so when I call the endpoints I pass that wallet address, the problem is, if you know the endpoint and its not protected, you would be able to pass whatever wallet address you want to modify
so I need to make sure the endpoints are being called from within the app itself
I don't understand what you mean by that. You said you have buttons to do it, so doesn't it mean you are the one who calls it?
its user agnostic, I dont save wallet addresses
you only see the buttons if you have connected your wallet address
the app works, I just need to protect the routes
I think you're confused in some part of that
No, you can't protect the routes if you want to expose them.
Only through authentication
I want to authorize my server to call the routes
how do I authenticate "my app"
Why not just call them as functions?
ok, no endpoints then, just functions that interact with the db
exactly
I'm going to try that
thank you!
actually, would still have the same problem, the functions would be client side
and exposed
They shouldn't be
If you have these functions on the server, they should not be in the client
yes but the client needs to call them
for instance, after pressing a button
But you said your app is the one that calls these functions
based on client input
this is very confusing hahaha
hahaha
yeah, I need to learn more about server side stuff on next, I know nothing of it
im making it more confusing then it needs to be
you probably are
usecase, 1. user logs in with wallet, 2. user clicks delete, 3. fetches route sends wallet address, db finds a document to delete with that wallet address
I have this working rn
problem is, if I know the endpoint, I could call it and pass whatever address
Ahhh
(like I do with insomnia with test it)
Then I think it's easier than it seems
If the user can only do something with its own wallet
thats right, but the enpoint is wallet agnostic
if one knows the endpoint you can pass any wallet
thats why I only want "my app" to be able to call that enpoint
You cannot have it both ways
Because, like you said yourself, even if you have this function "hidden", the user could manipulate the input to perform the action he wants
I dont understand, this is very confusing, seems like it would be a basic thing, not wanting direct endpoint access that does not originate from a server call
That is easy yeah
not for me lol 😛
But that's not the problem, it seems
like, you are saying that "based on the input, certain action will be performed"
So the input is what controls it
and the input can be manipulated
so I need middleware
Not really
You need an authentication
You need to know that X is X to delete X
but I know that
the app is fully functional right now
when you "log in with wallet", the API should send you a token to authenticate that user, or something like that
thats what I was trying to avoid
I guess I cannot
it's not a big deal
If it sends you a token, you don't have to save the wallet
because you'd decode the token and it should have the wallet id in it
yeah I get it now
and then you check against the wallet in the db
its the provider stuff, you wrap everything in a component that has the token
this is how next auth works
so I create my wrapper, which is context, and that context is where the token resides
the clients has no access to it
but the app does
I need to learn the context hook
and I would use that wrapper as the authentication, to make sure the endpoint fetches originate from inside the app
I think you need to understand the authentication flow
I do, but Im grasping at what it kinda does
Im going to look into this further
I'll write a minimal version of what a flow would look like, if you implemented it all yourself
so you understand kind of how it should work
1. create user
2. user logins in on the front-end by sending its info to the backend
3. the backend checks the login and the password against the db. if everything is correct, it generates a JWT and sends to the client
4. the client holds the JWT and sends it within every request (the
Bearer <token>
)
5. the backend decodes this JWT. It'll never work with an altered token because it was generated by the backend and the algorithm that generates it is unkwon to the client
6. if it authenticates the jwt token, then the request is successful, if not, throw an error
that's a very basic flow
But since you use the wallet to authenticate, I believe there's an API that generates the JWT token for you, and this API has a token to decode the jwt. I could be wrong tho, it's a guessOk, thank you! Appreciate this, I'm still going to try to do it without user authentication, let's see, but if it does not work I will create this flow you suggest, appreciated bro