diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index e87337c..840aea5 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -29,6 +29,9 @@ jobs:
run: |
docker buildx build --platform linux/amd64 \
-t ${{ secrets.DOCKER_USERNAME }}/hscodes:${{ github.run_number }} \
+ --build-arg VITE_AUTH0_DOMAIN=${{ secrets.AUTH0_DOMAIN }} \
+ --build-arg VITE_AUTH0_CLIENT_ID=${{ secrets.AUTH0_CLIENTID }} \
+ --build-arg VITE_AUTH0_AUDIENCE=${{ secrets.AUTH0_AUDIENCE }} \
--file Dockerfile \
--push .
diff --git a/Autumn.SPA/.env.example b/Autumn.SPA/.env.example
new file mode 100644
index 0000000..f6834f4
--- /dev/null
+++ b/Autumn.SPA/.env.example
@@ -0,0 +1,3 @@
+VITE_AUTH0_DOMAIN=your-tenant.auth0.com
+VITE_AUTH0_CLIENT_ID=your-client-id
+VITE_AUTH0_AUDIENCE=autumnapi
diff --git a/Autumn.SPA/.gitignore b/Autumn.SPA/.gitignore
index a547bf3..61cb0c2 100644
--- a/Autumn.SPA/.gitignore
+++ b/Autumn.SPA/.gitignore
@@ -11,6 +11,7 @@ node_modules
dist
dist-ssr
*.local
+.vite
# Editor directories and files
.vscode/*
diff --git a/Autumn.SPA/package-lock.json b/Autumn.SPA/package-lock.json
index 6b8a00c..dc2d5d0 100644
--- a/Autumn.SPA/package-lock.json
+++ b/Autumn.SPA/package-lock.json
@@ -8,6 +8,7 @@
"name": "autumn-spa",
"version": "0.0.0",
"dependencies": {
+ "@auth0/auth0-react": "^2.14.0",
"@tailwindcss/vite": "^4.1.18",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
@@ -26,6 +27,41 @@
"vite": "^7.3.1"
}
},
+ "node_modules/@auth0/auth0-auth-js": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@auth0/auth0-auth-js/-/auth0-auth-js-1.4.0.tgz",
+ "integrity": "sha512-ShA7KT4KvcBEtxsXZTcrmoNxai5q1JXhB2aEBFnZD1L6LNLzzmiUWiFTtGMsaaITCylr8TJ/onEQk6XZmUHXbg==",
+ "license": "MIT",
+ "dependencies": {
+ "jose": "^6.0.8",
+ "openid-client": "^6.8.0"
+ }
+ },
+ "node_modules/@auth0/auth0-react": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.14.0.tgz",
+ "integrity": "sha512-Uyn9mB+pOJ0ko81aJoTbOJok11E4rFeNXhzNFLsvWaUD7dp0Qe3B9zwsbQhaCxrsJOX1UXg0y4ck/RjwNnsPoA==",
+ "license": "MIT",
+ "dependencies": {
+ "@auth0/auth0-spa-js": "^2.15.0"
+ },
+ "peerDependencies": {
+ "react": "^16.11.0 || ^17 || ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1",
+ "react-dom": "^16.11.0 || ^17 || ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1"
+ }
+ },
+ "node_modules/@auth0/auth0-spa-js": {
+ "version": "2.15.0",
+ "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.15.0.tgz",
+ "integrity": "sha512-cXv1Isyy4JEc+GxesQPFj3SEbDSnCVQTGiardY9WLSoYTsMMU585Kgm9TJFPJO4dDq3wi+DSJoy3IUcB3rr9nA==",
+ "license": "MIT",
+ "dependencies": {
+ "@auth0/auth0-auth-js": "^1.4.0",
+ "browser-tabs-lock": "^1.2.15",
+ "dpop": "^2.1.1",
+ "es-cookie": "~1.3.2"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
@@ -1760,6 +1796,16 @@
"concat-map": "0.0.1"
}
},
+ "node_modules/browser-tabs-lock": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.3.0.tgz",
+ "integrity": "sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash": ">=4.17.21"
+ }
+ },
"node_modules/browserslist": {
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
@@ -1933,6 +1979,15 @@
"node": ">=8"
}
},
+ "node_modules/dpop": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/dpop/-/dpop-2.1.1.tgz",
+ "integrity": "sha512-J0Of2JTiM4h5si0tlbPQ/lkqfZ5wAEVkKYBhkwyyANnPJfWH4VsR5uIkZ+T+OSPIwDYUg1fbd5Mmodd25HjY1w==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.5.286",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
@@ -1953,6 +2008,12 @@
"node": ">=10.13.0"
}
},
+ "node_modules/es-cookie": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz",
+ "integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==",
+ "license": "MIT"
+ },
"node_modules/esbuild": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
@@ -2450,6 +2511,15 @@
"jiti": "lib/jiti-cli.mjs"
}
},
+ "node_modules/jose": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz",
+ "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -2806,6 +2876,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash": {
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "license": "MIT"
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -2893,6 +2969,28 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/oauth4webapi": {
+ "version": "3.8.4",
+ "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.4.tgz",
+ "integrity": "sha512-EKlVEgav8zH31IXxvhCqjEgQws6S9QmnmJyLXmeV5REf59g7VmqRVa5l/rhGWtUqGm2rLVTNwukn9hla5kJ2WQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/openid-client": {
+ "version": "6.8.2",
+ "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.2.tgz",
+ "integrity": "sha512-uOvTCndr4udZsKihJ68H9bUICrriHdUVJ6Az+4Ns6cW55rwM5h0bjVIzDz2SxgOI84LKjFyjOFvERLzdTUROGA==",
+ "license": "MIT",
+ "dependencies": {
+ "jose": "^6.1.3",
+ "oauth4webapi": "^3.8.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -3058,6 +3156,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
diff --git a/Autumn.SPA/package.json b/Autumn.SPA/package.json
index 1787163..9828fc8 100644
--- a/Autumn.SPA/package.json
+++ b/Autumn.SPA/package.json
@@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
+ "@auth0/auth0-react": "^2.14.0",
"@tailwindcss/vite": "^4.1.18",
"lucide-react": "^0.564.0",
"react": "^19.2.0",
diff --git a/Autumn.SPA/src/App.jsx b/Autumn.SPA/src/App.jsx
index 8be8a45..240afe3 100644
--- a/Autumn.SPA/src/App.jsx
+++ b/Autumn.SPA/src/App.jsx
@@ -1,11 +1,13 @@
-import { useState, useEffect } from "react";
-import { api } from "./api";
+import { useState, useEffect, useContext } from "react";
+import { Auth0Context } from "@auth0/auth0-react";
+import { api, setAuthToken } from "./api";
import Header from "./components/Header";
import Home from "./components/Home";
import SearchView from "./components/SearchView";
import CalcView from "./components/CalcView";
import BrowseView from "./components/BrowseView";
import Toast from "./components/Toast";
+import CookieConsent from "./components/CookieConsent";
export default function App() {
const [mode, setMode] = useState("light");
@@ -13,9 +15,18 @@ export default function App() {
const [query, setQuery] = useState("");
const [country, setCountry] = useState("NG");
const [countries, setCountries] = useState([]);
- // Cross-view state for calculator prefill
const [calcInit, setCalcInit] = useState({ hscode: "", product: "" });
+ // Only use Auth0 context when env vars are configured
+ const authEnabled = !!(import.meta.env.VITE_AUTH0_DOMAIN && import.meta.env.VITE_AUTH0_CLIENT_ID);
+ const rawAuth = useContext(Auth0Context);
+ const auth = authEnabled ? rawAuth : null;
+
+ useEffect(() => {
+ if (!auth?.isAuthenticated || !auth.getAccessTokenSilently) return;
+ auth.getAccessTokenSilently().then(setAuthToken).catch(() => {});
+ }, [auth?.isAuthenticated]);
+
useEffect(() => {
document.documentElement.classList.toggle("dark", mode === "dark");
}, [mode]);
@@ -33,13 +44,11 @@ export default function App() {
setQuery("");
};
- // Navigate from SearchView → CalcView with HS code prefilled
const goToCalc = (hscode, product) => {
setCalcInit({ hscode: hscode || "", product: product || "" });
setView("calculator");
};
- // Navigate from CalcView → SearchView to find an HS code
const goToSearch = (product) => {
setQuery(product || "");
setView("results");
@@ -56,6 +65,7 @@ export default function App() {
mode={mode}
setMode={setMode}
onReset={onReset}
+ auth={auth}
/>
{view === "home" && (
@@ -100,10 +110,10 @@ export default function App() {
)}
+ We use cookies to analyse site usage and improve our service. +
+