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
onClick={() => {
//updateTicket("free", id).then(() => setQuantity(quantity - 1));
if (!lockedRaffle) updateTicket("free", id);
}}
disabled={quantity <= 0 || lockedRaffle}
>
onClick={() => {
//updateTicket("free", id).then(() => setQuantity(quantity - 1));
if (!lockedRaffle) updateTicket("free", id);
}}
disabled={quantity <= 0 || lockedRaffle}
>
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
3arcus
3arcusOP•2y ago
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 😦
deforestor
deforestor•2y ago
(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 anyways
3arcus
3arcusOP•2y ago
gotcha! but that would expose the "bearer" token right?
deforestor
deforestor•2y ago
The backend should be the one doing the check
3arcus
3arcusOP•2y ago
thankfully my "bug" lead me to that conclusion but I cant find the proper solution now
deforestor
deforestor•2y ago
Yes
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
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?
3arcus
3arcusOP•2y ago
like Delete / Create yes, precisely, I dont want someone calling the endpoint and creating entries in the database, or deleting them
deforestor
deforestor•2y ago
There are many solutions for that you have buttons on the UI that perform those actions (for you)?
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
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 id
3arcus
3arcusOP•2y ago
it'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
deforestor
deforestor•2y ago
there's no user at all?
3arcus
3arcusOP•2y ago
there's no users on the database, just references to users
deforestor
deforestor•2y ago
What does that mean
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
That's impossible
3arcus
3arcusOP•2y ago
seems Im missing some server side component that does the auth
deforestor
deforestor•2y ago
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
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
But a bearer token would mean you have a way to authenticate the client
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
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?
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
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?
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
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
3arcus
3arcusOP•2y ago
I want to authorize my server to call the routes how do I authenticate "my app"
deforestor
deforestor•2y ago
Why not just call them as functions?
3arcus
3arcusOP•2y ago
ok, no endpoints then, just functions that interact with the db
deforestor
deforestor•2y ago
exactly
3arcus
3arcusOP•2y ago
I'm going to try that thank you! actually, would still have the same problem, the functions would be client side and exposed
deforestor
deforestor•2y ago
They shouldn't be If you have these functions on the server, they should not be in the client
3arcus
3arcusOP•2y ago
yes but the client needs to call them for instance, after pressing a button
deforestor
deforestor•2y ago
But you said your app is the one that calls these functions
3arcus
3arcusOP•2y ago
based on client input
deforestor
deforestor•2y ago
this is very confusing hahaha
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
you probably are
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
Ahhh
3arcus
3arcusOP•2y ago
(like I do with insomnia with test it)
deforestor
deforestor•2y ago
Then I think it's easier than it seems If the user can only do something with its own wallet
3arcus
3arcusOP•2y ago
crypepe 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
deforestor
deforestor•2y ago
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
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
That is easy yeah
3arcus
3arcusOP•2y ago
not for me lol 😛
deforestor
deforestor•2y ago
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
3arcus
3arcusOP•2y ago
so I need middleware
deforestor
deforestor•2y ago
Not really You need an authentication You need to know that X is X to delete X
3arcus
3arcusOP•2y ago
but I know that the app is fully functional right now
deforestor
deforestor•2y ago
when you "log in with wallet", the API should send you a token to authenticate that user, or something like that
3arcus
3arcusOP•2y ago
thats what I was trying to avoid I guess I cannot
deforestor
deforestor•2y ago
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
3arcus
3arcusOP•2y ago
yeah I get it now
deforestor
deforestor•2y ago
and then you check against the wallet in the db
3arcus
3arcusOP•2y ago
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
deforestor
deforestor•2y ago
I think you need to understand the authentication flow
3arcus
3arcusOP•2y ago
I do, but Im grasping at what it kinda does Im going to look into this further
deforestor
deforestor•2y ago
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 guess
3arcus
3arcusOP•2y ago
Ok, 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 smash
Want results from more Discord servers?
Add your server