diff --git a/server/api/v1/client/metadata/endpoint.get.ts b/server/api/v1/client/metadata/endpoint.get.ts new file mode 100644 index 0000000..781dbfd --- /dev/null +++ b/server/api/v1/client/metadata/endpoint.get.ts @@ -0,0 +1,5 @@ +import { defineClientEventHandler } from "~/server/internal/clients/event-handler"; + +export default defineClientEventHandler(async (h3) => { + +}); \ No newline at end of file diff --git a/server/api/v1/client/metadata/versions.get.ts b/server/api/v1/client/metadata/versions.get.ts new file mode 100644 index 0000000..5d49db0 --- /dev/null +++ b/server/api/v1/client/metadata/versions.get.ts @@ -0,0 +1,20 @@ +import { defineClientEventHandler } from "~/server/internal/clients/event-handler"; +import prisma from "~/server/internal/db/database"; + +export default defineClientEventHandler(async (h3, {}) => { + const query = getQuery(h3); + const id = query.id?.toString(); + if (!id) + throw createError({ + statusCode: 400, + statusMessage: "No ID in request query", + }); + + const versions = await prisma.gameVersion.findMany({ + where: { + gameId: id, + }, + }); + + return versions; +}); diff --git a/server/internal/clients/README.md b/server/internal/clients/README.md index 7da5dbf..0d15850 100644 --- a/server/internal/clients/README.md +++ b/server/internal/clients/README.md @@ -3,24 +3,29 @@ Drop clients need to complete a handshake in order to connect to a Drop server. It also trades certificates for encrypted P2P connections. ## 1. Client requests a handshake -Client makes request: `POST /api/v1/client/auth/initiate` with information about the client. -Server responds with a URL to send the user to. It generates a device ID, which has all the metadata attached. +Client makes request: `POST /api/v1/client/auth/initiate` with information about the client. + +Server responds with a URL to send the user to. It generates a device ID, which has all the metadata attached. ## 2. User signs in -Client sends user to the provided URL (in external browser). User signs in using the existing authentication stack. -Server sends redirect to `drop://handshake/[id]/[token]`, where the token is an authentication token to generate the necessary certificates, and the ID is the client ID as generated by the server. +Client sends user to the provided URL (in external browser). User signs in using the existing authentication stack. + +Server sends redirect to `drop://handshake/[id]/[token]`, where the token is an authentication token to generate the necessary certificates, and the ID is the client ID as generated by the server. ## 3. Client requests certificates -Client makes request: `POST /api/v1/client/auth/handshake` with the token recieved in the previous step. -The server uses it's CA to generate a public-private key pair, the CN of the client ID. It then sends that pair, plus the CA's public key, to the client, which stores it all. +Client makes request: `POST /api/v1/client/auth/handshake` with the token recieved in the previous step. -*The certificate lasts for a year, and is rotated when it has 3 months or less left on it's expiry.* +The server uses it's CA to generate a public-private key pair, the CN of the client ID. It then sends that pair, plus the CA's public key, to the client, which stores it all. + +_The certificate lasts for a year, and is rotated when it has 3 months or less left on it's expiry._ ## 4.a Client requests one-time device endpoint -The client generates a nonce and signs it with their private key. This is then attached to any device-related request. + +The client uses a millisecond UNIX timestamp and signs it with their private key. This is then attached to any device-related request. It has 30 seconds to make the request before the nonce becomes invalid (this is to prevent credential stealing & reusing). ## 4.b Client wants a long-lived session -The client does the same as above, but instead makes the request to `POST /api/v1/client/auth/session`, which generates a session token that lasts for a day. This can then be used in the request to provide authentication. \ No newline at end of file + +The client does the same as above, but instead makes the request to `POST /api/v1/client/auth/session`, which generates a session token that lasts for a day. This can then be used in the request to provide authentication. diff --git a/server/internal/clients/event-handler.ts b/server/internal/clients/event-handler.ts index a99680c..556d45f 100644 --- a/server/internal/clients/event-handler.ts +++ b/server/internal/clients/event-handler.ts @@ -14,6 +14,8 @@ type ClientUtils = { fetchUser: () => Promise; }; +const NONCE_LENIENCE = 30_000; + export function defineClientEventHandler(handler: EventHandlerFunction) { return defineEventHandler(async (h3) => { const header = await getHeader(h3, "Authorization"); @@ -30,6 +32,21 @@ export function defineClientEventHandler(handler: EventHandlerFunction) { if (!clientId || !nonce || !signature) throw createError({ statusCode: 403 }); + const nonceTime = parseInt(nonce); + const current = Date.now(); + if ( + // If it was generated in the future + nonceTime > current || + // Or more than thirty seconds ago + nonceTime < current - NONCE_LENIENCE + ) { + // We reject the request + throw createError({ + statusCode: 403, + statusMessage: "Nonce expired", + }); + } + const ca = h3.context.ca; const certBundle = await ca.fetchClientCertificate(clientId); if (!certBundle) diff --git a/server/internal/downloads/README.md b/server/internal/downloads/README.md index 7369010..f1251aa 100644 --- a/server/internal/downloads/README.md +++ b/server/internal/downloads/README.md @@ -1,5 +1,8 @@ # Drop Download System -The Drop download system uses a torrent-*like* system. It is not torrenting, nor is it compatible with torrenting clients. +Drop downloads come in two types: -## Clients -Drop clients have built-in HTTP APIs that they forward with UPnP. This API exposes different capabilities for different Drop features, like download aggegration and P2P networking. When they sign on, they send a list of supported capabilities to the server. \ No newline at end of file +## Public (not quite) HTTPS downloads endpoints +These use public HTTPS certificate, and while are authenticated, are 'public' in the sense that they aren't P2P; anyone can connect to them + +## Private mTLS P2P endpoints +Drop clients use P2P mTLS aided by the P2P co-ordinator to transfer chunks between themselves. \ No newline at end of file