Skip to content

Commit 3d53578

Browse files
committed
merge
2 parents 390d07c + a76e653 commit 3d53578

26 files changed

+1237
-25
lines changed

.changeset/fair-trains-reply.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/tall-masks-shop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@thirdweb-dev/service-utils": patch
3+
---
4+
5+
[service-utils] switch to lz4js for compression lib

apps/login/.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
.pnpm-debug.log*
32+
33+
# env files (can opt-in for committing if needed)
34+
.env*
35+
36+
# vercel
37+
.vercel
38+
39+
# typescript
40+
*.tsbuildinfo
41+
next-env.d.ts

apps/login/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
# or
10+
yarn dev
11+
# or
12+
pnpm dev
13+
# or
14+
bun dev
15+
```
16+
17+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18+
19+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20+
21+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22+
23+
## Learn More
24+
25+
To learn more about Next.js, take a look at the following resources:
26+
27+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29+
30+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31+
32+
## Deploy on Vercel
33+
34+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35+
36+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

apps/login/eslint.config.mjs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { dirname } from "node:path";
2+
import { fileURLToPath } from "node:url";
3+
import { FlatCompat } from "@eslint/eslintrc";
4+
5+
const __filename = fileURLToPath(import.meta.url);
6+
const __dirname = dirname(__filename);
7+
8+
const compat = new FlatCompat({
9+
baseDirectory: __dirname,
10+
});
11+
12+
const eslintConfig = [
13+
...compat.extends("next/core-web-vitals", "next/typescript"),
14+
];
15+
16+
export default eslintConfig;

apps/login/next.config.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { NextConfig } from "next";
2+
3+
const nextConfig: NextConfig = {
4+
/* config options here */
5+
async headers() {
6+
return [
7+
{
8+
source: "/api/request",
9+
headers: [
10+
{
11+
key: "Access-Control-Allow-Origin",
12+
value: "*", // Set your origin
13+
},
14+
{
15+
key: "Access-Control-Allow-Methods",
16+
value: "GET, POST, PUT, DELETE, OPTIONS",
17+
},
18+
{
19+
key: "Access-Control-Allow-Headers",
20+
value: "Content-Type, Authorization",
21+
},
22+
],
23+
},
24+
];
25+
},
26+
};
27+
28+
export default nextConfig;

apps/login/package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "thirdweb-login",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"preinstall": "npx only-allow pnpm",
7+
"dev": "next dev --turbopack",
8+
"build": "next build",
9+
"start": "next start",
10+
"format": "biome format ./src --write",
11+
"lint": "biome check ./src && knip && eslint ./src",
12+
"fix": "biome check ./src --fix && eslint ./src --fix",
13+
"typecheck": "tsc --noEmit",
14+
"knip": "knip"
15+
},
16+
"dependencies": {
17+
"next": "15.1.6",
18+
"react": "19.0.0",
19+
"react-dom": "19.0.0",
20+
"thirdweb": "workspace:*"
21+
},
22+
"devDependencies": {
23+
"@eslint/eslintrc": "^3",
24+
"@types/node": "22.10.10",
25+
"@types/react": "19.0.8",
26+
"@types/react-dom": "19.0.3",
27+
"eslint": "^9",
28+
"eslint-config-next": "15.1.6",
29+
"postcss": "8.5.1",
30+
"tailwindcss": "3.4.17",
31+
"typescript": "5.7.3"
32+
}
33+
}

apps/login/postcss.config.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** @type {import('postcss-load-config').Config} */
2+
const config = {
3+
plugins: {
4+
tailwindcss: {},
5+
},
6+
};
7+
8+
export default config;

apps/login/public/twl.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// biome-ignore lint/complexity/useArrowFunction: This is a self-executing function, we do not want to use an arrow function here.
2+
(function () {
3+
const globalSetup = getSetup();
4+
5+
const USER_ADDRESS_KEY = "tw.login:userAddress";
6+
const SESSION_KEY_ADDRESS_KEY = "tw.login:sessionKeyAddress";
7+
const CODE_KEY = "tw.login:code";
8+
9+
function main() {
10+
// check if redirected first, this sets up the logged in state if it was from redirect
11+
const params = parseURLHash(new URL(window.location));
12+
if (params && params.code === localStorage.getItem(CODE_KEY)) {
13+
// reset the URL hash
14+
window.location.hash = "";
15+
// reset the code
16+
localStorage.setItem(CODE_KEY, params.code);
17+
// write the userAddress to local storage
18+
localStorage.setItem(USER_ADDRESS_KEY, params.userAddress);
19+
// write the sessionKeyAddress to local storage
20+
localStorage.setItem(SESSION_KEY_ADDRESS_KEY, params.sessionKeyAddress);
21+
}
22+
23+
const userAddress = localStorage.getItem(USER_ADDRESS_KEY);
24+
const sessionKeyAddress = localStorage.getItem(SESSION_KEY_ADDRESS_KEY);
25+
26+
if (userAddress && sessionKeyAddress) {
27+
// handle logged in state
28+
handleIsLoggedIn();
29+
} else {
30+
// handle not logged in state
31+
handleNotLoggedIn();
32+
}
33+
}
34+
35+
function handleIsLoggedIn() {
36+
console.log("handleIsLoggedIn");
37+
38+
window.thirdweb = {
39+
isLoggedIn: true,
40+
getAddress: () => getAddress(),
41+
logout: () => {
42+
window.localStorage.removeItem(USER_ADDRESS_KEY);
43+
window.localStorage.removeItem(SESSION_KEY_ADDRESS_KEY);
44+
window.location.reload();
45+
},
46+
};
47+
}
48+
49+
function handleNotLoggedIn() {
50+
window.thirdweb = { login: onLogin, isLoggedIn: false };
51+
}
52+
53+
function onLogin() {
54+
const code = window.crypto.getRandomValues(new Uint8Array(4)).join("");
55+
localStorage.setItem(CODE_KEY, code);
56+
// redirect to the login page
57+
const redirect = new URL(globalSetup.baseUrl);
58+
redirect.searchParams.set("code", code);
59+
redirect.searchParams.set("clientId", globalSetup.clientId);
60+
redirect.searchParams.set("redirect", window.location.href);
61+
window.location.href = redirect.href;
62+
}
63+
64+
function getAddress() {
65+
return localStorage.getItem(USER_ADDRESS_KEY);
66+
}
67+
68+
// utils
69+
function getSetup() {
70+
const el = document.currentScript;
71+
if (!el) {
72+
throw new Error("Could not find script element");
73+
}
74+
const baseUrl = new URL(el.src).origin;
75+
const dataset = el.dataset;
76+
const clientId = dataset.clientId;
77+
if (!clientId) {
78+
throw new Error("Missing client-id");
79+
}
80+
return { clientId, baseUrl };
81+
}
82+
83+
/**
84+
* @param {URL} url
85+
* @returns null | { [key: string]: string }
86+
*/
87+
function parseURLHash(url) {
88+
if (!url.hash) {
89+
return null;
90+
}
91+
try {
92+
return decodeHash(url.hash);
93+
} catch {
94+
// if this fails, invalid data -> return null
95+
return null;
96+
}
97+
}
98+
99+
/**
100+
* Decodes a URL hash string to extract the three keys.
101+
*
102+
* @param {string} hash - A string like "#eyJrZXkxIjoiVmFsdWU..."
103+
* @returns {{ userAddress: string, sessionKeyAddress: string, code: string }} An object with the three keys
104+
*/
105+
function decodeHash(hash) {
106+
// Remove the "#" prefix, if present.
107+
const base64Data = hash.startsWith("#") ? hash.slice(1) : hash;
108+
109+
// Decode the Base64 string, then parse the JSON.
110+
const jsonString = atob(base64Data);
111+
const data = JSON.parse(jsonString);
112+
113+
// data should have the shape { userAddress, sessionKeyAddress, code }.
114+
if (
115+
"userAddress" in data &&
116+
"sessionKeyAddress" in data &&
117+
"code" in data
118+
) {
119+
return data;
120+
}
121+
return null;
122+
}
123+
124+
main();
125+
})();

apps/login/public/twl.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)