-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
47 changed files
with
5,901 additions
and
381 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,3 +46,6 @@ test-results | |
|
||
# Synpress | ||
.cache-synpress | ||
|
||
# Next cache | ||
.next |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
BASE_URL= | ||
|
||
DATABASE_URL= | ||
|
||
NEXT_PUBLIC_WALLET_CONNECT_ID= | ||
|
||
NEXTAUTH_SECRET= | ||
NEXTAUTH_URL= | ||
|
||
SUM_SUB_API_KEY= | ||
SUM_SUB_SECRET_KEY= | ||
SUM_SUB_WEBHOOK_SECRET_KEY= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# idOS SumSub provider demo | ||
|
||
A demo application showcasing the "@idos-network/issuer-sdk-js" package. | ||
|
||
|
||
## Getting started. | ||
|
||
1. Clone the repository. | ||
2. Install dependencies with `pnpm install`. | ||
3. Create an `.env.local` file and add the following environment variables: | ||
|
||
``` | ||
BASE_URL=<your-base-url> | ||
DATABASE_URL=<your-postgresql-database-url> | ||
NEXT_PUBLIC_WALLET_CONNECT_ID=<wallet-connect-id> | ||
# SumSub configurations | ||
SUM_SUB_API_KEY= | ||
SUM_SUB_SECRET_KEY= | ||
SUM_SUB_WEBHOOK_SECRET_KEY= | ||
# NextAuth | ||
NEXTAUTH_SECRET= | ||
NEXTAUTH_URL= | ||
# Wallet private key | ||
NEXT_ISSUER_PRIVATE_KEY= | ||
# Issuer (provider keys) | ||
NEXT_ISSUER_SECRET_KEY= | ||
``` | ||
|
||
4. Run the development server with `pnpm dev`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
"use client"; | ||
|
||
import { useRouter } from "next/navigation"; | ||
import { useCallback, useEffect, useState } from "react"; | ||
import { parse } from "uri-template"; | ||
import Button from "../components/Button"; | ||
|
||
const LEVELS = ["basic+liveness", "plus+liveness"]; | ||
|
||
const authLink = (items: Record<string, string | null>) => { | ||
const authTemplate = parse( | ||
`${window.location.origin}{?level,redirect_uri,client,public_encryption_key,grantee}`, | ||
); | ||
|
||
return authTemplate.expand(items); | ||
}; | ||
|
||
export default function All() { | ||
const router = useRouter(); | ||
|
||
const [client, setClient] = useState<string>("Superapp!"); | ||
const [level, setLevel] = useState<string>("basic+liveness"); | ||
const [redirectUri, setRedirectUri] = useState<string>("https://google.com/"); | ||
const [publicEncryptionKey, setPublicEncryptionKey] = useState<string | null>(null); | ||
const [grantee, setGrantee] = useState<string | null>(null); | ||
|
||
const [link, setLink] = useState<string | null>(null); | ||
|
||
useEffect(() => { | ||
const link = authLink({ | ||
client, | ||
level, | ||
redirect_uri: redirectUri, | ||
public_encryption_key: publicEncryptionKey, | ||
grantee, | ||
}); | ||
setLink(link); | ||
}, [client, level, redirectUri, publicEncryptionKey, grantee]); | ||
|
||
const changeLevel = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { | ||
setLevel(e.target.value); | ||
}, []); | ||
|
||
return ( | ||
<div className="h-full bg-slate-400 p-5"> | ||
<h1 className="mb-3 text-2xl">Link builder</h1> | ||
|
||
<h2 className="mb-2">KYC Level</h2> | ||
|
||
<div className="mb-2 flex flex-col gap-1"> | ||
{LEVELS.map((l) => ( | ||
<label key={l} className="block cursor-pointer text-sm"> | ||
<input | ||
type="radio" | ||
name="level" | ||
className="mr-1" | ||
value={l} | ||
checked={level === l} | ||
onChange={changeLevel} | ||
/> | ||
{l} | ||
</label> | ||
))} | ||
</div> | ||
|
||
<h2 className="mb-2">Client name</h2> | ||
|
||
<input | ||
type="text" | ||
className="mb-3 w-2/5 bg-white px-4 py-2" | ||
value={client ?? ""} | ||
onChange={(e) => setClient(e.target.value)} | ||
/> | ||
|
||
<h2 className="mb-2">Redirect URI</h2> | ||
|
||
<input | ||
type="text" | ||
className="mb-3 w-2/5 bg-white px-4 py-2" | ||
value={redirectUri ?? ""} | ||
onChange={(e) => setRedirectUri(e.target.value)} | ||
/> | ||
|
||
<h2 className="mb-2">Grantee DAG - address</h2> | ||
|
||
<input | ||
type="text" | ||
className="mb-3 w-2/5 bg-white px-4 py-2" | ||
value={grantee ?? ""} | ||
onChange={(e) => setGrantee(e.target.value)} | ||
/> | ||
|
||
<h2 className="mb-2">Grantee DAG - Public encryption key</h2> | ||
|
||
<input | ||
type="text" | ||
className="mb-3 w-2/5 bg-white px-4 py-2" | ||
value={publicEncryptionKey ?? ""} | ||
onChange={(e) => setPublicEncryptionKey(e.target.value)} | ||
/> | ||
|
||
<h2 className="mb-3">Generated URL</h2> | ||
|
||
<textarea | ||
className="mb-2 block cursor-default overflow-auto border-2 border-gray-400 bg-white" | ||
disabled={true} | ||
rows={6} | ||
cols={160} | ||
value={link ?? ""} | ||
/> | ||
|
||
<div className="flex flex-row gap-2 align-middle"> | ||
<Button onClick={() => (link ? router.push(link) : "")}>Continue</Button> | ||
<div className="mt-5 mb-3 inline-block">or</div> | ||
<Button | ||
onClick={() => { | ||
window.navigator.clipboard.writeText(link ?? ""); | ||
}} | ||
> | ||
Copy to clipboard | ||
</Button> | ||
</div> | ||
</div> | ||
); | ||
} |
1 change: 1 addition & 0 deletions
1
examples/provider-sumsub-demo/app/api/auth/[...nextauth]/route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { GET, POST } from "../../../auth"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import prisma from "@/app/lib/db"; | ||
import { cookies } from "next/headers"; | ||
import { auth } from "../../auth"; | ||
|
||
export async function GET() { | ||
const cookieStore = await cookies(); | ||
|
||
const client = cookieStore.get("client")?.value; | ||
|
||
if (!client) return Response.json({ error: "Client is missing" }, { status: 404 }); | ||
|
||
const level = cookieStore.get("level")?.value; | ||
const publicEncryptionKey = cookieStore.get("publicEncryptionKey")?.value; | ||
const redirectUri = cookieStore.get("redirectUri")?.value; | ||
const grantee = cookieStore.get("grantee")?.value; | ||
|
||
// Step /init | ||
const application = { | ||
client, | ||
level, | ||
publicEncryptionKey, | ||
redirectUri, | ||
grantee, | ||
}; | ||
|
||
// Step /wallet | ||
const currentUser = await auth(); | ||
|
||
// Fetch DB data | ||
const user = await prisma.user.findFirst({ | ||
// @ts-expect-error Not yet fully typed | ||
where: { address: currentUser?.user?.address }, | ||
}); | ||
|
||
// No DB user means that there is no user logged in | ||
if (!user) { | ||
return Response.json({ application, loggedIn: false }); | ||
} | ||
|
||
return Response.json({ application, user, loggedIn: !!currentUser }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import prisma from "@/app/lib/db"; | ||
import { createCredentials, createHumanProfile, insertDAG } from "@/app/lib/idos/backend"; | ||
import type { Prisma } from "@prisma/client"; | ||
import type { NextRequest } from "next/server"; | ||
import { auth } from "../../auth"; | ||
|
||
export const maxDuration = 60; // 1 minute max duration | ||
|
||
export async function POST(request: NextRequest) { | ||
const currentUser = await auth(); | ||
if (!currentUser?.user) | ||
return Response.json({ error: "User is not authenticated" }, { status: 401 }); | ||
|
||
const data: Record<string, string | number> = await request.json(); | ||
|
||
const update: Prisma.UserUpdateInput = {}; | ||
|
||
// Pick just known keys to be sure | ||
const keys = [ | ||
"idosPubKey", | ||
"idosGrantOwner", | ||
"idosGrantGrantee", | ||
"idosGrantDataId", | ||
"idosGrantLockedUntil", | ||
"idosGrantMessage", | ||
"idosGrantSignature", | ||
]; | ||
|
||
for (const key of keys) { | ||
if (data[key]) update[key] = data[key]; | ||
} | ||
|
||
if (Object.keys(update).length > 0) { | ||
await prisma.user.update({ | ||
// @ts-expect-error Not yet fully typed | ||
where: { address: currentUser.user.address }, | ||
data: update, | ||
}); | ||
} | ||
|
||
// Reload user | ||
const dbUser = await prisma.user.findFirstOrThrow({ | ||
// @ts-expect-error Not yet fully typed | ||
where: { address: currentUser.user.address }, | ||
}); | ||
|
||
// If public key is provided, but no idos profile, then we can create a new one | ||
if (dbUser.idosPubKey && !dbUser.idosHumanId) { | ||
const { idosHumanId, idosWalletId } = await createHumanProfile(dbUser); | ||
|
||
await prisma.user.update({ | ||
// @ts-expect-error Not yet fully typed | ||
where: { address: currentUser.user.address }, | ||
data: { idosHumanId, idosWalletId }, | ||
}); | ||
|
||
// Set to not re-fetch user from the database | ||
dbUser.idosHumanId = idosHumanId; | ||
dbUser.idosWalletId = idosWalletId; | ||
} | ||
|
||
// If data.publicKey is provided, then we can update the user's IDOS human profile | ||
if (dbUser.idosPubKey && !dbUser.idosCredentialId) { | ||
// The object is not re-fetched from the database, so we are passing | ||
// required human id to credentials. | ||
const idosCredentialId = await createCredentials(dbUser); | ||
|
||
await prisma.user.update({ | ||
// @ts-expect-error Not yet fully typed | ||
where: { address: currentUser.user.address }, | ||
data: { idosCredentialId }, | ||
}); | ||
} | ||
|
||
if (!dbUser.idosGrantTransactionId && dbUser.idosGrantMessage && dbUser.idosGrantSignature) { | ||
const transactionId = await insertDAG(dbUser); | ||
|
||
await prisma.user.update({ | ||
// @ts-expect-error Not yet fully typed | ||
where: { address: currentUser.user.address }, | ||
data: { idosGrantTransactionId: transactionId }, | ||
}); | ||
} | ||
|
||
return Response.json({ response: "ok" }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { cookies } from "next/headers"; | ||
import type { NextRequest } from "next/server"; | ||
|
||
export async function POST(request: NextRequest) { | ||
const { client, level, redirectUri, publicEncryptionKey, grantee } = await request.json(); | ||
const cookieStore = await cookies(); | ||
|
||
if (!client || !level || !redirectUri) { | ||
return Response.json({ error: "Client id, level, redirectUri are required." }, { status: 400 }); | ||
} | ||
|
||
cookieStore.set("client", client, { httpOnly: true }); | ||
cookieStore.set("level", level, { httpOnly: true }); | ||
cookieStore.set("redirectUri", redirectUri, { httpOnly: true }); | ||
cookieStore.set("publicEncryptionKey", publicEncryptionKey, { httpOnly: true }); | ||
cookieStore.set("grantee", grantee, { httpOnly: true }); | ||
|
||
return Response.json({}, { status: 202 }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { createAccessToken } from "@/app/lib/sumSub"; | ||
import { cookies } from "next/headers"; | ||
import { auth } from "../../auth"; | ||
|
||
export async function POST() { | ||
const cookieStore = await cookies(); | ||
|
||
const level = cookieStore.get("level")?.value; | ||
if (!level) return Response.json({ error: "Level is missing" }, { status: 404 }); | ||
|
||
const currentUser = await auth(); | ||
if (!currentUser?.user) | ||
return Response.json({ error: "User is not authenticated" }, { status: 401 }); | ||
|
||
// @ts-expect-error Not yet fully typed | ||
const sumSubToken = await createAccessToken(currentUser.user.address, level ?? "basic+liveness"); | ||
|
||
return Response.json({ token: sumSubToken }); | ||
} |
Oops, something went wrong.