Compare commits
9 Commits
95566d6f36
...
293180c2b5
Author | SHA1 | Date | |
---|---|---|---|
|
293180c2b5 | ||
|
24b83ec99e | ||
|
879f7aad3f | ||
|
efc2b41f9e | ||
|
7652938c74 | ||
|
b8012cf9fd | ||
|
978e438854 | ||
|
9aa3bfc0d2 | ||
|
042f9209ed |
10
package.json
10
package.json
@ -21,9 +21,12 @@
|
||||
"@types/cookie": "^0.5.1",
|
||||
"@types/js-cookie": "^3.0.3",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"concurrently": "^7.6.0",
|
||||
"postcss": "^8.4.29",
|
||||
"svelte": "^3.54.0",
|
||||
"svelte-check": "^3.0.1",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.2.0",
|
||||
@ -32,6 +35,7 @@
|
||||
"dependencies": {
|
||||
"@graphql-tools/graphql-file-loader": "^8.0.0",
|
||||
"@graphql-tools/load": "^8.0.0",
|
||||
"@iconify/icons-ion": "^1.2.10",
|
||||
"@iconify/svelte": "^3.1.4",
|
||||
"@lit-protocol/auth-helpers": "^2.2.50",
|
||||
"@lit-protocol/constants": "^2.2.50",
|
||||
@ -40,8 +44,10 @@
|
||||
"@lit-protocol/pkp-client": "^2.2.50",
|
||||
"@lit-protocol/types": "^2.2.50",
|
||||
"@tanstack/svelte-query": "^4.29.1",
|
||||
"@wagmi/core": "^1.3.9",
|
||||
"@wundergraph/sdk": "^0.174.5",
|
||||
"@wundergraph/svelte-query": "^0.3.10",
|
||||
"@xstate/svelte": "^2.1.0",
|
||||
"axios": "^1.4.0",
|
||||
"cookie": "^0.5.0",
|
||||
"dotenv": "^16.3.1",
|
||||
@ -52,7 +58,9 @@
|
||||
"jwks-rsa": "^3.0.1",
|
||||
"node-jose": "^2.2.0",
|
||||
"path": "^0.12.7",
|
||||
"url": "^0.11.1"
|
||||
"svelte-kit-cookie-session": "^4.0.0",
|
||||
"url": "^0.11.1",
|
||||
"xstate": "^4.38.2"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
1035
pnpm-lock.yaml
1035
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
3
src/app.css
Normal file
3
src/app.css
Normal file
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
17
src/app.d.ts
vendored
17
src/app.d.ts
vendored
@ -1,12 +1,23 @@
|
||||
import type { Session } from 'svelte-kit-cookie-session';
|
||||
|
||||
type SessionData = {
|
||||
views: number;
|
||||
};
|
||||
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
interface Locals {
|
||||
session: Session<SessionData>;
|
||||
}
|
||||
interface PageData {
|
||||
// can add any properties here, return it from your root layout
|
||||
session: SessionData;
|
||||
}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
export { };
|
5
src/hooks.server.ts
Normal file
5
src/hooks.server.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { handleSession } from 'svelte-kit-cookie-session';
|
||||
|
||||
export const handle = handleSession({
|
||||
secret: 'SOME_COMPLEX_SECRET_32_CHARSLONG'
|
||||
});
|
@ -1,179 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import {
|
||||
isSignInRedirect,
|
||||
getProviderFromUrl,
|
||||
} from "@lit-protocol/lit-auth-client";
|
||||
import type { IRelayPKP } from "@lit-protocol/types";
|
||||
import Icon from "@iconify/svelte";
|
||||
import { createLitSession } from "./createLitSession";
|
||||
import { connectProvider } from "./setupLit";
|
||||
import Signer from "./Signer.svelte";
|
||||
|
||||
const redirectUri = "http://localhost:3000/";
|
||||
|
||||
let sessionSigs = null;
|
||||
let error, currentPKP, authMethod, provider;
|
||||
let status = "Initializing...";
|
||||
let pkps: IRelayPKP[] = [];
|
||||
let view = "SIGN_IN";
|
||||
let sessionStatuses;
|
||||
let activeSession = null;
|
||||
|
||||
let messageToSign = { user: "Sam", loggedIn: true };
|
||||
|
||||
onMount(async () => {
|
||||
// Load activeSession from local storage
|
||||
const storedSession = localStorage.getItem("google-session");
|
||||
const storedPKP = localStorage.getItem("current-pkp");
|
||||
if (storedSession && storedPKP) {
|
||||
activeSession = JSON.parse(storedSession);
|
||||
currentPKP = JSON.parse(storedPKP);
|
||||
view = "READY";
|
||||
}
|
||||
initialize();
|
||||
});
|
||||
|
||||
$: if (sessionSigs) {
|
||||
// Store sessionSigs in local storage in its original format
|
||||
localStorage.setItem("google-session", JSON.stringify(sessionSigs));
|
||||
|
||||
// Update sessionStatuses
|
||||
sessionStatuses = Object.entries(sessionSigs).map(([node, data]) => {
|
||||
const sessionKey = JSON.parse(data.signedMessage).sessionKey;
|
||||
const expiration = new Date(JSON.parse(data.signedMessage).expiration);
|
||||
const isExpired = expiration < new Date();
|
||||
return {
|
||||
node,
|
||||
sessionKey,
|
||||
expiration: expiration.toISOString(),
|
||||
isExpired,
|
||||
};
|
||||
});
|
||||
|
||||
// Find an active session
|
||||
activeSession = sessionStatuses.find(({ isExpired }) => !isExpired);
|
||||
|
||||
view = "READY";
|
||||
}
|
||||
|
||||
async function initialize() {
|
||||
status = "Connecting to Google provider...";
|
||||
try {
|
||||
provider = await connectProvider();
|
||||
status = "Connected to Google provider.";
|
||||
if (isSignInRedirect(redirectUri)) {
|
||||
const providerName = getProviderFromUrl();
|
||||
if (providerName) {
|
||||
await handleRedirect(providerName);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async function authWithGoogle() {
|
||||
try {
|
||||
if (!provider) {
|
||||
provider = await connectProvider();
|
||||
status = "Reconnected to Google provider.";
|
||||
}
|
||||
await provider.signIn();
|
||||
status = "Signing in with Google...";
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRedirect(providerName: string) {
|
||||
try {
|
||||
if (!provider) throw new Error("Invalid provider.");
|
||||
authMethod = await provider.authenticate();
|
||||
status = "Authenticated successfully.";
|
||||
pkps = await provider.fetchPKPsThroughRelayer(authMethod);
|
||||
status = "Fetching your Google PKP...";
|
||||
if (pkps.length === 0) {
|
||||
status = "No PKPs found. Minting new PKP...";
|
||||
await mint();
|
||||
} else {
|
||||
// Use the first PKP directly
|
||||
await createSession(pkps[0]);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async function mint() {
|
||||
const newPKP: IRelayPKP = await mintPkp(provider, authMethod);
|
||||
pkps = [...pkps, newPKP];
|
||||
status = "New PKP minted.";
|
||||
await createSession(newPKP);
|
||||
}
|
||||
|
||||
async function createSession(pkp: IRelayPKP) {
|
||||
try {
|
||||
currentPKP = pkp; // Assign the selected PKP to currentPKP
|
||||
createLitSession(provider, pkp.publicKey, authMethod).then((sigs) => {
|
||||
sessionSigs = sigs;
|
||||
// Store sessionSigs and currentPKP in localStorage
|
||||
localStorage.setItem("google-signature", JSON.stringify(sessionSigs));
|
||||
localStorage.setItem("current-pkp", JSON.stringify(currentPKP));
|
||||
});
|
||||
status = "Session created successfully.";
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
}
|
||||
}
|
||||
|
||||
function setError(err) {
|
||||
error = err;
|
||||
view = "ERROR";
|
||||
status = `Error: ${err.message}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex items-center justify-center h-screen">
|
||||
<div>
|
||||
{#if error}
|
||||
<div class="mt-4 text-center">
|
||||
<h1>Error</h1>
|
||||
<p>{error.message}</p>
|
||||
</div>
|
||||
{:else if view === "SIGN_IN"}
|
||||
<button on:click={authWithGoogle} class="btn variant-filled">
|
||||
<span><Icon icon="flat-color-icons:google" /></span>
|
||||
<span>Sign in with Google</span>
|
||||
</button>
|
||||
{/if}
|
||||
{#if view === "READY"}
|
||||
<div>
|
||||
<h3>Your PKP Address:</h3>
|
||||
<p>{currentPKP.ethAddress}</p>
|
||||
</div>
|
||||
Signer
|
||||
<Signer {messageToSign} />
|
||||
Sessions
|
||||
{/if}
|
||||
<div class="mt-4 text-center">
|
||||
<p>{status}</p>
|
||||
</div>
|
||||
{#if activeSession}
|
||||
<div>
|
||||
<h3>Active Session:</h3>
|
||||
<p>Node: {activeSession.node}</p>
|
||||
<p>Session Key: {activeSession.sessionKey}</p>
|
||||
<p>Expiration: {activeSession.expiration}</p>
|
||||
</div>
|
||||
{#if sessionStatuses}
|
||||
{#each sessionStatuses as { node, sessionKey, expiration, isExpired }}
|
||||
<p>
|
||||
{isExpired ? "🔴" : "🟢"} Node: {node}, Session Key: {sessionKey},
|
||||
Expiration: {expiration}
|
||||
</p>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
@ -2,7 +2,17 @@
|
||||
import Cookies from "js-cookie";
|
||||
|
||||
async function login() {
|
||||
const response = await fetch("/api/auth", { method: "POST" });
|
||||
const response = await fetch("/api/auth", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: "Sam",
|
||||
loggedIn: true,
|
||||
roles: ["admin"],
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
alert("Login failed");
|
||||
} else {
|
||||
|
@ -1,106 +0,0 @@
|
||||
<!-- SignVerifyMessage.svelte -->
|
||||
<script lang="ts">
|
||||
import { ethers } from "ethers";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
export let messageToSign = {};
|
||||
|
||||
let currentPKP;
|
||||
let sessionSigs;
|
||||
let status = "";
|
||||
let litNodeClient;
|
||||
let messageSignature;
|
||||
|
||||
onMount(async () => {
|
||||
litNodeClient = new LitNodeClient({ litNetwork: "serrano" });
|
||||
await litNodeClient.connect();
|
||||
|
||||
const sessionSigsLocalStorage = localStorage.getItem("google-session");
|
||||
const currentPKPLocalStorage = localStorage.getItem("current-pkp");
|
||||
if (sessionSigsLocalStorage && currentPKPLocalStorage) {
|
||||
sessionSigs = JSON.parse(sessionSigsLocalStorage);
|
||||
currentPKP = JSON.parse(currentPKPLocalStorage);
|
||||
}
|
||||
});
|
||||
|
||||
export async function signMessageWithPKP() {
|
||||
const userConfirmed = window.confirm(
|
||||
"Do you want to sign the following message?\n\n" +
|
||||
JSON.stringify(messageToSign, null, 2)
|
||||
);
|
||||
|
||||
if (!userConfirmed) {
|
||||
status = "User did not allow to sign the message.";
|
||||
dispatch("status", status);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Create a specific JSON object
|
||||
const jsonString = JSON.stringify(messageToSign);
|
||||
|
||||
// Convert the JSON string to an array of character codes
|
||||
const toSign = ethers.getBytes(ethers.hashMessage(jsonString));
|
||||
|
||||
const litActionCode = `
|
||||
const go = async () => {
|
||||
const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName });
|
||||
};
|
||||
go();
|
||||
`;
|
||||
|
||||
// Sign message
|
||||
const results = await litNodeClient.executeJs({
|
||||
code: litActionCode,
|
||||
sessionSigs: sessionSigs,
|
||||
jsParams: {
|
||||
toSign: toSign,
|
||||
publicKey: currentPKP.publicKey,
|
||||
sigName: "sig1",
|
||||
},
|
||||
});
|
||||
|
||||
// Get signature
|
||||
const result = results.signatures["sig1"];
|
||||
messageSignature = ethers.Signature.from({
|
||||
r: "0x" + result.r,
|
||||
s: "0x" + result.s,
|
||||
v: result.recid,
|
||||
});
|
||||
|
||||
// Display the signed JSON
|
||||
status = JSON.stringify(messageToSign, null, 2);
|
||||
|
||||
// Verify the signature
|
||||
const recoveredAddr = ethers.verifyMessage(jsonString, messageSignature);
|
||||
|
||||
// Check if the address associated with the signature is the same as the current PKP
|
||||
const verified =
|
||||
currentPKP.ethAddress.toLowerCase() === recoveredAddr.toLowerCase();
|
||||
|
||||
if (verified) {
|
||||
status = "The signature is valid.";
|
||||
} else {
|
||||
status = "The signature is invalid.";
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={signMessageWithPKP}>Sign Message</button>
|
||||
{#if messageToSign}
|
||||
<pre>{JSON.stringify(messageToSign)}</pre>
|
||||
{/if}
|
||||
|
||||
{#if status}
|
||||
<div class="mt-4 text-center">
|
||||
<p>Status: {status}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if messageSignature}
|
||||
<div class="mt-4 text-center">
|
||||
<h3>Signature</h3>
|
||||
<pre>{JSON.stringify(messageSignature)}</pre>
|
||||
</div>
|
||||
{/if}
|
89
src/lib/Wallet.svelte
Normal file
89
src/lib/Wallet.svelte
Normal file
@ -0,0 +1,89 @@
|
||||
<script>
|
||||
import { useMachine } from "@xstate/svelte";
|
||||
import walletMachine from "./machines/walletMachine";
|
||||
import { onMount } from "svelte";
|
||||
import Icon from "@iconify/svelte";
|
||||
|
||||
import {
|
||||
signInWithGoogle,
|
||||
startSignIn as startSignInService,
|
||||
} from "./services/signInWithGoogle";
|
||||
|
||||
const { state, send } = useMachine(walletMachine);
|
||||
|
||||
$: {
|
||||
if ($state.context.pkps && $state.context.sessionSigs) {
|
||||
localStorage.setItem(
|
||||
"me",
|
||||
JSON.stringify({
|
||||
pkps: $state.context.pkps,
|
||||
sessionSigs: $state.context.sessionSigs,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
onMount(() => {
|
||||
const me = JSON.parse(localStorage.getItem("me"));
|
||||
if (me && me.pkps && me.sessionSigs) {
|
||||
send({ type: "RELOAD", ...me });
|
||||
}
|
||||
});
|
||||
|
||||
async function startSignIn() {
|
||||
startSignInService.set(true);
|
||||
await signInWithGoogle();
|
||||
}
|
||||
function clearSession() {
|
||||
send("LOGOUT");
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $state.matches("sessionAvailable") || $state.matches("creatingSession") || $state.matches("signIn")}
|
||||
{#if $state.matches("signIn")}
|
||||
<div class="w-1/3">
|
||||
<button
|
||||
on:click={startSignIn}
|
||||
class="w-full py-2 text-white bg-blue-500 rounded hover:bg-blue-700 flex items-center justify-center"
|
||||
>
|
||||
<span class="mr-2"><Icon icon="flat-color-icons:google" /></span>
|
||||
<span>Sign in with Google</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else if $state.context.pkps}
|
||||
<div
|
||||
class="fixed bottom-0 left-0 right-0 p-3 bg-white bg-opacity-75 rounded-t-lg shadow-md flex flex-col items-center space-y-4"
|
||||
>
|
||||
<div class="w-full flex items-center justify-between space-x-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div>
|
||||
<p class="text-sm">
|
||||
<span class="font-semibold">Address:</span>
|
||||
{$state.context.pkps[0].ethAddress}
|
||||
</p>
|
||||
<p class="text-xs">
|
||||
<span class="font-semibold">Provider:</span>
|
||||
{$state.context.providerName}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
on:click={clearSession}
|
||||
class="py-1 px-2 text-white bg-red-500 rounded hover:bg-red-700"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else if $state.matches("sessionExpired")}
|
||||
<div class="bg-white p-10">
|
||||
<p>Error creating session. Please try again.</p>
|
||||
<pre>{JSON.stringify($state.context.error, null, 2)}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="bg-white p-10 rounded-full">
|
||||
<div class="bg-white rounded-full p-5 animate-spin">
|
||||
<Icon icon="la:spinner" width="100" height="100" />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
116
src/lib/machines/walletMachine.ts
Normal file
116
src/lib/machines/walletMachine.ts
Normal file
@ -0,0 +1,116 @@
|
||||
// src/lib/machines/walletMachine.ts
|
||||
import { createMachine, assign } from 'xstate';
|
||||
import { signInWithGoogle } from '../services/signInWithGoogle';
|
||||
import { createSession } from '../services/createSession';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
const walletMachine = createMachine({
|
||||
id: 'wallet',
|
||||
initial: 'signIn',
|
||||
context: {
|
||||
provider: null,
|
||||
providerName: null,
|
||||
authMethod: null,
|
||||
pkps: [],
|
||||
sessionSigs: null,
|
||||
redirect: false
|
||||
},
|
||||
states: {
|
||||
signIn: {
|
||||
on: {
|
||||
RELOAD: {
|
||||
target: 'sessionAvailable',
|
||||
actions: assign({
|
||||
pkps: (_, event) => event.pkps,
|
||||
sessionSigs: (_, event) => event.sessionSigs,
|
||||
}),
|
||||
},
|
||||
},
|
||||
invoke: {
|
||||
src: 'signInWithGoogle',
|
||||
onDone: {
|
||||
target: 'authenticated',
|
||||
actions: assign({
|
||||
providerName: (_, event) => event.data.providerName,
|
||||
provider: (_, event) => event.data.provider,
|
||||
authMethod: (_, event) => event.data.authMethod,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
authenticated: {
|
||||
invoke: {
|
||||
src: async (context) => {
|
||||
const pkps = await context.provider.fetchPKPsThroughRelayer(context.authMethod);
|
||||
if (pkps.length === 0) {
|
||||
const newPKP = await context.provider.mintPKP(context.authMethod);
|
||||
pkps.push(newPKP);
|
||||
}
|
||||
return pkps;
|
||||
},
|
||||
onDone: {
|
||||
target: 'creatingSession',
|
||||
actions: assign({
|
||||
pkps: (_, event) => event.data,
|
||||
}),
|
||||
},
|
||||
onError: 'authenticated',
|
||||
},
|
||||
},
|
||||
creatingSession: {
|
||||
invoke: {
|
||||
src: async (context) => {
|
||||
const { pkps, sessionSigs } = await createSession(context.provider, context.authMethod, context.pkps);
|
||||
return { pkps, sessionSigs };
|
||||
},
|
||||
onDone: {
|
||||
target: 'sessionAvailable',
|
||||
actions: [
|
||||
assign({
|
||||
pkps: (_, event) => event.data.pkps,
|
||||
sessionSigs: (_, event) => event.data.sessionSigs,
|
||||
}),
|
||||
(context) => console.log('Context after creating session:', context), // Debug log
|
||||
],
|
||||
},
|
||||
onError: {
|
||||
target: 'sessionExpired',
|
||||
actions: assign({
|
||||
error: (_, event) => event.data,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
sessionAvailable: {
|
||||
on: {
|
||||
EXPIRED: {
|
||||
target: 'sessionExpired',
|
||||
cond: (context) => context.sessionSigs && Object.values(context.sessionSigs).every(sig => sig.expired)
|
||||
},
|
||||
LOGOUT: 'sessionExpired'
|
||||
}
|
||||
},
|
||||
sessionExpired: {
|
||||
entry: assign({
|
||||
sessionSigs: null,
|
||||
redirect: true
|
||||
}),
|
||||
after: {
|
||||
0: {
|
||||
target: 'signIn',
|
||||
actions: () => {
|
||||
localStorage.removeItem('me');
|
||||
window.location.href = '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}, {
|
||||
services: {
|
||||
signInWithGoogle,
|
||||
createSession
|
||||
},
|
||||
});
|
||||
|
||||
export default walletMachine;
|
18
src/lib/mintPkp.ts
Normal file
18
src/lib/mintPkp.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import type { IRelayPKP } from '@lit-protocol/types';
|
||||
import type { IProvider } from '$lib/IProvider';
|
||||
|
||||
export async function mintPkp(provider: IProvider, authMethod: any): Promise<IRelayPKP> {
|
||||
|
||||
const txHash: string = await provider.mintPKPThroughRelayer(authMethod);
|
||||
const response = await provider.relay.pollRequestUntilTerminalState(txHash);
|
||||
if (response.status !== 'Succeeded') {
|
||||
throw new Error('Minting failed');
|
||||
}
|
||||
const newPKP: IRelayPKP = {
|
||||
tokenId: response.pkpTokenId,
|
||||
publicKey: response.pkpPublicKey,
|
||||
ethAddress: response.pkpEthAddress
|
||||
};
|
||||
|
||||
return newPKP;
|
||||
}
|
30
src/lib/services/createSession.ts
Normal file
30
src/lib/services/createSession.ts
Normal file
@ -0,0 +1,30 @@
|
||||
// src/lib/services/createSession.ts
|
||||
import { createLitSession } from '$lib/createLitSession';
|
||||
import type { IRelayPKP } from "@lit-protocol/types";
|
||||
|
||||
export const createSession = async (provider, authMethod, pkps: IRelayPKP[]) => {
|
||||
try {
|
||||
let currentPKP;
|
||||
if (pkps.length === 0) {
|
||||
currentPKP = await provider.mintPKP(authMethod);
|
||||
pkps = [...pkps, currentPKP];
|
||||
} else {
|
||||
currentPKP = pkps[0];
|
||||
}
|
||||
|
||||
console.log('Current PKP:', currentPKP); // Debug log
|
||||
|
||||
const sessionSigs = await createLitSession(
|
||||
provider,
|
||||
currentPKP.publicKey,
|
||||
authMethod
|
||||
);
|
||||
|
||||
console.log('Session Signatures:', sessionSigs); // Debug log
|
||||
|
||||
return { pkps, sessionSigs };
|
||||
} catch (error) {
|
||||
console.error('Error in createSession:', error); // Debug log
|
||||
throw new Error(`Failed to create session: ${error.message}`);
|
||||
}
|
||||
};
|
38
src/lib/services/signInWithGoogle.ts
Normal file
38
src/lib/services/signInWithGoogle.ts
Normal file
@ -0,0 +1,38 @@
|
||||
// src/lib/services/signInWithGoogle.ts
|
||||
import { connectProvider } from "$lib/setupLit";
|
||||
import { isSignInRedirect, getProviderFromUrl } from "@lit-protocol/lit-auth-client";
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export let startSignIn = writable(false);
|
||||
|
||||
let providerName;
|
||||
|
||||
export const signInWithGoogle = async () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
try {
|
||||
let provider = await connectProvider();
|
||||
if (isSignInRedirect("http://localhost:3000/")) {
|
||||
providerName = getProviderFromUrl();
|
||||
if (providerName) {
|
||||
const authMethod = await provider.authenticate();
|
||||
return { authMethod, provider, providerName };
|
||||
}
|
||||
} else {
|
||||
let shouldStartSignIn = false;
|
||||
startSignIn.subscribe(value => {
|
||||
shouldStartSignIn = value;
|
||||
});
|
||||
if (!providerName && shouldStartSignIn) {
|
||||
await provider.signIn();
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error during sign-in:', err);
|
||||
throw err;
|
||||
} finally {
|
||||
startSignIn.set(false);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Cannot sign in with Google in a non-browser environment.");
|
||||
}
|
||||
};
|
58
src/lib/setupChainProvider.ts
Normal file
58
src/lib/setupChainProvider.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { configureChains, createConfig } from '@wagmi/core';
|
||||
import { gnosis } from '@wagmi/core/chains';
|
||||
import { publicProvider } from '@wagmi/core/providers/public';
|
||||
import { InjectedConnector } from '@wagmi/core/connectors/injected';
|
||||
import { jsonRpcProvider } from '@wagmi/core/providers/jsonRpc';
|
||||
|
||||
// const chronicleChain = {
|
||||
// id: 175177,
|
||||
// name: 'Chronicle',
|
||||
// network: 'chronicle',
|
||||
|
||||
// nativeCurrency: {
|
||||
// decimals: 18,
|
||||
// name: 'Chronicle - Lit Protocol Testnet',
|
||||
// symbol: 'LIT'
|
||||
// },
|
||||
// rpcUrls: {
|
||||
// default: {
|
||||
// http: ['https://chain-rpc.litprotocol.com/http']
|
||||
// },
|
||||
// public: {
|
||||
// http: ['https://chain-rpc.litprotocol.com/http']
|
||||
// }
|
||||
// },
|
||||
// blockExplorers: {
|
||||
// default: {
|
||||
// name: 'Chronicle - Lit Protocol Testnet',
|
||||
// url: 'https://chain.litprotocol.com'
|
||||
// }
|
||||
// },
|
||||
// testnet: true
|
||||
// };
|
||||
|
||||
export function initChainProvider() {
|
||||
const { chains, publicClient } = configureChains(
|
||||
[gnosis],
|
||||
[
|
||||
jsonRpcProvider({
|
||||
rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] })
|
||||
}),
|
||||
jsonRpcProvider({
|
||||
rpc: () => ({
|
||||
http: `https://rpc.ankr.com/gnosis`,
|
||||
wss: `wss://rpc.gnosischain.com/wss`
|
||||
})
|
||||
}),
|
||||
publicProvider()
|
||||
]
|
||||
);
|
||||
createConfig({
|
||||
autoConnect: true,
|
||||
connectors: [
|
||||
new InjectedConnector({ chains }),
|
||||
],
|
||||
publicClient
|
||||
});
|
||||
}
|
||||
|
11
src/lib/stores.js
Normal file
11
src/lib/stores.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const signRequest = writable({json: {}});
|
||||
|
||||
export const signedMessages = writable([])
|
||||
|
||||
export const redirectStore = writable(false);
|
||||
|
||||
export const googleSession = writable({
|
||||
activeSession: false
|
||||
});
|
@ -1,18 +1,43 @@
|
||||
<script lang="ts">
|
||||
import "../app.css";
|
||||
import { QueryClientProvider } from "@tanstack/svelte-query";
|
||||
import type { LayoutData } from "./$types";
|
||||
import { client } from "$lib/wundergraph";
|
||||
import Cookies from "js-cookie";
|
||||
import { onMount } from "svelte";
|
||||
import { initChainProvider } from "$lib/setupChainProvider";
|
||||
import { googleSession } from "$lib/stores.js";
|
||||
import Wallet from "$lib/Wallet.svelte";
|
||||
|
||||
let activeSession = false;
|
||||
|
||||
export let data: LayoutData;
|
||||
|
||||
const token = Cookies.get("token");
|
||||
|
||||
googleSession.subscribe((value) => {
|
||||
activeSession = value.activeSession;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
initChainProvider();
|
||||
});
|
||||
|
||||
if (token) {
|
||||
client.setAuthorizationToken(token);
|
||||
}
|
||||
</script>
|
||||
|
||||
<QueryClientProvider client={data.queryClient}>
|
||||
<slot />
|
||||
</QueryClientProvider>
|
||||
<div
|
||||
class="flex items-center justify-center h-screen bg-cover bg-center"
|
||||
style="background-image: url('lake.jpeg');"
|
||||
>
|
||||
<QueryClientProvider client={data.queryClient}>
|
||||
<Wallet />
|
||||
<!-- <GoogleSession />
|
||||
<div class="text-lg bg-white">{activeSession}</div> -->
|
||||
<slot />
|
||||
<!-- {#if activeSession}active {:else} expired {/if}
|
||||
<GooglePKP /> -->
|
||||
</QueryClientProvider>
|
||||
</div>
|
||||
|
@ -10,6 +10,5 @@ export const load: LayoutLoad = async () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return { queryClient };
|
||||
};
|
||||
|
10
src/routes/+page.server.ts
Normal file
10
src/routes/+page.server.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { ServerLoad } from '@sveltejs/kit';
|
||||
|
||||
export const load: ServerLoad = async ({ locals }) => {
|
||||
await locals.session.set({ myPKP: "hello1" });
|
||||
|
||||
return {
|
||||
myPKP: locals.session.data.myPKP
|
||||
};
|
||||
};
|
||||
|
@ -1,5 +1,14 @@
|
||||
<script>
|
||||
import GoogleAuth from "$lib/GoogleAuth.svelte";
|
||||
<script lang="ts">
|
||||
// import { signRequest } from "$lib/stores.js";
|
||||
|
||||
// function trigger() {
|
||||
// signRequest.set({ json: { hello: "test" } });
|
||||
// }
|
||||
|
||||
// import type { PageData } from "./$types";
|
||||
// export let data: PageData;
|
||||
</script>
|
||||
|
||||
<GoogleAuth />
|
||||
<!-- <div class="bg-white">myPKP {data.myPKP}</div> -->
|
||||
|
||||
<!-- <button on:click={trigger}>Sign Request</button> -->
|
||||
|
@ -1,10 +1,11 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
const secretKey = 'mysecrettestkey';
|
||||
const secretKey = process.env.JWT_KEY;
|
||||
|
||||
export async function POST() {
|
||||
const token = jwt.sign({ name: 'Samuel', loggedIn: true, roles: ['admin'] }, secretKey);
|
||||
export async function POST({ request }) {
|
||||
const user = await request.json();
|
||||
const token = jwt.sign(user, secretKey);
|
||||
if (!token) {
|
||||
throw error(400, 'No token created.');
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
const secretKey = 'mysecrettestkey';
|
||||
const secretKey = process.env.JWT_KEY;
|
||||
|
||||
export async function GET({ request }) {
|
||||
const authHeader = request.headers.get('Authorization');
|
||||
|
13
src/routes/api/login/+server.js
Normal file
13
src/routes/api/login/+server.js
Normal file
@ -0,0 +1,13 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
const secretKey = process.env.JWT_KEY;
|
||||
|
||||
export async function POST({ request }) {
|
||||
const user = await request.json();
|
||||
const token = jwt.sign(user, secretKey);
|
||||
if (!token) {
|
||||
throw error(400, 'No token created.');
|
||||
}
|
||||
return new Response(JSON.stringify({ token }), { status: 200 });
|
||||
}
|
15
src/routes/api/verify/+server.ts
Normal file
15
src/routes/api/verify/+server.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
export async function POST({ request }) {
|
||||
const { messageToSign, messageSignature, currentPKP } = await request.json();
|
||||
|
||||
// Verify the signature
|
||||
const jsonString = JSON.stringify(messageToSign);
|
||||
const recoveredAddr = ethers.verifyMessage(jsonString, messageSignature);
|
||||
|
||||
// Check if the address associated with the signature is the same as the current PKP
|
||||
const verified = currentPKP.ethAddress.toLowerCase() === recoveredAddr.toLowerCase();
|
||||
|
||||
return json({ verified }, { status: 200 });
|
||||
}
|
BIN
static/lake.jpeg
Normal file
BIN
static/lake.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
@ -13,6 +13,7 @@ const config = {
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter(),
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
9
tailwind.config.js
Normal file
9
tailwind.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import { fetchSchema } from './.wundergraph/schemas/fetch-schemas';
|
||||
import { fetchSchemas } from './.wundergraph/schemas/fetch-schemas';
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
|
Loading…
Reference in New Issue
Block a user