tokenCipher.ts
@file AES-256-GCM for the one thing this bot stores that is a real bearer…
Overview
@file AES-256-GCM for the one thing this bot stores that is a real bearer
credential rather than an opaque Discord user ID: the OAuth access and
refresh tokens Linked Roles needs to push updated metadata later without
re-prompting the user. D1 has no column-level encryption of its own, so
this exists to keep those tokens unreadable from a raw table dump —
OAUTH_ENCRYPTION_KEY is a Worker secret, never a D1 value.
used by oauthLinks.ts
API
Fexport async function encryptToken(plaintext: string, secretBase64: string): Promise<string>
iv || ciphertext, both base64-joined as one string, so storage is one
D1 column rather than two. A fresh random IV every call — AES-GCM must
never reuse an IV under the same key.
saveDiscordAccessToken · calls bytesToBase64, deriveKeyFexport async function decryptToken(blob: string, secretBase64: string): Promise<string>
Throws TokenCipherError on a wrong key, corrupted data, or a mismatched auth tag — GCM authenticates the ciphertext, so tampering is detected rather than silently producing garbage plaintext.
decryptedAccessToken · calls base64ToBytes, deriveKeyUndocumented (5)
TokenCipherError, TokenCipherError.constructor, base64ToBytes, bytesToBase64, deriveKey