@pidgeon-ai/issuer 0.1.1 → 0.2.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.d.ts CHANGED
@@ -42,6 +42,46 @@ declare function staticDirectory(agents: DirectoryAgent[]): Directory;
42
42
  */
43
43
  declare function cachedDirectory(inner: Directory, seconds?: number, now?: () => number): Directory;
44
44
 
45
+ /**
46
+ * A person signs in at the issuer: Pidgeon Cloud's person flow, with the
47
+ * company's own identity provider where Cloud has its consent page.
48
+ *
49
+ * The site speaks to the issuer exactly as it speaks to Cloud (the
50
+ * authorization code flow with PKCE and a nonce, `@pidgeon-ai/sign-in`'s
51
+ * `login` and `callback`). The issuer is a relying party of the company's
52
+ * OpenID Connect provider (Keycloak, Auth0, Okta, Entra): it sends the
53
+ * browser there with its own PKCE, state and nonce, checks the ID token
54
+ * that comes back, and signs the person in to the site as `kind: person`
55
+ * with the provider's `sub` and a verified address at the domain.
56
+ *
57
+ * **No database.** What a store would hold travels sealed (AES-256-GCM
58
+ * under `PIDGEON_ISSUER_SECRET`): the sign-in in progress in an HttpOnly
59
+ * cookie on the issuer's host, bound to the browser that started it; the
60
+ * code, sixty seconds, used once through the issuer's replay store.
61
+ * Rotating the secret ends the sign-ins in flight, and nothing else.
62
+ */
63
+ /** The company's identity provider, as its relying party. */
64
+ interface PersonOptions {
65
+ /** At least 32 characters. Seals the sign-in in progress and the code. */
66
+ secret: string;
67
+ oidc: {
68
+ /** The provider's issuer, exactly as its ID tokens say: `https://sso.acme.com/realms/acme`. */
69
+ issuer: string;
70
+ /**
71
+ * Where the issuer fetches the provider's configuration, when it reaches
72
+ * the provider by another name than browsers do. Default
73
+ * `<issuer>/.well-known/openid-configuration`.
74
+ */
75
+ discoveryUrl?: string;
76
+ clientId: string;
77
+ clientSecret: string;
78
+ /** How the issuer authenticates at the provider's token endpoint. Default `client_secret_basic`. */
79
+ tokenAuth?: 'client_secret_basic' | 'client_secret_post';
80
+ };
81
+ /** Domains a person's verified address may be at. Default the issuer's domain. */
82
+ emailDomains?: string[];
83
+ }
84
+
45
85
  /**
46
86
  * The issuer's signing key, behind one interface so custody never touches
47
87
  * the protocol: a file today; AWS KMS or an HSM over PKCS#11 implement the
@@ -88,7 +128,9 @@ declare function seedSigner(seed: string, options?: {
88
128
  * work against it unchanged: the policy and the card at `post.<domain>`
89
129
  * (the draft's §discovery and §card), the token exchange an agent signs
90
130
  * (RFC 8693, Web Bot Auth's profile) and the ID token a site checks
91
- * (`@pidgeon-ai/sign-in`), grants (§grants). The formats are the app's own
131
+ * (`@pidgeon-ai/sign-in`), grants (§grants), and a person's sign-in, the
132
+ * authorization code flow with PKCE, through the company's own identity
133
+ * provider (`person.ts`). The formats are the app's own
92
134
  * (`src/lib/post/card.ts`, `grant.ts`, `pidgeon-sign-in.ts`), written out
93
135
  * here because a package cannot import the app; `tests/unit/issuer.test.ts`
94
136
  * holds them equal by reading what this signs with the app's own readers.
@@ -97,7 +139,7 @@ declare function seedSigner(seed: string, options?: {
97
139
  * used once through `replay`, in memory unless a shared store is passed.
98
140
  * Run one replica, or pass one.
99
141
  *
100
- * Left out on purpose: the person's flow, userinfo, introspection and
142
+ * Left out on purpose: userinfo, introspection and
101
143
  * revocation (each needs a store; an ID token lives five minutes and
102
144
  * revoking an agent is disabling it in the identity provider), dynamic
103
145
  * client registration, and the card log.
@@ -110,6 +152,9 @@ declare const PATHS: {
110
152
  readonly card: "/.well-known/post/card";
111
153
  readonly directory: "/.well-known/http-message-signatures-directory";
112
154
  readonly token: "/api/post/oidc/token";
155
+ readonly authorize: "/api/post/oidc/authorize";
156
+ /** The issuer's own: where the company's identity provider sends a person back. Cloud has none. */
157
+ readonly callback: "/api/post/oidc/callback";
113
158
  readonly jwks: "/api/post/oidc/jwks";
