Personal Wiki

  • /wiki/React/AuthenticationInReactApps
  • Table of Contents

    1. Github repo: https://github.com/chenkie/orbit
    2. Ryan:
    3. Today:
    4. Notes
    5. JSON Web Tokens
      1. What is the difference between btoa and hmacSHA256?
      2. What are the considerations for a serverless based environment?
      3. [?]
    6. Login and Signup
      1. What happens after login/signup?
      2. Can browser extensions read local storage?
      3. https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/
    7. Access Control for the UI
    8. Access Control for Front End Routes
    9. Accessing Protected Server Resources
    10. Scoping Data to a User and Role
    11. Lazy Loading Protected Routes

    Github repo: https://github.com/chenkie/orbit

    Ryan:

    • Used to work at Auth0
    • Now a consultant

    Today:

    • Learn how to secure a React app from the ground up
    • Find out how to make it secure
    • JWT and how to think about them

    Notes

    • ATLASURL and JWT - sharing Ryans but they are throwaway. Make sure they arent in source control.
    • Compare in Github to see the start and end of lesson diff

    JSON Web Tokens

    With JWT, you need to:

    • refresh tokens
    • you cant revoke tokens
    • can be stolen and misused.

    Using a cookie/session authentication would fix this

    [?] What is the difference between btoa and hmacSHA256?

    [?] What are the considerations for a serverless based environment?

    With a SSR, there are some things to be considered - it is blurring the lines between backend and frontend, so not always clear what is/is not secure.

    [?]

    • Should make the JWT urlsafe, even if it is only being posted through as a header file.

    Login and Signup

    • Username/Password

      • Not a great way to do authentication most people dont have super strong password.
      • Lots of people dont have a password manager
    • Using an OAuth provider means that you dont have to do this Still password but not your responsibility

    • Passwordless authentication Put in your email, you get a new token on that email

    • TouchID Webautentication

    What happens after login/signup?

    • Set-cookie header
    • That is then sent on every time.
    • Where do you store the JWT?
      • local storage - this is the least secure. You could do a localStorage.getItem('accessToken') and then fetch them
      • HttpOnly cookie - this means it cant be read by JS
        • console.log(document.cookie)
        • The ones that are HttpOnly will not be read in this way - they only travel in HTTP requests
        • Are susceptible to cross site authentication issues?
          • A link to go to bank
          • Deposit money in an account
      • Browser state

    [?] Can browser extensions read local storage?

    • Make sure you hash your passwords

    • Good to keep instances of your fetch for public/private calls - dont want to unnecessarily send your token elsewhere

    • ReactSecurity.io

    [ ] https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/

    • On the front end, we have hints/clues that the user is authenticated. They arent fail-safe but they are helpful.

    Access Control for the UI

    We need to show certain parts if the user is currently authenticated.

    In a req/res roundtrip app - this is straightforward. As you request new pages, the server is responsible for putting together the HTML and can safely check whether the user is authenticated and has permissions.

    With SPA, all that is powered lives in the browser. We dont know what the user is going to do with our code once it has been shipped.

    Our front end apps can be hacked - there are things that we can do to make it harder but it is probably not impossible.

    The data that powers the app needs to stay on the server - it is the only way it is safe.

    We have clues to tell if a user is authenticated:

    • token expiry time

    • a boolean

    • presence of a token

    • We can use a Boolean in the AuthContext to communicate authentication and role (isAuth/isAdmin)

    • We can use a key on our routes and only show links if the relevant role is present, setting up a role key or otherwise in the definition.

      {navItems.map((navItem, i) => {
              if (navItem.allowedRoles.some("admin")&& !auth.isAdmin()) {
                return "";
              }
              return (
                <NavItemContainer key={i}>
                  <NavItem navItem={navItem} />
                </NavItemContainer>
              );
            })}
      

    Access Control for Front End Routes

    • There are routes that only authenticated users should see.
    • We can redirect if the user fails our authentication/authorization checks
    • Still not fully secure - the user cant get to the inventory route but the code that powers the route does get to their browser.

    Accessing Protected Server Resources

    • The browser isnt capable of doing this. We need to use server level middleware.

    • We send the JSON Web Token in the header or as part of a HttpOnly cookie.

    • On the server, we can use middleware to check if this is authenticated.

    • There is an express-jwt package that can be used for middleware

    • I use the Auth0 helper function,

      export default auth0.requireAuthentication(async function billingInfo(req, res) {
        const { user } = await auth0.getSession(req);
        res.json({
          email: user.email,
          country: 'United States',
          paymentMethod: 'Paypal'
        });
      });
      

    This workshop ended up going longer than I expected, so I missed the last two sessions. Below is my summary of the notes but Ill come back and view again when the video is available.

    Scoping Data to a User and Role

    • We did this on the frontend with the key and filtering the links but that doesnt protect us if the data is already in the browser.

    • We can use the JWT to easily query the data that belongs to specific users.

    • The payload contains the `sub` which is often the user ID.

    • We can trust the data which means we dont have to make a DB call to check on the validity - so we reduce the load on the db

    • We have to make two checks, one for the signature verification and one for the role in the token payload. This will often be handled by middleware.

    Lazy Loading Protected Routes

    • Once the code is in the browser, you have no control.

    • We can lazy load components that are authenticated.

    • This isnt possible with Next - without using getInitialProps I guess. We could check in there?

  • /wiki/React/kent-c-dodds-workshops
  • I had the privilege to spend a lot of time in Kent C. Dodds workshops learning about React. I program in React a lot but these workshops really helped to solidify my knowledge. Here are my posts exploring what I learnt.

    Given that this was more contact time than a lot of my modules at University, I learnt loads. Not just about React but about well-delivered online workshops.

  • /wiki/React/react-query-custom-mutation-hook
  • I'm trying to work out how to create a react-query custom hook for mutations. I've moved all my getting logic, I just need to get my setting logic. The problem I'm having is that most of the examples are in a single file or are just using useQuery (maybe this isn't possible?). Here was what I was hoping to do. In my hooks file, I have:

    function useSetUserData() {
      const [mutateUserData] = useMutation(
        ({ rowid, fields }) => setAirtableFields({ rowid, fields }),
        {
          onMutate: fields => {
            const previousValue = queryCache.getQueryData([Auth0])
    
            queryCache.setQueryData([Auth0], old => ({
              ...old,
              items: [...old.items, ...fields],
            }))
    
            return previousValue
          },
          // On failure, roll back to the previous value
          onError: (err, variables, previousValue) =>
            queryCache.setQueryData([Auth0], previousValue),
          // After success or failure, refetch the user query
          onSettled: () => queryCache.refetchQueries([Auth0]),
        }
      )
      return [mutateUserData]
    }
    
    export { useUserData, useSetUserData }
    

    Then, where I want to use it I have:

    import { useUserData, useSetUserData } from "../../lib/users";
    ...
      const [changeUser] = useSetUserData();
    
    ...
      const updateUser = (fields_to_change) => {
        fields_to_change = { ...fields_to_change, Auth0: user.sub };
        changeUser({ fields: fields_to_change, rowid: user.rowid });
      };
    
    

    This calls the useMutation hook but the parameters are not passed to the function. So, the initial problem is triggered at line 3 of my hooks file function. The onError is triggered and it complains that old is not defined. I've been using the example here but seem to be a bit stumped.