diff --git a/quickstarts/fullstack/remix.mdx b/quickstarts/fullstack/remix.mdx
index d9eea52..054bcd8 100644
--- a/quickstarts/fullstack/remix.mdx
+++ b/quickstarts/fullstack/remix.mdx
@@ -4,28 +4,51 @@ title: Integrate Hanko with Remix
description: Learn how to quickly add authentication and user profile in your Remix app using Hanko.
---
-## Install `@teamhanko/hanko-elements`
+## Create a Remix application
+Run the following command to [create a new Remix application](https://remix.run/docs/en/main/start/quickstart):
+
+
+```bash terminal
+npx create-remix@latest
+```
+
+
+## Install `@teamhanko/hanko-elements`
Once you've initialized your Remix app, installing hanko-elements provides you with access to the prebuilt components: `hanko-auth` and `hanko-profile`.
-
+
```bash npm
+cd project-name
npm install @teamhanko/hanko-elements
```
```bash pnpm
+cd project-name
pnpm add @teamhanko/hanko-elements
```
```bash bun
+cd project-name
bun add @teamhanko/hanko-elements
```
```bash yarn
+cd project-name
yarn add @teamhanko/hanko-elements
```
+
+## Setup your Hanko project
+Go to the [Hanko console](https://cloud.hanko.io/) and [create a project for this application.](/setup-hanko-cloud)
+
+
+ During creation make sure to input the URL you will be developing on as the `APP URL`.\
+ (Most likely http://localhost:5173/)
+
+
+
## Add the Hanko API URL
Retrieve the API URL from the [Hanko console](https://cloud.hanko.io/) and place it in your `.env` file.
@@ -39,6 +62,7 @@ HANKO_API_URL=https://f4****-4802-49ad-8e0b-3d3****ab32.hanko.io
backend.
+
## Importing and exporting Hanko functions in a `.client.ts` file
Since the `@teamhanko/hanko-elements` package is designed to work on the client side, you'll need to import the Hanko functions into a `.client.ts` file. This file will then export them, making them available for use throughout your application.
@@ -49,289 +73,278 @@ import { register, Hanko } from "@teamhanko/hanko-elements";
export { register, Hanko };
```
+## Create Hanko components
+Create a folder called `components` and create two files in it, `HankoAuth.tsx` and `HankoProfile.jsx`.
-## Add `` component
+### Hanko Auth
-The `` web component adds a login interface to your app. The following code example defines a `HankoAuth` component in the `login` route.
+Now lets setup the `HankoAuth.tsx` file to create a functioning login page.
+Here we subscribe to the `onSessionCreated` [event](/guides/hanko-elements/using-frontend-sdk#events), this triggers when a user successfully logs in. You can use these event to perform any desired action (e.g. redirect to your dashboard).
-It uses the register function from `hanko.client.ts` to register `` component with the brower's [CustomElementRegistry](https://developer.mozilla.org/de/docs/Web/API/CustomElementRegistry). When the authentication flow is completed, a callback is set up to redirect the user to the dashboard.
+[For more information please refer to the Auth Component Page.](/guides/hanko-elements/auth-component)
-```tsx routes/login.tsx
+```jsx components/HankoAuth.tsx
import { useNavigate, useLoaderData } from "@remix-run/react";
-
import { Suspense, useCallback, useEffect, useState } from "react";
-
-import { register, type Hanko } from "~/utils/hanko.client";
+import { register, type Hanko } from "../utils/hanko.client";
export const loader = () => {
return { hankoUrl: process.env.HANKO_API_URL };
};
-
const HankoAuth = () => {
- const [hanko, setHanko] = useState();
- const navigate = useNavigate();
-
- const data = useLoaderData();
- const hankoUrl = data.hankoUrl || '';
-
- const redirectAfterLogin = useCallback(() => {
- navigate("/dashboard");
- }, [navigate]);
+ const [hanko, setHanko] = useState();
+ const navigate = useNavigate();
+
+ const data = useLoaderData();
+ const hankoUrl = data.hankoUrl || '';
+
+ const redirectAfterLogin = useCallback(() => {
+ navigate("/dashboard");//Path user gets navigated to once they log in
+ }, [navigate]);
+
+ useEffect(() => {
+ if (hanko) {
+ hanko.onSessionCreated(() => {
+ //Succesfully Logged In
+ redirectAfterLogin();
+ });
+ }
+ }, [hanko, redirectAfterLogin]);
+
+ useEffect(() => {
+ register(hankoUrl)
+ .catch((error: Error) => {
+ console.error(error.message);
+ })
+ .then((result) => {
+ if (result) {
+ setHanko(result.hanko);
+ }
+ });
+ }, [hankoUrl]);
+
+ return (
+
- )
+export default function Dashboard() {
+ return (
+ <>
+
+ >
+ );
}
-
-export default Dashboard
```
-It should look like this π
+By now you should be able to go to `/` to see the ``, and to `/dashboard` to see the ``.
+They should look something like thisπ
-
+
+
+
+
+
+ If `HankoAuth` is not loading please restart the application as it might've not loaded the .env correctly the first time.\
+ \
+ `HankoProfile` will only look like the picture while you are logged in.
+
## Implement logout functionality
-You can use `@teamhanko/hanko-elements` to easily manage user logouts. Below we add a logout button to the dashboard page.
+You can use `@teamhanko/hanko-elements` to easily logout users. Here we will make a logout button.
+Create `LogoutButton.tsx` and insert the code below.
-```tsx app/routes/dashboard.tsx
+```jsx components/LogoutButton.tsx
import { useLoaderData, useNavigate } from "@remix-run/react";
-
-import { Suspense, useEffect, useState } from "react";
-import { type Hanko, register } from "~/utils/hanko.client";
+import { useEffect, useState, } from "react";
+import { type Hanko, } from "../utils/hanko.client";
export const loader = () => {
return { hankoUrl: process.env.HANKO_API_URL };
};
-function HankoProfile() {
- let data = useLoaderData();
- let hankoUrl = data.hankoUrl || '';
-
- const [hanko, setHanko] = useState();
- const navigate = useNavigate();
+const LogoutButton = () => {
+ const data = useLoaderData();
+ const hankoUrl = data.hankoUrl || '';
+ const navigate = useNavigate();
+ const [hanko, setHanko] = useState();
+
useEffect(() => {
- register(hankoUrl)
- .catch((error: Error) => {
- console.error(error.message);
- })
- .then((result) => {
- if (result) {
- setHanko(result.hanko);
- }
- });
- }, [hankoUrl]);
-
- // here we define the logout function
+ import("@teamhanko/hanko-elements").then(({ Hanko }) =>
+ setHanko(new Hanko(hankoUrl ?? ""))
+ );
+ }, []);
+
const logout = async () => {
try {
- await hanko?.user.logout();
- navigate("/login");
+ await hanko?.logout();
+
+ navigate("/");//Path the user will be redirected to once logged out
+
return;
} catch (error) {
console.error("Error during logout:", error);
}
};
- return (
-
-
-
-
-
-
-
-
- )
-}
-
-const Dashboard = () => {
- return (
-
-
-
- )
+ return ;
}
-
-export default Dashboard
+
+export default LogoutButton;
```
+
## Customize component styles
You can customize the appearance of `hanko-auth` and `hanko-profile` components using CSS variables and parts. Refer to our [customization guide](/guides/hanko-elements/customize-appearance).
## Securing routes
-To add JWT verification with Hanko in your Remix application, we're using the [jose](https://www.npmjs.com/package/jose) library. However, you're free to choose any other suitable library.
-
-Below we define a `validateJwtAndFetchUserId` function which validates the JWT and fetches the user ID. This will also be used to protect routes and ensure only authenticated users can access certain parts of the application.
+To secure our routes we can validate our cookie token using `hankoAPI/sessions/validate`.
+Create a file called `auth.server.ts` in `/services` and add this code.
```jsx app/services/auth.server.ts
import { parse } from "cookie";
-import { jwtVerify, createRemoteJWKSet } from "jose";
-
-const hankoApiUrl = process.env.HANKO_API_URL;
-
-export async function validateJwtAndFetchUserId(request: Request) {
- const cookies = parse(request.headers.get("Cookie") || "");
- const token = cookies.hanko;
-
- if (!token) {
- return null;
- }
-
- const JWKS = createRemoteJWKSet(
- new URL(`${hankoApiUrl}/.well-known/jwks.json`)
- );
- let payload;
-
- try {
- const verifiedJWT = await jwtVerify(token, JWKS);
- payload = verifiedJWT.payload;
- } catch {
- return null;
- }
- const userID = payload.sub;
+export async function ValidateCurrentSession(request: Request, hankoUrl: string){
- if (!userID) {
- return null;
- }
+ const cookies = parse(request.headers.get("Cookie") || "");
+ const token = cookies.hanko;
- return userID;
+ const validationOptions = {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: `{"session_token":"${token}"}`
+ }
+ try {
+ const response = await fetch(hankoUrl + '/sessions/validate', validationOptions);
+
+ if (!response.ok) throw new Error('Session validation failed');
+
+ const verifiedResponse = await response.json();
+
+ return verifiedResponse.is_valid
+
+ } catch (error) {
+ console.log(error)
+ return false;
+ }
}
+
```
-This is how you can use it in your routes:
+To protect a route using this we can validate it in the loader function.
-```tsx app/routes/protected.tsx
-import { type LoaderFunction, json, redirect } from '@remix-run/node';
-import { validateJwtAndFetchUserId } from '../services/auth.server';
+```tsx app/routes/dashboard.tsx
+import HankoProfile from "../components/HankoProfile";
+import LogoutButton from "../components/LogoutButton";
-export let loader: LoaderFunction = async ({ request }) => {
- const userID = await validateJwtAndFetchUserId(request);
+import { type LoaderFunction, redirect } from "@remix-run/node";
+import { ValidateCurrentSession } from "../services/auth.server";
- if (!userID) {
- return redirect('/login');
- }
+export const loader: LoaderFunction = async ({request}) => {
+ const hankoUrl = process.env.HANKO_API_URL || "";
+ const validated = await ValidateCurrentSession(request , hankoUrl);
- return json({ userID });
+ if(!validated){
+ return redirect("/");//Path to redirect to if user is not authenticated
+ }
+
+ return { hankoUrl: hankoUrl };
};
-export default function Protected() {
- return (
-