start implementing asset-api and m2m comms with identity-api
This commit is contained in:
parent
859844bcf4
commit
e566b16ea6
3 changed files with 161 additions and 3 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.keys/
|
||||
.assets/
|
|
@ -1,13 +1,68 @@
|
|||
import { readFileSync } from "node:fs"
|
||||
import { createSign } from "node:crypto";
|
||||
import Fastify from "fastify";
|
||||
|
||||
const { private: M2M_PRIVATE_KEY, public: M2M_PUBLIC_KEY } = loadM2MKeys()
|
||||
const M2M_ALGORITHM = "RSA-SHA512"
|
||||
|
||||
const ASSETS_FOLDER = "~/.assets/"
|
||||
const ASSET_API_LANDING_MESSAGE = "asset-api v1.0.0"
|
||||
|
||||
const IDENTITY_API_ENDPOINT = "http://localhost:3000"
|
||||
|
||||
const fastify = new Fastify({
|
||||
logger: true,
|
||||
})
|
||||
|
||||
fastify.get("/", async (request, reply) => {
|
||||
return ASSET_API_LANDING_MESSAGE
|
||||
return signString(ASSET_API_LANDING_MESSAGE)
|
||||
})
|
||||
|
||||
fastify.listen({ port: 3001 })
|
||||
fastify.get("/crypto/cert", async (request, reply) => {
|
||||
return M2M_PUBLIC_KEY
|
||||
})
|
||||
|
||||
fastify.get("/crypto/algo", (request, reply) => {
|
||||
return M2M_ALGORITHM
|
||||
})
|
||||
|
||||
fastify.get("/asset", {
|
||||
async handler(request, reply) {
|
||||
let url = new URL(IDENTITY_API_ENDPOINT)
|
||||
url.pathname = "/auth/account/fromkey"
|
||||
|
||||
let res = await fetch(url, {
|
||||
method: "POST",
|
||||
body: signString(JSON.stringify({
|
||||
session_key: request.query.session_key,
|
||||
}))
|
||||
})
|
||||
|
||||
return await res.text()
|
||||
},
|
||||
schema: {
|
||||
query: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string" },
|
||||
session_key: { type: "string" },
|
||||
},
|
||||
required: ["id", "session_key"],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
fastify.listen({ port: 3001 })
|
||||
|
||||
function loadM2MKeys() {
|
||||
return {
|
||||
private: readFileSync("../.keys/m2m-dev.pem").toString("ascii"),
|
||||
public: readFileSync("../.keys/m2m-dev.pub").toString("ascii"),
|
||||
}
|
||||
}
|
||||
|
||||
function signString(content) {
|
||||
let sign = createSign(M2M_ALGORITHM)
|
||||
sign.update(content)
|
||||
return `-----BEGIN SIGNED MESSAGE-----\n\n${content}\n\n-----BEGIN SIGNATURE-----\n\n${sign.sign(M2M_PRIVATE_KEY, "base64")}\n-----END SIGNATURE-----`
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
import { createVerify } from "node:crypto"
|
||||
import Fastify from 'fastify';
|
||||
import * as Jose from 'jose';
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import assert from "node:assert";
|
||||
|
||||
const IDENTITY_API_LANDING_MESSAGE = 'identity-api v1.0.0';
|
||||
const JWT_SECRET = new TextEncoder().encode(
|
||||
|
@ -8,6 +10,15 @@ const JWT_SECRET = new TextEncoder().encode(
|
|||
)
|
||||
const JWT_ALG = 'HS256'
|
||||
|
||||
const ASSET_API_ENDPOINT = "http://localhost:3001/"
|
||||
const ASSET_API_PUBKEY = await loadAssetPubkey()
|
||||
const ASSET_API_ALGORITHM = await loadAssetAlgo()
|
||||
|
||||
let session_keys = {
|
||||
'uid:005d6417-a23c-48bd-b348-eafeae649b94': 'e381ba8c-e18a-4bca-afce-b212b37bc26b',
|
||||
'key:e381ba8c-e18a-4bca-afce-b212b37bc26b': '005d6417-a23c-48bd-b348-eafeae649b94'
|
||||
};
|
||||
|
||||
let users = {
|
||||
'jane@identity.net': {
|
||||
uid: '005d6417-a23c-48bd-b348-eafeae649b94',
|
||||
|
@ -24,6 +35,47 @@ fastify.get('/', async (request, reply) => {
|
|||
return IDENTITY_API_LANDING_MESSAGE;
|
||||
})
|
||||
|
||||
fastify.post("/auth/account/fromkey", {
|
||||
async handler(request, reply) {
|
||||
if (!verifySignature(request.body, ASSET_API_PUBKEY)) {
|
||||
reply.statusCode(401)
|
||||
}
|
||||
|
||||
let body = JSON.parse(getContentFromSigned(request.body))
|
||||
|
||||
let uid = session_keys[`key:${body.session_key}`]
|
||||
let user = Object.values(users).filter(v => v.uid === uid)
|
||||
|
||||
assert(user.length === 1)
|
||||
user[0].password = undefined
|
||||
|
||||
return user[0]
|
||||
}
|
||||
})
|
||||
|
||||
fastify.get('/auth/genkey', {
|
||||
async handler(request, reply) {
|
||||
let jwt = request.headers['authorization'].replace('Bearer', '').trim()
|
||||
let { payload } = await Jose.jwtVerify(jwt, JWT_SECRET)
|
||||
|
||||
let key = uuidv4()
|
||||
session_keys[`uid:${payload.uid}`] = key
|
||||
session_keys[`key:${key}`] = payload.uid
|
||||
|
||||
return {
|
||||
session_key: session_keys[payload.uid]
|
||||
}
|
||||
},
|
||||
schema: {
|
||||
headers: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
authorization: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
fastify.get('/auth/account', {
|
||||
async handler(request, reply) {
|
||||
let jwt = request.headers['authorization'].replace('Bearer', '').trim()
|
||||
|
@ -118,4 +170,53 @@ fastify.post('/auth/register', {
|
|||
},
|
||||
})
|
||||
|
||||
fastify.listen({ port: 3000 })
|
||||
fastify.listen({ port: 3000 })
|
||||
|
||||
async function loadAssetPubkey() {
|
||||
let url = new URL(ASSET_API_ENDPOINT)
|
||||
url.pathname = "/crypto/cert"
|
||||
|
||||
let res = await fetch(url)
|
||||
return await res.text()
|
||||
}
|
||||
|
||||
async function loadAssetAlgo() {
|
||||
let url = new URL(ASSET_API_ENDPOINT)
|
||||
url.pathname = "/crypto/algo"
|
||||
|
||||
let res = await fetch(url)
|
||||
return await res.text()
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} content
|
||||
* @param {string} pubkeyText
|
||||
*/
|
||||
function verifySignature(content, pubkeyText) {
|
||||
let parts = content
|
||||
.replace("-----BEGIN SIGNED MESSAGE-----\n\n", "")
|
||||
.replace("\n-----END SIGNATURE-----", "")
|
||||
.split("\n\n-----BEGIN SIGNATURE-----\n\n")
|
||||
|
||||
assert(parts.length === 2);
|
||||
|
||||
let verify = createVerify(ASSET_API_ALGORITHM)
|
||||
verify.update(parts[0])
|
||||
|
||||
let pubkey = Buffer.from(pubkeyText, "ascii")
|
||||
let digest = Buffer.from(parts[1], "base64")
|
||||
|
||||
return verify.verify(pubkey, digest)
|
||||
}
|
||||
|
||||
function getContentFromSigned(content) {
|
||||
let parts = content
|
||||
.replace("-----BEGIN SIGNED MESSAGE-----\n\n", "")
|
||||
.replace("\n-----END SIGNATURE-----", "")
|
||||
.split("\n\n-----BEGIN SIGNATURE-----\n\n")
|
||||
|
||||
assert(parts.length === 2);
|
||||
|
||||
return parts[0]
|
||||
}
|
Loading…
Reference in a new issue