@jeffjassky/oauth-host 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +46 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +46 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +60 -6
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -400,9 +400,13 @@ export interface OAuthClientDoc {
|
|
|
400
400
|
name: string;
|
|
401
401
|
/**
|
|
402
402
|
* `public` clients hold no secret and authenticate with `client_id` alone —
|
|
403
|
-
* PKCE is the binding that stands in for it.
|
|
404
|
-
*
|
|
405
|
-
*
|
|
403
|
+
* PKCE is the binding that stands in for it. A `confidential` client can
|
|
404
|
+
* never downgrade itself by omitting its secret.
|
|
405
|
+
*
|
|
406
|
+
* Orthogonal to `registration`: a CIMD row is always public, but a public row
|
|
407
|
+
* is not always CIMD. `clients.create({ type: 'public' })` registers one by
|
|
408
|
+
* hand, which is the only path open to a client that wants PKCE-only and
|
|
409
|
+
* publishes no metadata document (Codex CLI).
|
|
406
410
|
*/
|
|
407
411
|
type: 'confidential' | 'public';
|
|
408
412
|
/**
|
|
@@ -574,16 +578,65 @@ export interface CreateClientSpec {
|
|
|
574
578
|
trusted?: boolean;
|
|
575
579
|
/** Supply a fixed id for a re-provisioned client. Generated when omitted. */
|
|
576
580
|
clientId?: string;
|
|
581
|
+
/**
|
|
582
|
+
* How this client authenticates at `/token`. **Defaults to `confidential`**,
|
|
583
|
+
* so an existing caller is unaffected.
|
|
584
|
+
*
|
|
585
|
+
* `public` generates no secret at all: the registration is `client_id` plus
|
|
586
|
+
* PKCE, `secrets` is empty, and `rotateSecret()` on it throws. Register one
|
|
587
|
+
* for a client that takes a `client_id` and nothing else — Codex CLI's MCP
|
|
588
|
+
* login has `oauth_client_id` and no `oauth_client_secret` field — and cannot
|
|
589
|
+
* use CIMD because it publishes no metadata document.
|
|
590
|
+
*/
|
|
591
|
+
type?: 'confidential' | 'public';
|
|
577
592
|
}
|
|
578
593
|
|
|
579
|
-
|
|
594
|
+
/** What `clients.create({ type: 'confidential' })` and `rotateSecret()` return. */
|
|
595
|
+
export interface CreatedConfidentialClient {
|
|
580
596
|
client: PublicClient;
|
|
581
597
|
clientId: string;
|
|
598
|
+
type: 'confidential';
|
|
582
599
|
/** Returned once. Only its SHA-256 is stored; there is no way to read it back. */
|
|
583
600
|
clientSecret: string;
|
|
584
601
|
}
|
|
585
602
|
|
|
603
|
+
/**
|
|
604
|
+
* What `clients.create({ type: 'public' })` returns.
|
|
605
|
+
*
|
|
606
|
+
* `clientSecret` is declared as `?: undefined` rather than omitted so that
|
|
607
|
+
* `created.clientSecret` still type-checks against the union below — and lands
|
|
608
|
+
* as `string | undefined`, which is what stops a provisioning script printing
|
|
609
|
+
* the word `undefined` into somebody's connector setup screen.
|
|
610
|
+
*/
|
|
611
|
+
export interface CreatedPublicClient {
|
|
612
|
+
client: PublicClient;
|
|
613
|
+
clientId: string;
|
|
614
|
+
type: 'public';
|
|
615
|
+
clientSecret?: undefined;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Discriminated on `type`, not a single interface with an optional secret.
|
|
620
|
+
*
|
|
621
|
+
* The alternative — widening `clientSecret` to `string | undefined` on one
|
|
622
|
+
* shape — reads as source-compatible and is not: `const s: string =
|
|
623
|
+
* created.clientSecret` stops compiling either way. The difference is what
|
|
624
|
+
* happens to the code that does not annotate. With an optional field a
|
|
625
|
+
* provisioning script keeps compiling and prints `undefined`; with a union the
|
|
626
|
+
* caller has to say which kind of registration it asked for before it can reach
|
|
627
|
+
* the secret at all.
|
|
628
|
+
*/
|
|
629
|
+
export type CreatedClient = CreatedConfidentialClient | CreatedPublicClient;
|
|
630
|
+
|
|
586
631
|
export interface ClientsApi {
|
|
632
|
+
/**
|
|
633
|
+
* The overloads exist so the default path keeps its precise type. A spec with
|
|
634
|
+
* no `type` (or `type: 'confidential'`) returns a `clientSecret: string`
|
|
635
|
+
* exactly as before; only a caller that asked for `public`, or that passes a
|
|
636
|
+
* spec whose `type` is not known statically, has to narrow.
|
|
637
|
+
*/
|
|
638
|
+
create(spec: CreateClientSpec & { type: 'public' }): Promise<CreatedPublicClient>;
|
|
639
|
+
create(spec: CreateClientSpec & { type?: 'confidential' }): Promise<CreatedConfidentialClient>;
|
|
587
640
|
create(spec: CreateClientSpec): Promise<CreatedClient>;
|
|
588
641
|
/**
|
|
589
642
|
* Issue a second valid secret and retire the current one after `retireAfter`
|
|
@@ -591,9 +644,10 @@ export interface ClientsApi {
|
|
|
591
644
|
* deployable without downtime.
|
|
592
645
|
*
|
|
593
646
|
* Throws on a **public** client. There is no secret to rotate, and returning
|
|
594
|
-
* one would hand the caller a credential the token endpoint refuses
|
|
647
|
+
* one would hand the caller a credential the token endpoint refuses — which
|
|
648
|
+
* is also why the return type is the confidential member alone.
|
|
595
649
|
*/
|
|
596
|
-
rotateSecret(clientId: string, opts?: { retireAfter?: number; label?: string }): Promise<
|
|
650
|
+
rotateSecret(clientId: string, opts?: { retireAfter?: number; label?: string }): Promise<CreatedConfidentialClient>;
|
|
597
651
|
update(clientId: string, patch: Partial<Omit<CreateClientSpec, 'clientId'>>): Promise<PublicClient>;
|
|
598
652
|
list(query?: { status?: 'active' | 'disabled'; limit?: number; skip?: number }): Promise<{ items: PublicClient[]; limit: number }>;
|
|
599
653
|
get(clientId: string): Promise<PublicClient | null>;
|