Skip to content

Commit

Permalink
get engine public jwk (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel authored Apr 3, 2024
1 parent f1e8bb9 commit 5cc5e0d
Show file tree
Hide file tree
Showing 17 changed files with 491 additions and 168 deletions.
6 changes: 3 additions & 3 deletions apps/devtool/src/app/_components/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const NavBar = () => {
<Link href="/policy-engine" className={`${currentPath === '/policy-engine' ? 'underline' : ''}`}>
Policy Engine
</Link>
<Link href="/vault" className={`${currentPath === '/vault' ? 'underline' : ''}`}>
Vault
</Link>
<Link href="/data-store" className={`${currentPath === '/data-store' ? 'underline' : ''}`}>
Data Store
</Link>
Expand All @@ -32,9 +35,6 @@ const NavBar = () => {
>
Transaction Request
</Link>
<Link href="/vault" className={`${currentPath === '/vault' ? 'underline' : ''}`}>
Vault
</Link>
</div>
</div>
<div className="flex flex-row-reverse gap-2 flex-1">
Expand Down
72 changes: 72 additions & 0 deletions apps/devtool/src/app/_hooks/useAccountSignature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
Curves,
JwsdHeader,
KeyTypes,
PublicKey,
SigningAlg,
hash,
hexToBase64Url,
signJwsd,
signJwt
} from '@narval/signature'
import { signMessage } from '@wagmi/core'
import { useEffect, useState } from 'react'
import { useAccount } from 'wagmi'
import { config } from '../_lib/config'

const useAccountSignature = () => {
const account = useAccount()
const [jwk, setJwk] = useState<PublicKey>()

useEffect(() => {
if (!account.address) return

setJwk({
kty: KeyTypes.EC,
crv: Curves.SECP256K1,
alg: SigningAlg.ES256K,
kid: account.address,
addr: account.address
})
}, [account.address])

const signer = async (message: string) => {
const signature = await signMessage(config, { message })

return hexToBase64Url(signature)
}

const signAccountJwt = async (payload: any) => {
if (!jwk) return ''

const signature = await signJwt(payload, jwk, { alg: SigningAlg.EIP191 }, signer)

return signature
}

const signAccountJwsd = async (payload: any, accessToken: string) => {
if (!jwk) return ''

const jwsdHeader: JwsdHeader = {
alg: SigningAlg.EIP191,
kid: jwk.kid,
typ: 'gnap-binding-jwsd',
htm: 'POST',
uri: 'https://armory.narval.xyz/sign',
created: new Date().getTime(),
ath: hexToBase64Url(`0x${hash(accessToken)}`)
}

const signature = await signJwsd(payload, jwsdHeader, signer).then((jws) => {
const parts = jws.split('.')
parts[1] = ''
return parts.join('.')
})

return signature
}

return { jwk, signAccountJwt, signAccountJwsd }
}

export default useAccountSignature
13 changes: 10 additions & 3 deletions apps/devtool/src/app/_hooks/useStore.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { PublicKey } from '@narval/signature'
import { useLocalStorage } from 'usehooks-ts'
import { DATA_STORE_URL, ENGINE_URL, LOCAL_STORAGE_KEYS, VAULT_URL } from '../_lib/constants'

const useStore = () => {
const [engineUrl, setEngineUrl] = useLocalStorage(LOCAL_STORAGE_KEYS.engineUrl, ENGINE_URL)
const [engineApiKey, setEngineApiKey] = useLocalStorage(LOCAL_STORAGE_KEYS.engineApiKey, '')
const [enginePublicJwk, setEnginePublicJwk] = useLocalStorage<PublicKey | undefined>(
LOCAL_STORAGE_KEYS.enginePublicJwk,
undefined
)
const [engineAdminApiKey, setEngineAdminApiKey] = useLocalStorage(LOCAL_STORAGE_KEYS.engineAdminApiKey, '')
const [engineClientId, setEngineClientId] = useLocalStorage(LOCAL_STORAGE_KEYS.engineClientId, '')
const [engineClientSecret, setEngineClientSecret] = useLocalStorage(LOCAL_STORAGE_KEYS.engineClientSecret, '')

Expand Down Expand Up @@ -32,8 +37,10 @@ const useStore = () => {
return {
engineUrl,
setEngineUrl,
engineApiKey,
setEngineApiKey,
enginePublicJwk,
setEnginePublicJwk,
engineAdminApiKey,
setEngineAdminApiKey,
engineClientId,
setEngineClientId,
engineClientSecret,
Expand Down
3 changes: 2 additions & 1 deletion apps/devtool/src/app/_lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ export const VAULT_URL = 'http://127.0.0.1:3011'

export const LOCAL_STORAGE_KEYS = {
engineUrl: 'narvalEngineUrl',
engineApiKey: 'narvalEngineApiKey',
enginePublicJwk: 'narvalEnginePublicJwk',
engineAdminApiKey: 'narvalEngineAdminApiKey',
engineClientId: 'narvalEngineClientId',
engineClientSecret: 'narvalEngineClientSecret',
vaultUrl: 'narvalVaultUrl',
Expand Down
13 changes: 6 additions & 7 deletions apps/devtool/src/app/api/data-store/example.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
"addressBook": [],
"credentials": [
{
"id": "0xE7349Bf47e09d3aa047FC4cDE514DaafAfc97037",
"id": "0x000c0d191308A336356BEe3813CC17F6868972C4",
"userId": "fe723044-35df-4e99-9739-122a48d4ab96",
"key": {
"kty": "EC",
"crv": "secp256k1",
"alg": "ES256K",
"kid": "0xE7349Bf47e09d3aa047FC4cDE514DaafAfc97037",
"x": "PQ1ekiQFSp6UN3-RGQDUwzUuZC7jjaDY_vIOhuGI_f4",
"y": "CNiOEfEhjvPfIFaz7CzIgyST_tHtQDhNqak9CDDIwRc"
"kid": "0x000c0d191308A336356BEe3813CC17F6868972C4",
"addr": "0x000c0d191308A336356BEe3813CC17F6868972C4"
}
}
],
Expand All @@ -23,7 +22,7 @@
"userWallets": [
{
"userId": "fe723044-35df-4e99-9739-122a48d4ab96",
"walletId": "f5b63a0f5072a336a5c56158fe74d06ab4abc7fcf583fdbf5604a6341c73405a"
"walletId": "eip155:eoa:0x56085a7d2e308ec8ce6fd707cdc5282431d025db"
}
],
"users": [
Expand All @@ -36,8 +35,8 @@
"walletGroups": [],
"wallets": [
{
"id": "f5b63a0f5072a336a5c56158fe74d06ab4abc7fcf583fdbf5604a6341c73405a",
"address": "0x24f0914062f66d487dc082802aac53cd10328d96",
"id": "eip155:eoa:0x56085a7d2e308ec8ce6fd707cdc5282431d025db",
"address": "0x56085a7d2e308ec8ce6fd707cdc5282431d025db",
"accountType": "eoa"
}
]
Expand Down
76 changes: 22 additions & 54 deletions apps/devtool/src/app/data-store/_components/DataStoreConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ import { faCheckCircle, faSpinner } from '@fortawesome/pro-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import Editor from '@monaco-editor/react'
import { EntityStore, EntityUtil, PolicyStore, entityDataSchema, policyDataSchema } from '@narval/policy-engine-shared'
import { Curves, Jwk, KeyTypes, Payload, SigningAlg, hash, hexToBase64Url, signJwt } from '@narval/signature'
import { signMessage } from '@wagmi/core'
import { Payload, hash } from '@narval/signature'
import axios from 'axios'
import { useEffect, useRef, useState } from 'react'
import { useAccount } from 'wagmi'
import NarButton from '../../_design-system/NarButton'
import NarDialog from '../../_design-system/NarDialog'
import NarInput from '../../_design-system/NarInput'
import useAccountSignature from '../../_hooks/useAccountSignature'
import useStore from '../../_hooks/useStore'
import { config } from '../../_lib/config'

const ActionStatus = (isDone: boolean, label: string) => {
if (!isDone) {
Expand All @@ -34,7 +32,6 @@ const ActionStatus = (isDone: boolean, label: string) => {
}

const DataStoreConfig = () => {
const account = useAccount()
const {
engineUrl,
engineClientId,
Expand All @@ -48,6 +45,7 @@ const DataStoreConfig = () => {
policySignatureUrl,
setPolicySignatureUrl
} = useStore()
const { jwk, signAccountJwt } = useAccountSignature()

const [data, setData] = useState<string>()
const [dataStore, setDataStore] = useState<{ entity: EntityStore; policy: PolicyStore }>()
Expand All @@ -65,22 +63,8 @@ const DataStoreConfig = () => {
const editorRef = useRef<any>(null)
const monacoRef = useRef<any>(null)

useEffect(() => {
if (data) return

const getData = async () => {
const dataStore = await axios.get('/api/data-store')
const { entity, policy } = dataStore.data
setData(JSON.stringify({ entity: entity.data, policy: policy.data }, null, 2))
setDataStore(dataStore.data)
}

getData()
}, [data])

const signEntityData = async () => {
if (!data || !dataStore) return
if (!account.address) return
if (!data || !dataStore || !jwk) return

const { entity } = JSON.parse(data)

Expand All @@ -104,28 +88,14 @@ const DataStoreConfig = () => {

setIsEntitySigning(true)

const jwtSigner = async (message: string) => {
const jwtSig = await signMessage(config, { message })

return hexToBase64Url(jwtSig)
}

const entityPayload: Payload = {
data: hash(entity),
sub: account.address,
sub: jwk.addr,
iss: 'https://devtool.narval.xyz',
iat: Math.floor(Date.now() / 1000)
}

const jwk: Jwk = {
kty: KeyTypes.EC,
crv: Curves.SECP256K1,
alg: SigningAlg.ES256K,
kid: account.address,
addr: account.address
}

const entitySig = await signJwt(entityPayload, jwk, { alg: SigningAlg.EIP191 }, jwtSigner)
const entitySig = await signAccountJwt(entityPayload)
setProcessingStatus((prev) => ({ ...prev, entitySigned: true }))

await axios.post('/api/data-store', {
Expand All @@ -135,6 +105,7 @@ const DataStoreConfig = () => {
},
policy: dataStore.policy
})
await getData()
setProcessingStatus((prev) => ({ ...prev, dataSaved: true }))

await axios.post(`${engineUrl}/tenants/sync`, null, {
Expand All @@ -157,8 +128,7 @@ const DataStoreConfig = () => {
}

const signPolicyData = async () => {
if (!data || !dataStore) return
if (!account.address) return
if (!data || !dataStore || !jwk) return

const { policy } = JSON.parse(data)

Expand All @@ -174,28 +144,14 @@ const DataStoreConfig = () => {

setIsPolicySigning(true)

const jwtSigner = async (message: string) => {
const jwtSig = await signMessage(config, { message })

return hexToBase64Url(jwtSig)
}

const policyPayload: Payload = {
data: hash(policy),
sub: account.address,
sub: jwk.addr,
iss: 'https://devtool.narval.xyz',
iat: Math.floor(Date.now() / 1000)
}

const jwk: Jwk = {
kty: KeyTypes.EC,
crv: Curves.SECP256K1,
alg: SigningAlg.ES256K,
kid: account.address,
addr: account.address
}

const policySig = await signJwt(policyPayload, jwk, { alg: SigningAlg.EIP191 }, jwtSigner)
const policySig = await signAccountJwt(policyPayload)
setProcessingStatus((prev) => ({ ...prev, policySigned: true }))

await axios.post('/api/data-store', {
Expand All @@ -205,6 +161,7 @@ const DataStoreConfig = () => {
data: policy
}
})
await getData()
setProcessingStatus((prev) => ({ ...prev, dataSaved: true }))

await axios.post(`${engineUrl}/tenants/sync`, null, {
Expand All @@ -226,6 +183,17 @@ const DataStoreConfig = () => {
}, 5000)
}

const getData = async () => {
const { data: dataStore } = await axios.get('/api/data-store')
const { entity, policy } = dataStore
setData(JSON.stringify({ entity: entity.data, policy: policy.data }, null, 2))
setDataStore(dataStore)
}

useEffect(() => {
getData()
}, [])

return (
<>
<div className="flex gap-12">
Expand Down
Loading

0 comments on commit 5cc5e0d

Please sign in to comment.