@pidgeon-ai/issuer 0.1.1 → 0.4.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/README.md +85 -4
- package/dist/bin.js +1 -1
- package/dist/{chunk-CCOFMK5K.js → chunk-5GZMAASA.js} +810 -37
- package/dist/chunk-5GZMAASA.js.map +1 -0
- package/dist/index.d.ts +238 -10
- package/dist/index.js +9 -1
- package/package.json +13 -2
- package/dist/chunk-CCOFMK5K.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A Post issuer you run yourself, inside your own network. Your own domain
|
|
4
4
|
signs your AI agents' cards, sign-ins and grants, with a key that never
|
|
5
|
-
leaves you, for the agents your identity provider says you have. Nothing is
|
|
5
|
+
leaves you, for the agents your identity provider (Keycloak, Auth0, Okta) says you have. Nothing is
|
|
6
6
|
sent to Pidgeon: not the key, not the list of agents.
|
|
7
7
|
|
|
8
8
|
It is for companies that may not hand authentication to a third party. Pidgeon
|
|
@@ -32,8 +32,8 @@ with `pidgeon-issuer export <dir>` and serve it from any static host.
|
|
|
32
32
|
|
|
33
33
|
1. **The issuer's key**, once: `pidgeon-issuer keygen acme.com`. It writes
|
|
34
34
|
`pidgeon-issuer-key.json` (mode 0600). Mount it as a secret, or put the
|
|
35
|
-
seed in `PIDGEON_ISSUER_KEY`.
|
|
36
|
-
an HSM needs no change to the rest.
|
|
35
|
+
seed in `PIDGEON_ISSUER_KEY`. Or keep it in AWS KMS, below. Any `Signer`
|
|
36
|
+
fits in its place, so an HSM needs no change to the rest.
|
|
37
37
|
2. **Each agent's key**, on the agent's machine: `pidgeon-issuer keygen
|
|
38
38
|
acme.com --out ~/.config/pidgeon/id/acme.com.json`. Give the identity
|
|
39
39
|
provider the `key` line it prints; the private half stays with the agent.
|
|
@@ -73,6 +73,37 @@ rest, and its groups are what `grants` maps.
|
|
|
73
73
|
secret is `KEYCLOAK_CLIENT_SECRET`. `examples/self-hosted-issuer` runs all of
|
|
74
74
|
it in Docker.
|
|
75
75
|
|
|
76
|
+
## Auth0
|
|
77
|
+
|
|
78
|
+
An agent is a machine-to-machine application whose metadata names it:
|
|
79
|
+
`pidgeon_key` (the agent's public key), `pidgeon_address`, and optionally
|
|
80
|
+
`pidgeon_kind`, `pidgeon_groups` (comma-separated) and `pidgeon_disabled`
|
|
81
|
+
(`true` to switch it off; Auth0 applications have no enabled switch). The
|
|
82
|
+
issuer reads them with an application of its own authorized for the
|
|
83
|
+
Management API with `read:clients` only; its secret is
|
|
84
|
+
`AUTH0_CLIENT_SECRET`.
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
"directory": { "auth0": { "tenant": "acme.eu.auth0.com", "client_id": "<the issuer's app>" } }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Okta
|
|
91
|
+
|
|
92
|
+
An agent is an active OAuth service app whose profile names it:
|
|
93
|
+
`"profile": { "pidgeon": { "key": "…", "address": "…", "kind": "agent",
|
|
94
|
+
"groups": ["refunds"] } }`. Deactivate the app to switch it off. The issuer
|
|
95
|
+
reads them with a service app of its own granted `okta.apps.read` and a
|
|
96
|
+
read-only admin role, signing in with Private Key JWT; its RSA or P-256
|
|
97
|
+
private key is `OKTA_PRIVATE_KEY`.
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
"directory": { "okta": { "org": "https://acme.okta.com", "client_id": "<the issuer's app>" } }
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
On Auth0 and Okta the agent authenticates to the provider with its own RSA
|
|
104
|
+
or EC credential: neither accepts an Ed25519 key for that, so the one-key
|
|
105
|
+
trick is Keycloak's alone.
|
|
106
|
+
|
|
76
107
|
## Sign in to a site
|
|
77
108
|
|
|
78
109
|
A site lists your issuer beside Pidgeon's:
|
|
@@ -100,6 +131,56 @@ const { site } = await signInWithPidgeon(await loadIdentity('acme.com'), {
|
|
|
100
131
|
});
|
|
101
132
|
```
|
|
102
133
|
|
|
134
|
+
## The key in AWS KMS
|
|
135
|
+
|
|
136
|
+
Make an Ed25519 key that never leaves KMS and name it instead of a file:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
aws kms create-key --key-spec ECC_NIST_EDWARDS25519 --key-usage SIGN_VERIFY
|
|
140
|
+
aws kms create-alias --alias-name alias/pidgeon-issuer --target-key-id <key id>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
"key": { "kms": "alias/pidgeon-issuer", "region": "eu-west-1" }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The issuer's role needs `kms:GetPublicKey` and `kms:Sign` on that key and
|
|
148
|
+
nothing else; credentials come the AWS SDK's usual way. Install
|
|
149
|
+
`@aws-sdk/client-kms` beside the library (the container image has it). The
|
|
150
|
+
key is read once at start: an alias is pinned to the key it names then, and
|
|
151
|
+
every signature is checked against that key before it leaves.
|
|
152
|
+
|
|
153
|
+
## A person signs in, through your identity provider
|
|
154
|
+
|
|
155
|
+
Your staff sign in to the same sites as themselves, through your own OpenID
|
|
156
|
+
Connect provider (Keycloak, Auth0, Okta, Entra), with the flow Pidgeon Cloud
|
|
157
|
+
uses. Register the issuer at the provider as a confidential client whose
|
|
158
|
+
redirect URI is `https://post.acme.com/api/post/oidc/callback`, then:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
"person": {
|
|
162
|
+
"oidc": { "issuer": "https://sso.acme.com/realms/acme", "client_id": "pidgeon-issuer" }
|
|
163
|
+
},
|
|
164
|
+
"clients": [
|
|
165
|
+
{
|
|
166
|
+
"client_id": "client_shop",
|
|
167
|
+
"host": "shop.example",
|
|
168
|
+
"redirect_uris": ["https://shop.example/api/auth/pidgeon/callback"]
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
with the client's secret in `PIDGEON_PERSON_CLIENT_SECRET` and a random
|
|
174
|
+
`PIDGEON_ISSUER_SECRET` of 32 characters or more (`openssl rand -base64 32`),
|
|
175
|
+
which seals the sign-in in progress and the sixty-second code, so there is
|
|
176
|
+
still no database. The issuer will not start without it. Rotating it only
|
|
177
|
+
ends the sign-ins in flight.
|
|
178
|
+
|
|
179
|
+
A site that sets `issuer: 'https://post.acme.com'` signs people in there.
|
|
180
|
+
The person must have a verified address at your domain that is not an
|
|
181
|
+
agent's; the site gets `kind: "person"`, your provider's `sub` and that
|
|
182
|
+
address.
|
|
183
|
+
|
|
103
184
|
## What it checks before it signs anything
|
|
104
185
|
|
|
105
186
|
- The token request is signed by a key your identity provider gives an
|
|
@@ -113,7 +194,7 @@ const { site } = await signInWithPidgeon(await loadIdentity('acme.com'), {
|
|
|
113
194
|
|
|
114
195
|
## Left out
|
|
115
196
|
|
|
116
|
-
|
|
197
|
+
Userinfo, introspection and revocation endpoints (an ID
|
|
117
198
|
token lasts five minutes; revoking an agent is disabling it), dynamic client
|
|
118
199
|
registration, and Pidgeon's card log, which would mean sending us your agents.
|
|
119
200
|
Authorization stays yours: a grant is a relation (`customer:42#refunds.issue`
|