114
159
  readonly grants: "/api/post/grants";
115
160
  };
@@ -133,10 +178,14 @@ interface GrantRule {
133
178
  /** How long each signed copy lasts. Default 30. */
134
179
  days?: number;
135
180
  }
136
- /** A site, registered by hand: its `client_id` and the host its grants must be for. */
181
+ /**
182
+ * A site, registered by hand: its `client_id`, the host its grants must be
183
+ * for, and where a person's sign-in may return to, each matched exactly.
184
+ */
137
185
  interface StaticClient {
138
186
  client_id: string;
139
187
  host: string;
188
+ redirect_uris?: string[];
140
189
  }
141
190
  /** Remembers a signature's `keyid nonce` until it expires; false when it was used. */
142
191
  interface ReplayStore {
@@ -162,6 +211,8 @@ interface IssuerOptions {
162
211
  * make it fetch an arbitrary URL.
163
212
  */
164
213
  clientMetadataHosts?: string[];
214
+ /** A person's sign-in, through the company's identity provider. Absent: agents only. */
215
+ person?: PersonOptions;
165
216
  replay?: ReplayStore;
166
217
  fetch?: typeof fetch;
167
218
  /** Milliseconds since the epoch, for tests. */
@@ -272,6 +323,24 @@ interface IssuerFile {
272
323
  group?: string;
273
324
  };
274
325
  };
326
+ /**
327
+ * A person's sign-in, through the company's OpenID Connect provider, with
328
+ * the issuer as one of its clients. Needs `PIDGEON_ISSUER_SECRET`.
329
+ */
330
+ person?: {
331
+ oidc: {
332
+ /** The provider's issuer, as its ID tokens say. */
333
+ issuer: string;
334
+ /** Where the issuer reaches the provider's configuration, when not at its issuer URL. */
335
+ discovery_url?: string;
336
+ client_id: string;
337
+ /** The environment variable holding its secret. Default `PIDGEON_PERSON_CLIENT_SECRET`. */
338
+ client_secret_env?: string;
339
+ token_auth?: 'client_secret_basic' | 'client_secret_post';
340
+ };
341
+ /** Default the domain. */
342
+ email_domains?: string[];
343
+ };
275
344
  grants?: GrantRule[];
276
345
  clients?: StaticClient[];
277
346
  client_metadata_hosts?: string[];
@@ -304,4 +373,4 @@ declare function runIssuer(args: string[], env?: Record<string, string | undefin
304
373
  log?: (line: string) => void;
305
374
  }): Promise<string>;
306
375
 
307
- export { type Directory, type DirectoryAgent, type GrantRule, type Issuer, type IssuerFile, type IssuerOptions, type KeycloakOptions, PATHS, type ReplayStore, type RetiredKey, type Signer, type StaticClient, TOKEN_EXCHANGE, UsageError, cachedDirectory, createIssuer, fileSigner, keycloakDirectory, loadConfig, memoryReplayStore, runIssuer, seedSigner, serve, staticDirectory };
376
+ export { type Directory, type DirectoryAgent, type GrantRule, type Issuer, type IssuerFile, type IssuerOptions, type KeycloakOptions, PATHS, type PersonOptions, type ReplayStore, type RetiredKey, type Signer, type StaticClient, TOKEN_EXCHANGE, UsageError, cachedDirectory, createIssuer, fileSigner, keycloakDirectory, loadConfig, memoryReplayStore, runIssuer, seedSigner, serve, staticDirectory };
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  seedSigner,
14
14
  serve,
15
15
  staticDirectory
16
- } from "./chunk-CCOFMK5K.js";
16
+ } from "./chunk-BXZ2EI2Y.js";
17
17
  export {
18
18
  PATHS,
19
19
  TOKEN_EXCHANGE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pidgeon-ai/issuer",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "A Post issuer you run yourself, inside your own network: your domain signs your AI agents' cards and sign-ins with a key that never leaves it, for the agents your identity provider (Keycloak) says you have.",
5
5
  "keywords": [
6
6
  "agent-identity",