diff --git a/package.json b/package.json index 6194458..fae9a91 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "node": ">= v18.12.1" }, "scripts": { - "prettify": "prettier --write \"src/**/*.ts*\"", + "prettify": "prettier --write \"src/**/*.js*\"", "lint": "yarn run lint-css && yarn run lint-js", - "lint-js": "eslint --max-warnings 0 --config ./.eslintrc.json \"src/**/*.ts*\"", + "lint-js": "eslint --max-warnings 0 --config ./.eslintrc.json \"src/**/*.js*\"", "lint-css": "stylelint --max-warnings 0 --fix \"src/**/*.css\"", "did-create": "node --enable-source-maps dist/backend/createDID.js", "did-configuration": "node --enable-source-maps dist/backend/createDIDConfiguration.js", @@ -69,9 +69,9 @@ "backend": { "context": "node", "source": [ - "src/backend/server.ts", - "src/backend/createDID.ts", - "src/backend/createDIDConfiguration.ts" + "src/backend/server.js", + "src/backend/createDID.js", + "src/backend/createDIDConfiguration.js" ], "distDir": "dist/backend", "outputFormat": "esmodule" diff --git a/src/backend/cTypes/discordCType.ts b/src/backend/cTypes/discordCType.js similarity index 79% rename from src/backend/cTypes/discordCType.ts rename to src/backend/cTypes/discordCType.js index 752db6a..4b25484 100644 --- a/src/backend/cTypes/discordCType.ts +++ b/src/backend/cTypes/discordCType.js @@ -1,6 +1,4 @@ -import type { ICType } from '@kiltprotocol/sdk-js'; - -export const discordCType: ICType = { +export const discordCType = { $schema: 'http://kilt-protocol.org/draft-01/ctype#', title: 'Discord', properties: { diff --git a/src/backend/cTypes/domainLinkageCType.ts b/src/backend/cTypes/domainLinkageCType.js similarity index 75% rename from src/backend/cTypes/domainLinkageCType.ts rename to src/backend/cTypes/domainLinkageCType.js index 62f0281..cb1a309 100644 --- a/src/backend/cTypes/domainLinkageCType.ts +++ b/src/backend/cTypes/domainLinkageCType.js @@ -1,6 +1,4 @@ -import type { ICType } from '@kiltprotocol/sdk-js'; - -export const domainLinkageCType: ICType = { +export const domainLinkageCType = { $schema: 'http://kilt-protocol.org/draft-01/ctype#', title: 'Domain Linkage Credential', properties: { diff --git a/src/backend/cTypes/emailCType.ts b/src/backend/cTypes/emailCType.js similarity index 73% rename from src/backend/cTypes/emailCType.ts rename to src/backend/cTypes/emailCType.js index 0b66d8b..4ea82e4 100644 --- a/src/backend/cTypes/emailCType.ts +++ b/src/backend/cTypes/emailCType.js @@ -1,6 +1,4 @@ -import type { ICType } from '@kiltprotocol/sdk-js'; - -export const emailCType: ICType = { +export const emailCType = { $schema: 'http://kilt-protocol.org/draft-01/ctype#', title: 'Email', properties: { diff --git a/src/backend/cTypes/githubCType.ts b/src/backend/cTypes/githubCType.js similarity index 76% rename from src/backend/cTypes/githubCType.ts rename to src/backend/cTypes/githubCType.js index 7beda23..3f65c9c 100644 --- a/src/backend/cTypes/githubCType.ts +++ b/src/backend/cTypes/githubCType.js @@ -1,6 +1,4 @@ -import type { ICType } from '@kiltprotocol/sdk-js'; - -export const githubCType: ICType = { +export const githubCType = { $schema: 'http://kilt-protocol.org/draft-01/ctype#', title: 'GitHub', properties: { diff --git a/src/backend/cTypes/telegramCType.ts b/src/backend/cTypes/telegramCType.js similarity index 81% rename from src/backend/cTypes/telegramCType.ts rename to src/backend/cTypes/telegramCType.js index d20760c..19db517 100644 --- a/src/backend/cTypes/telegramCType.ts +++ b/src/backend/cTypes/telegramCType.js @@ -1,6 +1,4 @@ -import type { ICType } from '@kiltprotocol/sdk-js'; - -export const telegramCType: ICType = { +export const telegramCType = { $schema: 'http://kilt-protocol.org/draft-01/ctype#', title: 'Telegram', properties: { diff --git a/src/backend/cTypes/twitchCType.ts b/src/backend/cTypes/twitchCType.js similarity index 76% rename from src/backend/cTypes/twitchCType.ts rename to src/backend/cTypes/twitchCType.js index f5af65a..a370366 100644 --- a/src/backend/cTypes/twitchCType.ts +++ b/src/backend/cTypes/twitchCType.js @@ -1,6 +1,4 @@ -import type { ICType } from '@kiltprotocol/sdk-js'; - -export const twitchCType: ICType = { +export const twitchCType = { $schema: 'http://kilt-protocol.org/draft-01/ctype#', title: 'Twitch', properties: { diff --git a/src/backend/cTypes/twitterCType.ts b/src/backend/cTypes/twitterCType.js similarity index 73% rename from src/backend/cTypes/twitterCType.ts rename to src/backend/cTypes/twitterCType.js index 3475f04..7f0effc 100644 --- a/src/backend/cTypes/twitterCType.ts +++ b/src/backend/cTypes/twitterCType.js @@ -1,6 +1,4 @@ -import type { ICType } from '@kiltprotocol/sdk-js'; - -export const twitterCType: ICType = { +export const twitterCType = { $schema: 'http://kilt-protocol.org/draft-01/ctype#', title: 'Twitter', properties: { diff --git a/src/backend/cTypes/youtubeCType.ts b/src/backend/cTypes/youtubeCType.js similarity index 77% rename from src/backend/cTypes/youtubeCType.ts rename to src/backend/cTypes/youtubeCType.js index 8c057cb..6b685a5 100644 --- a/src/backend/cTypes/youtubeCType.ts +++ b/src/backend/cTypes/youtubeCType.js @@ -1,6 +1,4 @@ -import type { ICType } from '@kiltprotocol/sdk-js'; - -export const youtubeCType: ICType = { +export const youtubeCType = { $schema: 'http://kilt-protocol.org/draft-01/ctype#', title: 'YoutubeChannel', properties: { diff --git a/src/backend/createDID.ts b/src/backend/createDID.js similarity index 99% rename from src/backend/createDID.ts rename to src/backend/createDID.js index 79b4a2c..48537ae 100644 --- a/src/backend/createDID.ts +++ b/src/backend/createDID.js @@ -5,24 +5,19 @@ import { Did, disconnect, } from '@kiltprotocol/sdk-js'; - import { configuration } from './utilities/configuration'; import { initKilt } from './utilities/initKilt'; import { keypairsPromise } from './utilities/keypairs'; import { logger } from './utilities/logger'; - (async () => { await initKilt(); - if (configuration.did) { throw new Error( `DID ${configuration.did} already exists on the blockchain ${configuration.blockchainEndpoint}`, ); } - const { assertionMethod, authentication, payer, keyAgreement } = await keypairsPromise; - const api = ConfigService.get('api'); const balances = await api.query.system.account(payer.address); const free = balances.data.free; @@ -32,7 +27,6 @@ import { logger } from './utilities/logger'; `The payer account ${payer.address} has insufficient funds ${freeText}`, ); } - const tx = await Did.getStoreTx( { authentication: [authentication], @@ -45,12 +39,9 @@ import { logger } from './utilities/logger'; keyType: authentication.type, }), ); - logger.warn(`Storing the DID on the blockchain…`); await Blockchain.signAndSubmitTx(tx, payer); - const did = Did.getFullDidUriFromKey(authentication); logger.warn(`Add this to your .env: DID=${did}`); - await disconnect(); })(); diff --git a/src/backend/createDIDConfiguration.ts b/src/backend/createDIDConfiguration.js similarity index 96% rename from src/backend/createDIDConfiguration.ts rename to src/backend/createDIDConfiguration.js index 40e7b57..d5785d3 100644 --- a/src/backend/createDIDConfiguration.ts +++ b/src/backend/createDIDConfiguration.js @@ -1,41 +1,33 @@ import { mkdir, writeFile, lstat } from 'node:fs/promises'; import { join } from 'node:path'; - import { disconnect } from '@kiltprotocol/sdk-js'; - import { configuration } from './utilities/configuration'; import { attestDomainLinkage, fromCredential, } from './utilities/domainLinkageCredential'; - (async () => { console.log( `Generating the .well-known/did-configuration.json file for DID ${configuration.did} and host ${configuration.baseUri}`, ); - const wellKnown = join(configuration.distFolder, '.well-known'); try { await lstat(wellKnown); } catch (error) { console.warn( 'Looks like the .well-known folder doesn’t exist, creating', - (error as Error).message, + error.message, ); await mkdir(wellKnown); } - const credential = await attestDomainLinkage(); const domainLinkageCredential = fromCredential(credential); - const didConfigurationResource = { '@context': 'https://identity.foundation/.well-known/did-configuration/v1', linked_dids: [domainLinkageCredential], }; const json = JSON.stringify(didConfigurationResource); - const path = join(wellKnown, 'did-configuration.json'); await writeFile(path, json); - await disconnect(); })(); diff --git a/src/backend/endpoints/paths.ts b/src/backend/endpoints/paths.js similarity index 100% rename from src/backend/endpoints/paths.ts rename to src/backend/endpoints/paths.js diff --git a/src/backend/endpoints/requestCredential.ts b/src/backend/endpoints/requestCredential.js similarity index 70% rename from src/backend/endpoints/requestCredential.ts rename to src/backend/endpoints/requestCredential.js index 9c7f985..3cd5c42 100644 --- a/src/backend/endpoints/requestCredential.ts +++ b/src/backend/endpoints/requestCredential.js @@ -1,33 +1,22 @@ -import { Request, Response, Router } from 'express'; +import { Router } from 'express'; import { StatusCodes } from 'http-status-codes'; import { randomAsHex } from '@polkadot/util-crypto'; -import { CType, ICType } from '@kiltprotocol/sdk-js'; - +import { CType } from '@kiltprotocol/sdk-js'; import { encryptMessageBody } from '../utilities/encryptMessage'; import { paths } from './paths'; -import { - Session, - sessionMiddleware, - setSession, -} from '../utilities/sessionStorage'; +import { sessionMiddleware, setSession } from '../utilities/sessionStorage'; import { logger } from '../utilities/logger'; import { trustedAttesters } from '../utilities/trustedAttesters'; - -type Input = Array; - -async function handler(request: Request, response: Response): Promise { +async function handler(request, response) { try { logger.debug('Request credential started'); - - const { session } = request as Request & { session: Session }; + const { session } = request; const { encryptionKeyUri } = session; - - const cTypes = (request.body as Input).map((id) => ({ + const cTypes = request.body.map((id) => ({ cTypeHash: CType.idToHash(id), trustedAttesters, })); logger.debug('Request credential CType found'); - const challenge = randomAsHex(24); setSession({ ...session, requestChallenge: challenge }); const output = await encryptMessageBody(encryptionKeyUri, { @@ -37,14 +26,11 @@ async function handler(request: Request, response: Response): Promise { }, type: 'request-credential', }); - logger.debug('Request credential completed'); response.send(output); } catch (error) { response.status(StatusCodes.INTERNAL_SERVER_ERROR).send(error); } } - export const requestCredential = Router(); - requestCredential.post(paths.requestCredential, sessionMiddleware, handler); diff --git a/src/backend/endpoints/session.ts b/src/backend/endpoints/session.js similarity index 74% rename from src/backend/endpoints/session.ts rename to src/backend/endpoints/session.js index 38232d2..105d44f 100644 --- a/src/backend/endpoints/session.ts +++ b/src/backend/endpoints/session.js @@ -1,40 +1,24 @@ -import { Request, Response, Router } from 'express'; +import { Router } from 'express'; import { StatusCodes } from 'http-status-codes'; - -import { Did, DidResourceUri, Utils } from '@kiltprotocol/sdk-js'; +import { Did, Utils } from '@kiltprotocol/sdk-js'; import { randomAsHex } from '@polkadot/util-crypto'; - import { didDocumentPromise } from '../utilities/didDocument'; import { decrypt } from '../utilities/cryptoCallbacks'; import { - BasicSession, basicSessionMiddleware, setSession, } from '../utilities/sessionStorage'; import { logger } from '../utilities/logger'; - import { paths } from './paths'; - -export interface CheckSessionInput { - encryptionKeyUri: DidResourceUri; - encryptedChallenge: string; - nonce: string; -} - -async function handler(request: Request, response: Response): Promise { +async function handler(request, response) { try { logger.debug('Session confirmation started'); - - const payload = request.body as CheckSessionInput; + const payload = request.body; const { encryptionKeyUri, encryptedChallenge, nonce } = payload; - const { session } = request as Request & { session: BasicSession }; - + const { session } = request; const encryptionKey = await Did.resolveKey(encryptionKeyUri); - logger.debug('Session confirmation resolved DID encryption key'); - const { keyAgreementKey, did } = await didDocumentPromise; - const { data } = await decrypt({ data: Utils.Crypto.coToUInt8(encryptedChallenge), nonce: Utils.Crypto.coToUInt8(nonce), @@ -42,60 +26,43 @@ async function handler(request: Request, response: Response): Promise { peerPublicKey: encryptionKey.publicKey, }); logger.debug('Session confirmation decrypted challenge'); - const decryptedChallenge = Utils.Crypto.u8aToHex(data); const originalChallenge = session.didChallenge; - if (decryptedChallenge !== originalChallenge) { response .status(StatusCodes.FORBIDDEN) .send('Challenge signature mismatch'); return; } - setSession({ ...session, did: encryptionKey.controller, encryptionKeyUri, didConfirmed: true, }); - logger.debug('Challenge confirmation matches'); response.sendStatus(StatusCodes.NO_CONTENT); } catch (error) { response.status(StatusCodes.INTERNAL_SERVER_ERROR).send(error); } } - function startSession() { const sessionId = randomAsHex(24); const challenge = randomAsHex(24); - setSession({ sessionId, didChallenge: challenge }); - return { challenge, sessionId, }; } - -export interface GetSessionOutput { - dAppEncryptionKeyUri: DidResourceUri; - sessionId: string; - challenge: string; -} - const path = paths.session; - export const session = Router(); - session.get(path, async (request, response) => { const { did, keyAgreementKey } = await didDocumentPromise; - const dAppEncryptionKeyUri: DidResourceUri = `${did}${keyAgreementKey.id}`; + const dAppEncryptionKeyUri = `${did}${keyAgreementKey.id}`; response.send({ dAppEncryptionKeyUri, ...startSession(), - } as GetSessionOutput); + }); }); - session.post(path, basicSessionMiddleware, handler); diff --git a/src/backend/endpoints/sessionApi.ts b/src/backend/endpoints/sessionApi.js similarity index 50% rename from src/backend/endpoints/sessionApi.ts rename to src/backend/endpoints/sessionApi.js index 12af026..0a669a6 100644 --- a/src/backend/endpoints/sessionApi.ts +++ b/src/backend/endpoints/sessionApi.js @@ -1,17 +1,10 @@ import ky from 'ky'; - -import { CheckSessionInput, GetSessionOutput } from './session'; import { paths } from './paths'; import { sessionHeader } from './sessionHeader'; - -export async function getSessionValues(): Promise { +export async function getSessionValues() { return ky.get(paths.session).json(); } - -export async function checkSession( - json: CheckSessionInput, - sessionId: string, -): Promise { +export async function checkSession(json, sessionId) { const headers = { [sessionHeader]: sessionId }; await ky.post(paths.session, { json, headers }); } diff --git a/src/backend/endpoints/sessionHeader.ts b/src/backend/endpoints/sessionHeader.js similarity index 100% rename from src/backend/endpoints/sessionHeader.ts rename to src/backend/endpoints/sessionHeader.js diff --git a/src/backend/endpoints/staticFiles.ts b/src/backend/endpoints/staticFiles.js similarity index 99% rename from src/backend/endpoints/staticFiles.ts rename to src/backend/endpoints/staticFiles.js index 55f20d7..3fa30b2 100644 --- a/src/backend/endpoints/staticFiles.ts +++ b/src/backend/endpoints/staticFiles.js @@ -1,9 +1,6 @@ import express, { Router } from 'express'; - import { configuration } from '../utilities/configuration'; - export const staticFiles = Router(); - staticFiles.use( // eslint-disable-next-line import/no-named-as-default-member express.static(configuration.distFolder, { diff --git a/src/backend/endpoints/verify.ts b/src/backend/endpoints/verify.js similarity index 78% rename from src/backend/endpoints/verify.ts rename to src/backend/endpoints/verify.js index 59942b5..aea3e11 100644 --- a/src/backend/endpoints/verify.ts +++ b/src/backend/endpoints/verify.js @@ -1,50 +1,33 @@ -import type { DidUri, ICredentialPresentation } from '@kiltprotocol/sdk-js'; import { Credential, CType, Message } from '@kiltprotocol/sdk-js'; -import { Request, Response, Router } from 'express'; +import { Router } from 'express'; import { StatusCodes } from 'http-status-codes'; - -import { Session, sessionMiddleware } from '../utilities/sessionStorage'; +import { sessionMiddleware } from '../utilities/sessionStorage'; import { logger } from '../utilities/logger'; import { decrypt } from '../utilities/cryptoCallbacks'; import { trustedAttesters } from '../utilities/trustedAttesters'; import { socialCTypeIds, supportedCTypes } from '../utilities/supportedCType'; - import { paths } from './paths'; - -interface Output { - presentation: ICredentialPresentation; - isAttested: boolean; - revoked?: boolean; - attester?: DidUri; -} - -async function handler(request: Request, response: Response): Promise { +async function handler(request, response) { try { logger.debug('Verification started'); - const message = await Message.decrypt(request.body, decrypt); const messageBody = message.body; logger.debug('Message decrypted'); - Message.verifyMessageBody(messageBody); const { type } = messageBody; - if (type === 'reject') { response.status(StatusCodes.CONFLICT).send('Message contains rejection'); return; } - if (type !== 'submit-credential') { throw new Error('Unexpected message type'); } - - const { session } = request as Request & { session: Session }; + const { session } = request; if (!session.requestChallenge) { response.status(StatusCodes.FORBIDDEN).send('No request challenge'); return; } const challenge = session.requestChallenge; - const presentation = messageBody.content[0]; try { const cTypeId = CType.hashToId(presentation.claim.cTypeHash); @@ -52,11 +35,9 @@ async function handler(request: Request, response: Response): Promise { response.status(StatusCodes.FORBIDDEN).send('Not a CType we requested'); return; } - const ctype = Object.values(supportedCTypes).find( ({ $id }) => $id === cTypeId, ); - const { revoked, attester } = await Credential.verifyPresentation( presentation, { @@ -64,19 +45,16 @@ async function handler(request: Request, response: Response): Promise { challenge, }, ); - const isAttested = !revoked; - if (!trustedAttesters.includes(attester)) { response .status(StatusCodes.FORBIDDEN) .send('Not an attester we requested'); return; } - - response.send({ presentation, isAttested, attester, revoked } as Output); + response.send({ presentation, isAttested, attester, revoked }); } catch { - response.send({ presentation, isAttested: false } as Output); + response.send({ presentation, isAttested: false }); } finally { logger.debug('Verification completed'); } @@ -84,7 +62,5 @@ async function handler(request: Request, response: Response): Promise { response.status(StatusCodes.INTERNAL_SERVER_ERROR).send(error); } } - export const verify = Router(); - verify.post(paths.verify, sessionMiddleware, handler); diff --git a/src/backend/server.ts b/src/backend/server.js similarity index 99% rename from src/backend/server.ts rename to src/backend/server.js index c1458a7..2b5148d 100644 --- a/src/backend/server.ts +++ b/src/backend/server.js @@ -1,40 +1,31 @@ import express from 'express'; import bodyParser from 'body-parser'; - import { didDocumentPromise } from './utilities/didDocument'; import { configuration } from './utilities/configuration'; import { logger } from './utilities/logger'; - import { session } from './endpoints/session'; import { requestCredential } from './endpoints/requestCredential'; import { verify } from './endpoints/verify'; import { staticFiles } from './endpoints/staticFiles'; - (async () => { await didDocumentPromise; logger.info('Blockchain connection initialized'); - const app = express(); app.use(bodyParser.json()); - app.use(session); app.use(requestCredential); app.use(verify); app.use(staticFiles); - logger.info('Routes configured'); - const host = '0.0.0.0'; const { port, baseUri } = configuration; const started = app.listen(port, host, () => logger.info(`Listening on ${baseUri} (host: ${host}, port: ${port})`), ); - function stop() { started.close(); process.exit(1); } - process.on('unhandledRejection', stop); process.on('uncaughtException', stop); })(); diff --git a/src/backend/utilities/configuration.ts b/src/backend/utilities/configuration.js similarity index 89% rename from src/backend/utilities/configuration.ts rename to src/backend/utilities/configuration.js index 8f75d41..124ccca 100644 --- a/src/backend/utilities/configuration.ts +++ b/src/backend/utilities/configuration.js @@ -1,29 +1,21 @@ import { cwd } from 'node:process'; import path from 'node:path'; - import { config } from 'dotenv'; import { pino } from 'pino'; -import { DidUri } from '@kiltprotocol/sdk-js'; - config(); - class ConfigurationError extends Error { - constructor(message: string) { + constructor(message) { super(message); pino().fatal(message); process.exit(1); } } - const { env } = process; - const baseUri = env.URL; if (!baseUri) { throw new ConfigurationError('URL is not provided'); } - -const did = env.DID as DidUri; - +const did = env.DID; const payerMnemonic = env.SECRET_PAYER_MNEMONIC; if (!payerMnemonic) { throw new ConfigurationError('SECRET_PAYER_MNEMONIC is not provided'); @@ -44,14 +36,12 @@ const keyAgreementMnemonic = env.SECRET_KEY_AGREEMENT_MNEMONIC; if (!keyAgreementMnemonic) { throw new ConfigurationError('SECRET_KEY_AGREEMENT_MNEMONIC is not provided'); } - const blockchainEndpoint = env.BLOCKCHAIN_ENDPOINT; if (!blockchainEndpoint) { throw new ConfigurationError('No blockchain endpoint provided'); } - export const configuration = { - port: parseInt(env.PORT as string) || 3000, + port: parseInt(env.PORT) || 3000, blockchainEndpoint, baseUri, distFolder: path.join(cwd(), 'dist', 'frontend'), diff --git a/src/backend/utilities/cryptoCallbacks.ts b/src/backend/utilities/cryptoCallbacks.js similarity index 63% rename from src/backend/utilities/cryptoCallbacks.ts rename to src/backend/utilities/cryptoCallbacks.js index cecf7ff..279d2d5 100644 --- a/src/backend/utilities/cryptoCallbacks.ts +++ b/src/backend/utilities/cryptoCallbacks.js @@ -1,55 +1,33 @@ -import { - DecryptCallback, - DidResourceUri, - EncryptCallback, - Utils, -} from '@kiltprotocol/sdk-js'; - +import { Utils } from '@kiltprotocol/sdk-js'; import { keypairsPromise } from './keypairs'; import { didDocumentPromise } from './didDocument'; - -export async function signWithAssertionMethod({ data }: { data: Uint8Array }) { +export async function signWithAssertionMethod({ data }) { const { assertionMethod } = await keypairsPromise; - const { did, assertionMethodKey } = await didDocumentPromise; - const keyUri: DidResourceUri = `${did}${assertionMethodKey.id}`; - + const keyUri = `${did}${assertionMethodKey.id}`; return { signature: assertionMethod.sign(data, { withType: false }), keyType: assertionMethod.type, keyUri, }; } - -export async function encrypt({ - data, - peerPublicKey, -}: Parameters[0]) { +export async function encrypt({ data, peerPublicKey }) { const { keyAgreement } = await keypairsPromise; - const { did, keyAgreementKey } = await didDocumentPromise; - const keyUri: DidResourceUri = `${did}${keyAgreementKey.id}`; - + const keyUri = `${did}${keyAgreementKey.id}`; const { box, nonce } = Utils.Crypto.encryptAsymmetric( data, peerPublicKey, keyAgreement.secretKey, ); - return { data: box, nonce, keyUri, }; } - -export async function decrypt({ - data: box, - nonce, - peerPublicKey, -}: Parameters[0]) { +export async function decrypt({ data: box, nonce, peerPublicKey }) { const { keyAgreement } = await keypairsPromise; - const data = Utils.Crypto.decryptAsymmetric( { box, nonce }, peerPublicKey, @@ -58,7 +36,6 @@ export async function decrypt({ if (!data) { throw new Error('Failed to decrypt with given key'); } - return { data, }; diff --git a/src/backend/utilities/didDocument.ts b/src/backend/utilities/didDocument.js similarity index 85% rename from src/backend/utilities/didDocument.ts rename to src/backend/utilities/didDocument.js index d25a904..c81bce7 100644 --- a/src/backend/utilities/didDocument.ts +++ b/src/backend/utilities/didDocument.js @@ -1,34 +1,23 @@ -import type { DidDocument, KeyRelationship } from '@kiltprotocol/sdk-js'; import { Did, Utils } from '@kiltprotocol/sdk-js'; - import { keypairsPromise } from './keypairs'; import { configuration } from './configuration'; import { initKilt } from './initKilt'; - -async function compareKeys( - configured: { publicKey: Uint8Array }, - resolved: { publicKey: Uint8Array } | undefined, - relationship: KeyRelationship, -): Promise { +async function compareKeys(configured, resolved, relationship) { if (!resolved) { throw new Error( `Your on-chain DID is broken: the resolved key for ${relationship} is undefined`, ); } - const derivedHex = Utils.Crypto.u8aToHex(configured.publicKey); const resolvedHex = Utils.Crypto.u8aToHex(resolved.publicKey); - if (derivedHex !== resolvedHex) { throw new Error( `Your on-chain DID is broken: the configured key for ${relationship} does not match resolved one`, ); } } - -async function compareAllKeys(fullDid: DidDocument): Promise { +async function compareAllKeys(fullDid) { const keypairs = await keypairsPromise; - await compareKeys( keypairs.authentication, fullDid.authentication[0], @@ -45,35 +34,28 @@ async function compareAllKeys(fullDid: DidDocument): Promise { 'keyAgreement', ); } - export const didDocumentPromise = (async () => { await initKilt(); - const { did } = configuration; if (!did) { throw new Error('DID not configured, have you run createDID script?'); } - const resolved = await Did.resolve(did); if (!resolved || !resolved.document) { throw new Error( `Could not resolve the configured DID ${did}, have you run createDID script?`, ); } - const { document } = resolved; await compareAllKeys(document); - const assertionMethodKey = document.assertionMethod?.[0]; if (!assertionMethodKey) { throw new Error('Impossible: assertion method key not found'); } - const keyAgreementKey = document.keyAgreement?.[0]; if (!keyAgreementKey) { throw new Error('Impossible: key agreement key not found'); } - return { did, didDocument: document, diff --git a/src/backend/utilities/domainLinkageCredential.ts b/src/backend/utilities/domainLinkageCredential.js similarity index 74% rename from src/backend/utilities/domainLinkageCredential.ts rename to src/backend/utilities/domainLinkageCredential.js index 1e7d22d..8f41024 100644 --- a/src/backend/utilities/domainLinkageCredential.ts +++ b/src/backend/utilities/domainLinkageCredential.js @@ -1,78 +1,45 @@ -import { - Claim, - Credential, - IClaimContents, - ICredentialPresentation, -} from '@kiltprotocol/sdk-js'; -import { - constants, - Proof, - VerifiableCredential, -} from '@kiltprotocol/vc-export'; - +import { Claim, Credential } from '@kiltprotocol/sdk-js'; +import { constants } from '@kiltprotocol/vc-export'; import { configuration } from './configuration'; import { domainLinkageCType } from '../cTypes/domainLinkageCType'; import { signWithAssertionMethod } from './cryptoCallbacks'; - const { DEFAULT_VERIFIABLECREDENTIAL_CONTEXT, DEFAULT_VERIFIABLECREDENTIAL_TYPE, KILT_SELF_SIGNED_PROOF_TYPE, KILT_VERIFIABLECREDENTIAL_TYPE, } = constants; - const context = [ DEFAULT_VERIFIABLECREDENTIAL_CONTEXT, 'https://identity.foundation/.well-known/did-configuration/v1', ]; - const TTL = 1000 * 60 * 60 * 24 * 365 * 5; // 5 years - -export async function attestDomainLinkage(): Promise { +export async function attestDomainLinkage() { const claimContents = { id: configuration.did, origin: configuration.baseUri, }; - const claim = Claim.fromCTypeAndClaimContents( domainLinkageCType, claimContents, configuration.did, ); - const credential = Credential.fromClaim(claim); - return Credential.createPresentation({ credential, // the domain linkage credential is special in that it is signed with the assertionMethod key signCallback: signWithAssertionMethod, }); } - -interface DomainLinkageCredential - extends Omit< - VerifiableCredential, - '@context' | 'id' | 'legitimationIds' | 'credentialSubject' | 'proof' - > { - '@context': typeof context; - credentialSubject: IClaimContents; - proof: Proof; -} - -export function fromCredential( - input: ICredentialPresentation, -): DomainLinkageCredential { +export function fromCredential(input) { const credentialSubject = { ...input.claim.contents, rootHash: input.rootHash, }; const issuer = input.claim.owner; - const issuanceDate = new Date().toISOString(); const expirationDate = new Date(Date.now() + TTL).toISOString(); - const { claimerSignature } = input; - // add self-signed proof const proof = { type: KILT_SELF_SIGNED_PROOF_TYPE, @@ -81,7 +48,6 @@ export function fromCredential( signature: claimerSignature.signature, challenge: claimerSignature.challenge, }; - return { '@context': context, issuer, diff --git a/src/backend/utilities/encryptMessage.ts b/src/backend/utilities/encryptMessage.js similarity index 57% rename from src/backend/utilities/encryptMessage.ts rename to src/backend/utilities/encryptMessage.js index ed9ab3d..daafeeb 100644 --- a/src/backend/utilities/encryptMessage.ts +++ b/src/backend/utilities/encryptMessage.js @@ -1,19 +1,8 @@ -import type { - DidResourceUri, - IEncryptedMessage, - MessageBody, -} from '@kiltprotocol/sdk-js'; import { Did, Message } from '@kiltprotocol/sdk-js'; - import { encrypt } from './cryptoCallbacks'; import { configuration } from './configuration'; - -export async function encryptMessageBody( - encryptionKeyUri: DidResourceUri, - messageBody: MessageBody, -): Promise { +export async function encryptMessageBody(encryptionKeyUri, messageBody) { const { did } = Did.parse(encryptionKeyUri); - const message = Message.fromBody(messageBody, configuration.did, did); return Message.encrypt(message, encrypt, encryptionKeyUri); } diff --git a/src/backend/utilities/initKilt.ts b/src/backend/utilities/initKilt.js similarity index 74% rename from src/backend/utilities/initKilt.ts rename to src/backend/utilities/initKilt.js index 32e5bed..977785b 100644 --- a/src/backend/utilities/initKilt.ts +++ b/src/backend/utilities/initKilt.js @@ -1,7 +1,5 @@ import { connect } from '@kiltprotocol/sdk-js'; - import { configuration } from './configuration'; - -export async function initKilt(): Promise { +export async function initKilt() { await connect(configuration.blockchainEndpoint); } diff --git a/src/backend/utilities/keypairs.ts b/src/backend/utilities/keypairs.js similarity index 99% rename from src/backend/utilities/keypairs.ts rename to src/backend/utilities/keypairs.js index 893857f..d5ef10e 100644 --- a/src/backend/utilities/keypairs.ts +++ b/src/backend/utilities/keypairs.js @@ -1,25 +1,18 @@ import { Utils } from '@kiltprotocol/sdk-js'; import { cryptoWaitReady } from '@polkadot/util-crypto'; - import { configuration } from './configuration'; - export const keypairsPromise = (async () => { await cryptoWaitReady(); - const payer = Utils.Crypto.makeKeypairFromUri(configuration.payerMnemonic); - const authentication = Utils.Crypto.makeKeypairFromUri( configuration.authenticationMnemonic, ); - const assertionMethod = Utils.Crypto.makeKeypairFromUri( configuration.assertionMethodMnemonic, ); - const keyAgreement = Utils.Crypto.makeEncryptionKeypairFromSeed( Utils.Crypto.mnemonicToMiniSecret(configuration.keyAgreementMnemonic), ); - return { payer, authentication, diff --git a/src/backend/utilities/logger.ts b/src/backend/utilities/logger.js similarity index 99% rename from src/backend/utilities/logger.ts rename to src/backend/utilities/logger.js index 6f09b80..2c26f1e 100644 --- a/src/backend/utilities/logger.ts +++ b/src/backend/utilities/logger.js @@ -1,5 +1,4 @@ import { pino } from 'pino'; - export const logger = pino({ level: 'trace', transport: { diff --git a/src/backend/utilities/sessionStorage.ts b/src/backend/utilities/sessionStorage.js similarity index 52% rename from src/backend/utilities/sessionStorage.ts rename to src/backend/utilities/sessionStorage.js index 6e40d9c..372cac0 100644 --- a/src/backend/utilities/sessionStorage.ts +++ b/src/backend/utilities/sessionStorage.js @@ -1,81 +1,45 @@ -import { NextFunction, Request, Response } from 'express'; import { StatusCodes } from 'http-status-codes'; import NodeCache from 'node-cache'; -import { DidResourceUri, DidUri } from '@kiltprotocol/sdk-js'; - import { sessionHeader } from '../endpoints/sessionHeader'; - -export interface BasicSession { - sessionId: string; - did?: DidUri; - encryptionKeyUri?: DidResourceUri; - didChallenge?: string; - didConfirmed?: boolean; - requestChallenge?: string; -} - -export type Session = BasicSession & { - did: DidUri; - encryptionKeyUri: DidResourceUri; -}; - const sessionStorage = new NodeCache({ stdTTL: 60 * 60, useClones: false }); - -function getSessionById(sessionId: string): BasicSession { +function getSessionById(sessionId) { const session = sessionStorage.get(sessionId); if (!session) { throw new Error(`Unknown or expired session ${sessionId}`); } - return session as BasicSession; + return session; } - -function getBasicSession(request: Request): BasicSession { +function getBasicSession(request) { const sessionId = request.get(sessionHeader); - if (!sessionId) { throw new Error(`Required header ${sessionHeader} is missing`); } - return getSessionById(sessionId); } - -function getSession(request: Request): Session { +function getSession(request) { const session = getBasicSession(request); - const { did, didConfirmed, encryptionKeyUri } = session; if (!did || !didConfirmed || !encryptionKeyUri) { throw new Error('Unconfirmed DID'); } - return { ...session, did, encryptionKeyUri }; } - -export function setSession(session: BasicSession): void { +export function setSession(session) { sessionStorage.set(session.sessionId, session); } - -export function basicSessionMiddleware( - request: Request, - response: Response, - next: NextFunction, -): void { +export function basicSessionMiddleware(request, response, next) { try { const session = getBasicSession(request); - (request as Request & { session: BasicSession }).session = session; + request.session = session; next(); } catch (error) { response.status(StatusCodes.FORBIDDEN).send(error); } } - -export function sessionMiddleware( - request: Request, - response: Response, - next: NextFunction, -): void { +export function sessionMiddleware(request, response, next) { try { const session = getSession(request); - (request as Request & { session: Session }).session = session; + request.session = session; next(); } catch (error) { response.status(StatusCodes.FORBIDDEN).send(error); diff --git a/src/backend/utilities/supportedCType.ts b/src/backend/utilities/supportedCType.js similarity index 81% rename from src/backend/utilities/supportedCType.ts rename to src/backend/utilities/supportedCType.js index 1c4ddf7..127e277 100644 --- a/src/backend/utilities/supportedCType.ts +++ b/src/backend/utilities/supportedCType.js @@ -1,5 +1,3 @@ -import type { ICType } from '@kiltprotocol/sdk-js'; - import { discordCType } from '../cTypes/discordCType'; import { emailCType } from '../cTypes/emailCType'; import { githubCType } from '../cTypes/githubCType'; @@ -7,7 +5,6 @@ import { telegramCType } from '../cTypes/telegramCType'; import { twitchCType } from '../cTypes/twitchCType'; import { twitterCType } from '../cTypes/twitterCType'; import { youtubeCType } from '../cTypes/youtubeCType'; - export const supportedCTypeKeys = [ 'discord', 'email', @@ -16,11 +13,8 @@ export const supportedCTypeKeys = [ 'twitch', 'twitter', 'youtube', -] as const; - -export type SupportedCType = (typeof supportedCTypeKeys)[number]; - -export const supportedCTypes: Record = { +]; +export const supportedCTypes = { discord: discordCType, email: emailCType, github: githubCType, @@ -29,7 +23,6 @@ export const supportedCTypes: Record = { twitter: twitterCType, youtube: youtubeCType, }; - export const socialCTypeIds = [ supportedCTypes.discord.$id, supportedCTypes.telegram.$id, diff --git a/src/backend/utilities/trustedAttesters.js b/src/backend/utilities/trustedAttesters.js new file mode 100644 index 0000000..f60c8a6 --- /dev/null +++ b/src/backend/utilities/trustedAttesters.js @@ -0,0 +1,4 @@ +export const trustedAttesters = [ + 'did:kilt:4pehddkhEanexVTTzWAtrrfo2R7xPnePpuiJLC7shQU894aY', + 'did:kilt:4pnfkRn5UurBJTW92d9TaVLR2CqJdY4z5HPjrEbpGyBykare', // SocialKYC on Spiritnet +]; diff --git a/src/backend/utilities/trustedAttesters.ts b/src/backend/utilities/trustedAttesters.ts deleted file mode 100644 index 58dfe29..0000000 --- a/src/backend/utilities/trustedAttesters.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { DidUri } from '@kiltprotocol/sdk-js'; - -export const trustedAttesters: DidUri[] = [ - 'did:kilt:4pehddkhEanexVTTzWAtrrfo2R7xPnePpuiJLC7shQU894aY', // SocialKYC on Peregrine - 'did:kilt:4pnfkRn5UurBJTW92d9TaVLR2CqJdY4z5HPjrEbpGyBykare', // SocialKYC on Spiritnet -]; diff --git a/src/frontend/index.html b/src/frontend/index.html index 9a8a79f..3cfc579 100644 --- a/src/frontend/index.html +++ b/src/frontend/index.html @@ -14,6 +14,6 @@
- + diff --git a/src/frontend/index.tsx b/src/frontend/index.jsx similarity index 84% rename from src/frontend/index.tsx rename to src/frontend/index.jsx index 73fd5d7..3ef0cd8 100644 --- a/src/frontend/index.tsx +++ b/src/frontend/index.jsx @@ -1,91 +1,66 @@ -import type { - DidUri, - ICredentialPresentation, - IEncryptedMessage, -} from '@kiltprotocol/sdk-js'; - import { Fragment, useCallback, useEffect, useState } from 'react'; import { createRoot } from 'react-dom/client'; import ky from 'ky'; - import { socialCTypeIds, supportedCTypes, } from '../backend/utilities/supportedCType'; import { sessionHeader } from '../backend/endpoints/sessionHeader'; import { paths } from '../backend/endpoints/paths'; - import { apiWindow, getCompatibleExtensions, getSession, } from './utilities/session'; import { exceptionToError } from './utilities/exceptionToError'; - -const cTypes: Record = Object.fromEntries( +const cTypes = Object.fromEntries( Object.values(supportedCTypes).map(({ title, $id }) => [ $id.replace('kilt:ctype:', ''), title, ]), ); - function App() { const [processing, setProcessing] = useState(false); - const [error, setError] = useState<'closed' | 'rejected' | 'unknown'>(); - - const [presentation, setPresentation] = useState(); - const [attester, setAttester] = useState(); - const [revoked, setRevoked] = useState(); - const [isAttested, setIsAttested] = useState(false); - + const [error, setError] = useState(); + const [presentation, setPresentation] = useState(); + const [attester, setAttester] = useState(); + const [revoked, setRevoked] = useState(); + const [isAttested, setIsAttested] = useState(false); const { kilt } = apiWindow; - const [extensions, setExtensions] = useState(getCompatibleExtensions()); useEffect(() => { function handler() { setExtensions(getCompatibleExtensions()); } - window.dispatchEvent(new CustomEvent('kilt-dapp#initialized')); window.addEventListener('kilt-extension#initialized', handler); return () => window.removeEventListener('kilt-extension#initialized', handler); }, []); - const handleClick = useCallback( - async (extension: string) => { + async (extension) => { try { setPresentation(undefined); setProcessing(true); setError(undefined); - const session = await getSession(kilt[extension]); setProcessing(false); - const { sessionId } = session; const headers = { [sessionHeader]: sessionId }; - // define in advance how to handle the response from the extension await session.listen(async (message) => { - const result: { - presentation: ICredentialPresentation; - isAttested: boolean; - revoked?: boolean; - attester?: DidUri; - // decrypt the message and verify credential in the backend: - } = await ky.post(paths.verify, { headers, json: message }).json(); - + const result = await ky + .post(paths.verify, { headers, json: message }) + .json(); setPresentation(result.presentation); setAttester(result.attester); setRevoked(result.revoked); setIsAttested(result.isAttested); }); - // encrypt message on the backend - const message: IEncryptedMessage = await ky + const message = await ky .post(paths.requestCredential, { headers, json: socialCTypeIds }) .json(); - // forward the encrypted message to the extension await session.send(message); } catch (exception) { @@ -103,9 +78,7 @@ function App() { }, [kilt], ); - const contents = presentation && Object.entries(presentation.claim.contents); - return (

ProofCheck

@@ -206,6 +179,5 @@ function App() {
); } - -const root = createRoot(document.querySelector('#app') as HTMLElement); +const root = createRoot(document.querySelector('#app')); root.render(); diff --git a/src/frontend/utilities/exceptionToError.ts b/src/frontend/utilities/exceptionToError.js similarity index 84% rename from src/frontend/utilities/exceptionToError.ts rename to src/frontend/utilities/exceptionToError.js index 23ab7a9..a3d7efc 100644 --- a/src/frontend/utilities/exceptionToError.ts +++ b/src/frontend/utilities/exceptionToError.js @@ -3,6 +3,6 @@ * Typescript makes it very explicit that in the catch clause the caught exception can be of any type: * https://devblogs.microsoft.com/typescript/announcing-typescript-4-4/#use-unknown-catch-variables * */ -export function exceptionToError(exception: unknown): Error { +export function exceptionToError(exception) { return exception instanceof Error ? exception : new Error(String(exception)); } diff --git a/src/frontend/utilities/session.js b/src/frontend/utilities/session.js new file mode 100644 index 0000000..7916898 --- /dev/null +++ b/src/frontend/utilities/session.js @@ -0,0 +1,34 @@ +import { + checkSession, + getSessionValues, +} from '../../backend/endpoints/sessionApi'; +export const apiWindow = window; +export function getCompatibleExtensions() { + return Object.entries(apiWindow.kilt) + .filter(([, provider]) => provider.specVersion.startsWith('3.')) + .map(([name]) => name); +} +export async function getSession(provider) { + if (!provider) { + throw new Error('No provider'); + } + const { dAppEncryptionKeyUri, challenge, sessionId } = + await getSessionValues(); + const dAppName = 'ProofCheck'; + const session = await provider.startSession( + dAppName, + dAppEncryptionKeyUri, + challenge, + ); + const { encryptionKeyUri, encryptedChallenge, nonce } = session; + await checkSession( + { + encryptionKeyUri, + encryptedChallenge, + nonce, + }, + sessionId, + ); + const { name } = provider; + return { ...session, sessionId, name }; +} diff --git a/src/frontend/utilities/session.ts b/src/frontend/utilities/session.ts deleted file mode 100644 index 7d93afd..0000000 --- a/src/frontend/utilities/session.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { DidResourceUri, IEncryptedMessage } from '@kiltprotocol/sdk-js'; - -import { - checkSession, - getSessionValues, -} from '../../backend/endpoints/sessionApi'; - -interface PubSubSession { - listen: ( - callback: (message: IEncryptedMessage) => Promise, - ) => Promise; - close: () => Promise; - send: (message: IEncryptedMessage) => Promise; - encryptionKeyUri: DidResourceUri; - encryptedChallenge: string; - nonce: string; -} - -interface InjectedWindowProvider { - startSession: ( - dAppName: string, - dAppEncryptionKeyUri: DidResourceUri, - challenge: string, - ) => Promise; - name: string; - version: string; - specVersion: '3.0'; -} - -export const apiWindow = window as unknown as { - kilt: Record; -}; - -export function getCompatibleExtensions(): Array { - return Object.entries(apiWindow.kilt) - .filter(([, provider]) => provider.specVersion.startsWith('3.')) - .map(([name]) => name); -} - -export type Session = PubSubSession & { - sessionId: string; - name: string; -}; - -export async function getSession( - provider: InjectedWindowProvider, -): Promise { - if (!provider) { - throw new Error('No provider'); - } - - const { dAppEncryptionKeyUri, challenge, sessionId } = - await getSessionValues(); - const dAppName = 'ProofCheck'; - - const session = await provider.startSession( - dAppName, - dAppEncryptionKeyUri, - challenge, - ); - - const { encryptionKeyUri, encryptedChallenge, nonce } = session; - await checkSession( - { - encryptionKeyUri, - encryptedChallenge, - nonce, - }, - sessionId, - ); - - const { name } = provider; - - return { ...session, sessionId, name }; -} diff --git a/tsconfig.json b/tsconfig.json index 08534ac..ff26f59 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,12 +1,12 @@ { "compilerOptions": { "target": "ES2020", - "module": "CommonJS", + "module": "ESNext", "moduleResolution": "node", "rootDir": "./src", "outDir": "dist", "strict": true, - "jsx": "react-jsx", + "jsx": "preserve", "esModuleInterop": true, "skipLibCheck": true },