From ee9af4b9d3b898f3d6d7564f6c57f5e9552f49cd Mon Sep 17 00:00:00 2001 From: Simon Green Date: Fri, 12 Apr 2024 10:27:22 -0600 Subject: [PATCH 1/2] allow customized fn to get client ip closes #11 --- src/lib/api-keys.ts | 6 ++++-- src/lib/api.ts | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/lib/api-keys.ts b/src/lib/api-keys.ts index 68e4336..ccfb577 100644 --- a/src/lib/api-keys.ts +++ b/src/lib/api-keys.ts @@ -9,7 +9,7 @@ import de from 'bad-words-next/data/de.json' import type { KeyInfoData } from './key-info' import type { KeyStore } from './key-store' import type { TokenBuckets } from './bucket' -import { Api } from './api' +import { Api, type ClientIP } from './api' const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' const base62 = basex(BASE62) @@ -32,6 +32,7 @@ interface BaseParam { cookie?: string custom?: CustomFn key_length?: number + client_ip?: ClientIP } // SearchParam defines the URL searchParam to use for the API key @@ -146,8 +147,9 @@ export class ApiKeys { const key = await this.extract(event) const info = await this.validate(key) + const client_ip = this.options.client_ip ?? ((event) => event.getClientAddress()) - locals.api = new Api(event, this.buckets, key, info) + locals.api = new Api(event, this.buckets, client_ip, key, info) return await resolve(event) } diff --git a/src/lib/api.ts b/src/lib/api.ts index c2c2c43..9558812 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -4,6 +4,8 @@ import type { TokenBuckets } from './bucket' import type { KeyInfo } from './key-info' import type { Refill } from './refill' +export type ClientIP = (event: RequestEvent) => string + export class Api { private _name = '' private _cost = 1 @@ -40,6 +42,7 @@ export class Api { constructor( private readonly event: RequestEvent, private readonly bucket: TokenBuckets, + private readonly client_ip: ClientIP, public readonly key: string | null, public readonly info: KeyInfo | null, ) {} @@ -182,7 +185,7 @@ export class Api { } // TODO: hash client IP address (?) - const bucketPrefix = info?.user || this.event.getClientAddress() + const bucketPrefix = info?.user || this.client_ip(this.event) const bucketKey = bucketPrefix + ':' + this._name // apply rate limiting From 833f5b22c14a0d7cd046f0360b4f177e17c7c5c8 Mon Sep 17 00:00:00 2001 From: Simon Green Date: Fri, 12 Apr 2024 10:48:47 -0600 Subject: [PATCH 2/2] docs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a35a109..5a07c41 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,8 @@ There is an optional parameters object that can also control it's behavior by pa `custom` (default undefined) sets a custom key extraction & transform function that allows you to perform your own key lookups, perhaps via an existing session cookie or similar, and also allows you to transform any existing key that has been extracted using the previous settings - you might [prefix keys to indicate their usage as Stripe does](https://docs.stripe.com/docs/api/authentication) for instance. This will override all other methods if specified. +`client_ip` (default uses SvelteKit's `GetClientAddress`) sets a custom function to obtain the Client IP address, only called if `.anonymous()` is used. + `key_length` (default 32) sets the length, in bytes, of the API key to generate. If you want shorter API keys you could consider setting it to a lower value such as 24 or 16 (but too low risks conflicts when generating new keys). Keys are converted to human-readable format using Base62 for compactness and easy copy-paste. So as a more complete example your `src/lib/api_keys.ts` may end up looking something like this, but using whatever key store and token bucket implementations make sense for you: