Fullstack Development

261497: Fullstack Development

Authentication / Authorization

261497: Fullstack Development

Part 4: SPA

261497: Fullstack Development

Setup

  • git clone https://github.com/fullstack-68/auth-spa.git
  • Backend
    • cd backend
    • Fill in .env
    • pnpm install
    • pnpm run db:reset
    • pnpm run dev
261497: Fullstack Development

Setup

  • Frontend
    • cd frontend
    • pnpm install
    • pnpm run dev
261497: Fullstack Development

Backend

  • Routes now return json / redirect header instead of html.
  • Created GET /me route for clients to check their auth' states.
  • Apart from that, there is very mininal change, surprisingly.
261497: Fullstack Development

261497: Fullstack Development

Frontend

  • Created (client-side) routing.
  • Created logic to query/update auth's state.
  • Modified proxy server to take care of Callback URL (bypass client-routing).
  • Created signup form/login and other UI.
261497: Fullstack Development

Highlighted packages

{
  "@tanstack/react-query": "^5.85.5",
  "react-router": "^7.8.2"
}
261497: Fullstack Development

Client-side routing

src/App.tsx

import { RouterProvider } from "react-router/dom";
import { createBrowserRouter } from "react-router";
const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    children: [
      {
        path: "/",
        element: <Home />,
      },
    ],
  },
]);
261497: Fullstack Development

Client-side routing

src/App.tsx

function App() {
  return (
    // ...
    <RouterProvider router={router} />
    // ...
  );
}

export default App;
261497: Fullstack Development

Getting auth state

src/hooks/useAuth.ts

import { useQuery } from "@tanstack/react-query";
// ...
function getMe() {
  return axios.get<AuthData>("/api/me");
}

function useAuth() {
  // Queries
  const { data, error, refetch } = useQuery({
    queryFn: getMe,
    // Other options
  });
  return { user: data?.user, sessions: data?.sessions, error, refetch };
}
261497: Fullstack Development

Getting auth state

src/components/Nav.tsx

const Nav: FC = () => {
  const { user } = useAuth(); 👈👈👈
  // ...
  return (
    <nav>
      // ...
    </nav>
  );
};

export default Nav;
261497: Fullstack Development

Handle Callback URL

vite.config.ts

export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      "/api": {
        // ...
      },
      "/callback": { 👈👈👈
        target: "http://localhost:5002",
      },
    },
  },
});
261497: Fullstack Development

261497: Fullstack Development

With proxy server

  • The cookie will automatically sent to backend for all requests because it is the "same site".
261497: Fullstack Development

If you don't have proxy server.

  • Cookies are still automatically sent given the same host but different port.
  • Need to set withCredentials to true on any AJAX request (via fetch or axios APIs)
    • Note that httpOnly cookie are sent by this technique.
  • Need to allow CORS in the backend.
261497: Fullstack Development

But we still need more

  • More OAuth providers
  • Forget password feature
  • Email confirmation
  • OTP link
  • Two factor authentication
  • Passkey
  • ...
261497: Fullstack Development

When can I start building my stuff?

261497: Fullstack Development

Third-party solution

261497: Fullstack Development

Better-Auth

261497: Fullstack Development

Design

  • Better-Auth (and AuthJS) is not a middleware.
    • Unlike passport
  • It provides ready-made API endpoints.
  • This architecture is more suitable for SPAs and fullstack frameworks (NextJS).
    • Using it with MPA is quite unintuitive.
261497: Fullstack Development

Setup

  • git clone https://github.com/fullstack-68/auth-spa-better-auth.git
  • Backend
    • cd backend
    • Fill in .env
    • pnpm install
    • pnpm run db:reset
    • pnpm run dev
261497: Fullstack Development

Setup

  • Frontend

    • cd frontend
    • pnpm install
    • pnpm run dev
261497: Fullstack Development

Note

  • DB schema
  • npx @better-auth/cli@latest generate
261497: Fullstack Development

New routes (backend)

261497: Fullstack Development

Frontend client

useAuth.ts

import { useQuery } from "@tanstack/react-query";
import { authClient } from "../lib/auth-client";
function getMe() {
  return authClient.getSession();
}
261497: Fullstack Development

Sign up

Signup.tsx

const res = await authClient.signUp.email({
  email,
  password,
  name,
  image: "logos/robot.png",
});
261497: Fullstack Development

Login (email)

const res = await authClient.signIn.email({
  email,
  password,
  rememberMe: true,
});
261497: Fullstack Development

Login (Social)

const signInGitHub = async () => {
  const data = await authClient.signIn.social({
    provider: "github",
  });
  console.log({ data });
};
261497: Fullstack Development

More features

  • Forget password
  • Email confirmation
  • OTP link
  • Two factor
261497: Fullstack Development