Skip to content

Commit

Permalink
Add permission in evaluation token when signing
Browse files Browse the repository at this point in the history
  • Loading branch information
samuel committed May 10, 2024
1 parent 34af7c6 commit cd1293f
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 26 deletions.
6 changes: 3 additions & 3 deletions apps/devtool/src/app/_hooks/useAccountSignature.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ const useAccountSignature = () => {
return signature
}

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

const jwsdHeader: JwsdHeader = {
alg: SigningAlg.EIP191,
kid: jwk.kid,
typ: 'gnap-binding-jwsd',
htm: 'POST',
uri: 'http://localhost:3011/sign',
uri: opts.uri,
created: new Date().getTime(),
ath: hexToBase64Url(hash(accessToken))
ath: hexToBase64Url(hash(opts.accessToken))
}

const signature = await signJwsd(payload, jwsdHeader, signer).then((jws) => {
Expand Down
29 changes: 15 additions & 14 deletions apps/devtool/src/app/_hooks/useVaultApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,22 @@ const useVaultApi = () => {
}
}

const importPrivateKey = async (privateKey: string) => {
if (!vaultClientId || !vaultClientSecret || !privateKey) return
const importPrivateKey = async (payload: { privateKey: string }, accessToken: string) => {
if (!vaultClientId || !accessToken) return

setErrors(undefined)

try {
const { data } = await axios.post<WalletEntity>(
`${vaultUrl}/import/private-key`,
{ privateKey },
{
headers: {
'x-client-id': vaultClientId,
'x-client-secret': vaultClientSecret
}
const uri = `${vaultUrl}/import/private-key`
const detachedJws = await signAccountJwsd(payload, { accessToken, uri })

const { data } = await axios.post<WalletEntity>(uri, payload, {
headers: {
'x-client-id': vaultClientId,
'detached-jws': detachedJws,
authorization: `GNAP ${accessToken}`
}
)
})

return data
} catch (error) {
Expand All @@ -73,14 +73,15 @@ const useVaultApi = () => {
}

const signTransaction = async (payload: { request: Request }, accessToken: string) => {
if (!vaultClientId || !vaultClientSecret || !accessToken) return
if (!vaultClientId || !accessToken) return

setErrors(undefined)

try {
const detachedJws = await signAccountJwsd(payload, accessToken)
const uri = `${vaultUrl}/sign`
const detachedJws = await signAccountJwsd(payload, { accessToken, uri })

const { data } = await axios.post(`${vaultUrl}/sign`, payload, {
const { data } = await axios.post(uri, payload, {
headers: {
'x-client-id': vaultClientId,
'detached-jws': detachedJws,
Expand Down
6 changes: 3 additions & 3 deletions apps/devtool/src/app/_lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const DATA_STORE_URL = 'http://127.0.0.1:4200/api/data-store'
export const ENGINE_URL = 'http://127.0.0.1:3010'
export const VAULT_URL = 'http://127.0.0.1:3011'
export const DATA_STORE_URL = 'http://localhost:4200/api/data-store'
export const ENGINE_URL = 'http://localhost:3010'
export const VAULT_URL = 'http://localhost:3011'

export const LOCAL_STORAGE_KEYS = {
engineUrl: 'narvalEngineUrl',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import {
faCheckCircle,
faFileSignature,
faTriangleExclamation,
faUpload,
faXmarkCircle
} from '@fortawesome/pro-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { Editor } from '@monaco-editor/react'
import { Decision, EvaluationRequest, EvaluationResponse } from '@narval/policy-engine-shared'
import { useEffect, useMemo, useRef, useState } from 'react'
import { generatePrivateKey } from 'viem/accounts'
import NarButton from '../../_design-system/NarButton'
import useAccountSignature from '../../_hooks/useAccountSignature'
import useEngineApi from '../../_hooks/useEngineApi'
import useVaultApi from '../../_hooks/useVaultApi'
import { erc20, grantPermission, spendingLimits } from './request'

const PlaygroundEditor = () => {
const { signAccountJwt } = useAccountSignature()
const { errors: evaluationErrors, evaluateRequest } = useEngineApi()
const { errors: signatureErrors, signTransaction } = useVaultApi()
const { errors: signatureErrors, signTransaction, importPrivateKey } = useVaultApi()
const [codeEditor, setCodeEditor] = useState<string | undefined>()
const [isProcessing, setIsProcessing] = useState<boolean>(false)
const [evaluationResponse, setEvaluationResponse] = useState<EvaluationResponse>()
Expand Down Expand Up @@ -84,6 +84,22 @@ const PlaygroundEditor = () => {
setIsProcessing(false)
}

const importWallet = async () => {
if (!evaluationResponse) return

const { accessToken, request } = evaluationResponse

if (!accessToken?.value || !request) return

setIsProcessing(true)
setEvaluationResponse(undefined)

await importPrivateKey({ privateKey: generatePrivateKey() }, accessToken.value)

setEvaluationResponse(undefined)
setIsProcessing(false)
}

const updateExample = async () => {
setCodeEditor(JSON.stringify(await erc20(), null, 2))
}
Expand Down Expand Up @@ -143,6 +159,12 @@ const PlaygroundEditor = () => {
onClick={signRequest}
disabled={isProcessing || !canBeSigned}
/>
<NarButton
label="Import Wallet"
leftIcon={<FontAwesomeIcon icon={faUpload} />}
onClick={importWallet}
disabled={isProcessing || !canBeSigned}
/>

{!isProcessing && !error && evaluationResponse && (
<div className="flex items-center gap-2">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Action,
ApprovalRequirement,
CredentialEntity,
Decision,
Expand All @@ -8,6 +9,7 @@ import {
EvaluationRequest,
EvaluationResponse,
JwtString,
Permission,
Policy
} from '@narval/policy-engine-shared'
import { Payload, PrivateKey, PublicKey, SigningAlg, decodeJwt, hash, signJwt, verifyJwt } from '@narval/signature'
Expand Down Expand Up @@ -130,7 +132,15 @@ export class OpenPolicyAgentEngine implements Engine<OpenPolicyAgentEngine> {
accessToken: {
value: await this.sign({
principalCredential,
message
message,
...(evaluation.request.action === Action.GRANT_PERMISSION && {
access: [
{
resource: evaluation.request.resourceId,
permissions: evaluation.request.permissions
}
]
})
})
}
}
Expand Down Expand Up @@ -265,7 +275,16 @@ export class OpenPolicyAgentEngine implements Engine<OpenPolicyAgentEngine> {
return Boolean(result.reasons?.some((reason) => reason.type === 'permit' && reason.approvalsMissing.length > 0))
}

private async sign(params: { principalCredential: CredentialEntity; message: string }): Promise<JwtString> {
private async sign(params: {
principalCredential: CredentialEntity
message: string
access?: [
{
resource: string
permissions: Permission[]
}
]
}): Promise<JwtString> {
const principalJwk: PublicKey = params.principalCredential.key

const payload: Payload = {
Expand All @@ -279,7 +298,8 @@ export class OpenPolicyAgentEngine implements Engine<OpenPolicyAgentEngine> {
iss: 'https://armory.narval.xyz',
// aud: TODO
// jti: TODO
cnf: principalJwk
cnf: principalJwk,
...(params.access && { access: params.access })
}

return signJwt(payload, this.privateKey, { alg: SigningAlg.EIP191 })
Expand Down

0 comments on commit cd1293f

Please sign in to comment.