Initial Commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# Token match — `@suaveplan/api-auth-bearer-static`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`findTokenIndex` performs a constant-time lookup of a presented bearer
|
||||
token against a fixed table, returning the matching entry's index or `-1`
|
||||
if no entry matches. The function is the security boundary of the package:
|
||||
every other code path in the resolver assumes the match is timing-safe so
|
||||
an attacker cannot binary-search the token table by measuring response
|
||||
latency.
|
||||
|
||||
The implementation walks every entry, calling `constantTimeEqual` from
|
||||
`@suaveplan/crypto-utils` once per row. The match-tracking variable is
|
||||
written only on the first match; subsequent matches are ignored. Crucially
|
||||
the loop does not break on first match — exiting early would leak the
|
||||
position of the matching token via wall-clock timing.
|
||||
|
||||
## Features
|
||||
|
||||
- Wraps `@suaveplan/crypto-utils` `constantTimeEqual` over UTF-8 byte
|
||||
views.
|
||||
- Iterates the full table for every call — no early exit, no short-circuit
|
||||
on `presented === entry.token`.
|
||||
- Returns the first matching index or `-1`.
|
||||
- Handles `undefined` entries (defensive guard for sparse arrays).
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { findTokenIndex } from "@suaveplan/api-auth-bearer-static/constant-time";
|
||||
|
||||
const table = [
|
||||
{ token: "T1" },
|
||||
{ token: "T2" },
|
||||
];
|
||||
|
||||
const index = findTokenIndex("T2", table);
|
||||
// index === 1
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { findTokenIndex } from "@suaveplan/api-auth-bearer-static/constant-time";
|
||||
|
||||
interface TokenEntry {
|
||||
readonly token: string;
|
||||
readonly principalId: string;
|
||||
}
|
||||
|
||||
const table: ReadonlyArray<TokenEntry> = [
|
||||
{ token: "service-cron", principalId: "p1" },
|
||||
{ token: "service-admin", principalId: "p2" },
|
||||
];
|
||||
|
||||
function lookup(presented: string): TokenEntry | null {
|
||||
const idx = findTokenIndex(presented, table);
|
||||
return idx === -1 ? null : (table[idx] ?? null);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `findTokenIndex(presented: string, table: ReadonlyArray<{ token: string }>): number`
|
||||
— returns the index of the first entry whose `token` matches
|
||||
`presented`, or `-1` if no entry matches.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`constantTimeEqual` from `@suaveplan/crypto-utils` walks every byte of the
|
||||
two `Uint8Array` views and accumulates differences into an integer via
|
||||
bitwise OR; the function returns whether the accumulator stayed zero. The
|
||||
length-mismatch fast path is documented as not secret (token lengths are
|
||||
not secret in this adapter's threat model — the table is operator-supplied
|
||||
config). The `TextEncoder` round-trip ensures we compare bytes, not
|
||||
surrogate-pair code units; otherwise an attacker could compare a multi-
|
||||
byte presented token against a single-byte entry and learn from the
|
||||
length-mismatch shortcut whether their candidate had the right length.
|
||||
Reference in New Issue
Block a user