PricingBlog

Best practice for handling redirects for non-authenticated users and auto-logout

  • tomthebigtree-1161938252433993758

    Tom Ireland

    2 years ago

    I'm wondering what the best practice is for handling redirects when a user accesses a page where they're not authenticated. At the moment, I have an API call set in my site header that does a GET request to get the logged in user profile (profiles table). If the user does not have an access token obviously no data is returned but the request still returns a status of 200 and an empty array is returned, which indicates partial success but no data is available due to the RLS policy for the table.

    This means that no error is returned in order to handle redirect to another page e.g. /login, so I'm wondering what the best approach might be to handle this.

    Similarly, when a user session expires, users can still access application pages but no data is returned due to RLS policies in place. For testing and development this is okay but I'd obviously look to lock this down and auto-redirect on session expiry so that access to the page is no longer possible and the user is redirected to /login.

    Does anyone have any pointers on how best to handle these scenarios?
  • tomthebigtree-1161939041697153024

    Tom Ireland

    2 years ago

    I see the JS docs for Supabase has options for listening to auth and retrieving session info, which might be useful but I can't see anything for handling this via the API.
  • erikbeus-1161940982544207902

    Erik Beuschau

    2 years ago

    We plan to add builtin support for this in toddle. We're thinking of allowing you to specify per API whether it should trigger a redirect on a certain response (status code perhaps). That means you'll (eventually) be able to perform these redirects server side.
    For now though, your approach of calling an API to check whether the user is authenticated, and then do a client-side redirect does the trick. It's also possible to extend a user's session by using a refresh token, but in toddle we had to handle that server side as well. Perhaps you could build an edge function in Supabase that allows you to extend an access token based on a refresh token?
    👍1
  • tomthebigtree-1161954082441928704

    Tom Ireland

    2 years ago

    That sounds promising, Erik. 🙂 Edge functions are a bit beyond me so I may have to utilise the auth check as you mentioned and maybe do that before I do the profile check.
    👍2
  • tomthebigtree-1166829855116116078

    Tom Ireland

    2 years ago

    @Erik Beuschau (or anyone else in the community) - Just looping back on this one as I did put the redirect in place with an auth check. Basically a table in Supabase with one entry that should return true if authenticated; if not, redirect to login page. This is checked via my header component that does most of the initial user data loading after auth.

    I'm still getting a flash of some page elements (no data as got RLS in place) prior to redirect because the header is further down the element tree.

    I think I might be able to work around this by doing the auth check in a standalone component further up the tree but not sure if a) that will work or b) is the best way to do this via the Supabase API.

    I have seen some nice loading examples in toddle before content is rendered. I'm curious as to how this is done. It's probably really simple but I cannot figure it out for myself. 😄
  • datomnz-1166834809637699644

    Tom Wrench

    2 years ago

    In regards to the flash of some page elements prior to redirect, are you putting a show/hide formula on these elements so they only show once auth is successful and user exists? If they are hidden until this condition is met then they shouldn't show at all and the user redirected without seeing any of these elements.

    In my project my main-navigation component (see screenshot) deals with the auth API check, and the page content (under the main container) has a show/hide formula to only show once auth is complete and user exists. If no user exists (i.e. auth is unsuccessful) then I redirect and the content under main never gets shown.

    I've got a userData variable on my page, which gets populated with the user data from my profiles table in Supabase via the main-navigation component if auth is successful and user exists. Else the variable remains empty/blank if user doesn't exist. So my show/hide formula for the 'main' container is basically if the userData variable contains data, then show, else hide.

    Hopefully this make sense?
    1166834809436393482-image.png
  • tomthebigtree-1166836714334072933

    Tom Ireland

    2 years ago

    Thanks, @Tom Wrench I was thinking of that but my header comes after my sidebar, so still get a flash. I could set a show/hide on my sidebar but then I'd have to do this across every page, right? The sidebar is a component, so would only need to be set once I suppose.
  • datomnz-1166837998596391042

    Tom Wrench

    2 years ago

    yes, I guess if you have an attribute for your side bar component for show/hide (set to false be default), which links to a show/hide formula on the whole component (within the component itself)... maybe that could work?
  • But you'd still need to set this attribute for every page so not sure what the most efficient approach would be here.
  • lucasg-1166842236424687639

    Lucas G

    2 years ago

    There isn’t really an efficient method for this either way at least not until the auth functionality that Erik mentioned gets implemented.
    That or global scripts/APIs
  • tomthebigtree-1166843208530149436

    Tom Ireland

    2 years ago

    Yeah, it's an interesting problem. 🤔 It's not a massive deal but it sure does irritate me. 😄
  • lucasg-1166844462207598653

    Lucas G

    2 years ago

    My solution was going to be similar to Tom’s but instead of setting the show/hide condition on the main content, I was going to use a “loader” component that only goes away until auth is verified
  • That way I would only have to set it on the one thing instead of multiple lol
  • The component just needs a crazy high z-index
  • lucasg-1166845103613165718

    Lucas G

    2 years ago

    And the other benefit is that there’s a “loading” feedback to the user instead of only showing a blank screen which isn’t great UX.
  • tomthebigtree-1166954539958673428

    Tom Ireland

    2 years ago

    Yes! I had a similar idea later last night about blank page 🫣 and high z-index on a component but could not get it to work, which I think was part due to current way I’m doing checks, so needs a refactor. Curious to see your approach if you feel like sharing when you do it, @Lucas G
  • datomnz-1166970502208823296

    Tom Wrench

    2 years ago

    That is a great solution! A downside to that however is that the "hidden" content is still in the page code, so if someone goes digging into the page source they could probably still surface what's underneath the loader (albeit without user data), if they really wanted! That might not matter in some cases, but in others it could.

    For example I've got some page content that contains a form which I only want logged in users being able to use. As far as I'm aware the show/hide formula method will not render the code in the page until the conditions are met, so even if an unauthorised someone went digging in the page source they wouldn't be able to find the form.

    You could always have both - hide the page content with show/hide formula until auth is successful and meanwhile show a loader div/animation until auth is successful.

    I'm probably taking an overly cautious approach, but at least the above is my understanding of how it works. Happy to be told otherwise 😅
  • Tod-1166970503722979368

    Tod

    2 years ago

    Great energy @Tom Wrench! Your continuous contribution to the toddle Community just made you advance to Community Level 8!
    👏2
  • stockton_f-1166980885862694943

    Stockton

    2 years ago

    Just throwing my two cents in here. I can't wait for native support lol Thank you for your time 😉
    😆2
  • lucasg-1166981867174625381

    Lucas G

    2 years ago

    Just played around with it and it works pretty well to be honest.
  • lucasg-1166983483307081811

    Lucas G

    2 years ago

    I made a "loader" component that's fixed position
    100% width and 100vh height
    top: 0
    left: 0

    Added a spinning lottie in the middle. Simple enough.

    Added a "isNotAuthenticated" variable with default 'true' value—this is used for the show/hide condition
    I added an API call to the loader that fetches the user from supabase
    path is /auth/v1/user/
    with bearer token of course

    If a user is not logged in, it returns an error.

    API call on success sets "isNotAuthenticated" to false (hiding the loader)
    API call on fail/error goes to /login/

    Since the API call happens pretty quick, any visits that aren't logged in are immediately redirected.

    Not 100% perfect as a server-side implementation would be ideal but it's working fine!
    🤘3
  • lucasg-1166988523405070336

    Lucas G

    2 years ago

    (loader has been removed so I could access without login for anyone reading this now)
  • That redirects to /login/ without issue
  • Good enough for most uses lol
  • tomthebigtree-1166988879774097489

    Tom Ireland

    2 years ago

    Nice one, @Lucas G . I'm going to take a look at this myself later after I finish work (and after the live stream Buildship x toddle event 😄 )
  • The loader doesn't actually show inside the builder as it instantly redirects to the login page
  • But if you disable the redirect on API fail/error then you can see it normally
  • All the blocks and API call are visible as normal though
  • tomthebigtree-1166989771319549952

    Tom Ireland

    2 years ago

    🤘
  • Thanks for taking the time to share! 🙂
  • lucasg-1166990237889744926

    Lucas G

    2 years ago

    That's the rushed version lol have it properly set up on another project but can't share that one
  • Hope it helps thou
  • datomnz-1166999684179562566

    Tom Wrench

    2 years ago

    Nice one @Lucas G !
  • lordalmighty_-1167065958020562944

    Lord Almighty

    2 years ago

    How about making redirect to login page the default behavior and that it only goes to the page if the user is verified as being logged in. This may make things a bit more secure.
  • tomthebigtree-1167068194876424223

    Tom Ireland

    2 years ago

    It would be nice but the use-case is that if you have a user that tries to access any app URL that's legit (404 is different), you need to redirect (this works fine) but you get a page flash before redirect when checking auth, which is the main challenge. As Lucas, Tom et al were saying, handling this server-side would be better. The current workarounds do appear to work but are a wee bit hacky. When the toddle crew support the server-side stuff, it should just work without the need for the workaround. 🤞
  • Tod-1167068196868735117

    Tod

    2 years ago

    Great energy @Tom Ireland! Your continuous contribution to the toddle Community just made you advance to Community Level 11!
    🔥1
  • okgo.-1182703709692641453

    okgo

    2 years ago

    Hi all! I'm having the same problem. Right now I call the Supabase user auth API on page load to see if the user is authenticated, but this causes all kinds of unexpected behaviour and messes up the builder. Is this 'best practice' or is there a better way? Thanks. 😀
    1182703708925083688-Screenshot_2023-12-08_at_16.20.29.png
    1182703709415821492-Screenshot_2023-12-08_at_16.20.41.png
  • tomthebigtree-1182706984051544124

    Tom Ireland

    2 years ago

    @okgo - What kind of issues are you having in the builder/editor?
  • okgo.-1182707728066551928

    okgo

    2 years ago

    @Tom Ireland Thanks for your reply! The dashboard page with the redirect kicks off the redirect to /login, even if authenticated through the browser plugin. Then after switching to 'test app' and logging in the page is frozen and cannot be edited.
  • tomthebigtree-1182708118325579817

    Tom Ireland

    2 years ago

    Are you logging in via via the preview first? So, not the test view but the actually liver browser preview?
  • If not, you'll need to do that and then we you refresh in the editor, all will be well. 🙂
  • okgo.-1182708370512298116

    okgo

    2 years ago

    Yes exactly, I always authenticate via the preview
  • Do the screenshots that I sent above make sense to you? 🙂
  • tomthebigtree-1182710554452832256

    Tom Ireland

    2 years ago

    They do. It looks like on page load it's getting the results from the API call and checking a certain column/key value but returning false, so I think it may be something to do with the order of the auth check.

    Personally speaking, I have mine set up so that I have a header component on every page (except login). This header has an auto fetch auth check. This is a table in the db with one boolean record where the value is true and the table has an RLS policy set to authenticated users only. If a user accesses a page, it will call this check and on success of a result (this will always be successful because the way Supabase works is that you still get a result from the API) it stores the result in a variable. It will either be true or not. It then has a switch that checked if the value is equal to true and calls more profile info. If the value is not true, it will redirect to /login.

    It may be that you need to tweak yours a wee bit?
    1182710554259890206-auth-check.png
    🙌1
  • okgo.-1182711654094483596

    okgo

    2 years ago

    Thanks for all the information! Definitely very helpful. I will try to tweak my configuration and see if that fixes the issue. 🙂
  • tomthebigtree-1182716885549199531

    Tom Ireland

    2 years ago

    @okgo - Did a short Loom if it helps. I think the toddle crew are planning some improvements from an auth perspective to avoid this workaround I'm demonstrating but in the interim it works well.

    https://www.loom.com/share/30d84ec94b754d65842297b4ad297e30?sid=b4ab30d2-c7c4-4f63-9dac-d509a60b7ddb
  • lucasg-1182726481626005625

    Lucas G

    2 years ago

    Hey Tom, is there a reason why you separate it into 2 calls? Or do you store the user info in a separate table from the standard supabase auth/users table
  • tomthebigtree-1182727166279045230

    Tom Ireland

    2 years ago

    Yep - I have a profile table populated with some data from auth. I don't need/want everything from auth as that all gets returned and I wanted a simple way to set avatars and other metadata. I could use auth and set metadata but - at the time - it was a bit complicated for me to grasp. Supabase did an article on the profile table thing, so thought it was good practice and I think toddle do the same thing.
  • lucasg-1182727205499981835

    Lucas G

    2 years ago

    sometimes you just need to do a refresh for the auth token to work. I typically navigate to something that referesh the canvas like the custom action or theme tabs then switch back to the page I'm working on
  • tomthebigtree-1182727455065247944

    Tom Ireland

    2 years ago

    Plus, I wanted profile updates to be possible by end users and I think auth is read only?
  • Brb - off for dinner!
  • lucasg-1182728003227226275

    Lucas G

    2 years ago

    Well sort of. If a user wants to update their own info then that's not a problem. They just can't update someone else's info
  • Like say an admin updating another user
  • In that case, a profiles table would work better
  • But a user can update their own username, password, and metadata
  • lucasg-1182728776640114779

    Lucas G

    2 years ago

    curl -X PUT 'https://<your-path>.supabase.co/auth/v1/user'
    -d '{
    "email": "someone@email.com",
    "password": "new-password",
    "data": {
    "key": "value"
    }
    }'
  • tomthebigtree-1182758767775981678

    Tom Ireland

    2 years ago

    @Lucas G - Good to know and thanks for sharing that. 👍