zuplo 6.68.16 → 6.68.18
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/docs/articles/api-key-api.mdx +44 -13
- package/docs/articles/api-key-leak-detection.mdx +53 -29
- package/docs/articles/api-key-management.mdx +91 -37
- package/docs/articles/secure-tunnel.mdx +3 -3
- package/docs/articles/tunnel-setup.mdx +12 -0
- package/docs/articles/tunnel-troubleshooting.mdx +3 -0
- package/docs/concepts/api-keys.md +69 -9
- package/docs/dev-portal/auth-provider-api-identities.md +190 -0
- package/docs/policies/monetization-inbound/doc.md +79 -0
- package/docs/policies/monetization-inbound/intro.md +3 -3
- package/docs/programmable-api/zone-cache.mdx +24 -12
- package/package.json +4 -4
|
@@ -186,23 +186,53 @@ The response will look like this:
|
|
|
186
186
|
|
|
187
187
|
### Roll a Consumer's Keys
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
of
|
|
191
|
-
|
|
189
|
+
Key rotation is a critical part of API key lifecycle management. Instant
|
|
190
|
+
revocation of a key can break every system that depends on it, so Zuplo supports
|
|
191
|
+
**rolling transitions** — creating a new key while keeping the old key active
|
|
192
|
+
for a defined grace period.
|
|
193
|
+
|
|
194
|
+
When you call the roll key endpoint, Zuplo:
|
|
195
|
+
|
|
196
|
+
1. Creates a new key with no expiration for the consumer
|
|
197
|
+
2. Sets the `expiresOn` date on all existing keys to the value you specify
|
|
198
|
+
|
|
199
|
+
This gives consumers time to update their integration to the new key before the
|
|
200
|
+
old one stops working.
|
|
201
|
+
|
|
202
|
+
#### Choosing a transition period
|
|
203
|
+
|
|
204
|
+
The right `expiresOn` value depends on how quickly consumers can update their
|
|
205
|
+
keys:
|
|
206
|
+
|
|
207
|
+
- **Leaked key response** — set `expiresOn` to a past date or within minutes.
|
|
208
|
+
Security incidents demand immediate action; a brief disruption is preferable
|
|
209
|
+
to continued exposure.
|
|
210
|
+
- **Routine rotation** — 24 to 72 hours gives most teams enough time to
|
|
211
|
+
propagate the new key through CI/CD pipelines, environment variables, and
|
|
212
|
+
secret managers.
|
|
213
|
+
- **Scheduled rotation** — for large organizations, 7 to 14 days accommodates
|
|
214
|
+
deploy freezes, multi-team coordination, and staged rollouts.
|
|
215
|
+
|
|
216
|
+
:::tip{title="Multiple keys per consumer"}
|
|
217
|
+
|
|
218
|
+
Consumers can also manage rotation themselves by creating a second key,
|
|
219
|
+
deploying it, verifying traffic, and then deleting the old key on their own
|
|
220
|
+
schedule. This pattern avoids any expiration deadline and puts the consumer in
|
|
221
|
+
full control.
|
|
222
|
+
|
|
223
|
+
:::
|
|
192
224
|
|
|
193
225
|
:::tip{title="Tags for Request Authorization"}
|
|
194
226
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
roll key request below the `orgId` tag is set on the request - this ensures that
|
|
200
|
-
the consumer being updated is tagged with that org.
|
|
227
|
+
Most API requests support `tags` as query parameters, even on non-GET requests.
|
|
228
|
+
This lets you enforce conditions without a separate lookup. In the example
|
|
229
|
+
below, the `orgId` tag ensures the consumer being updated belongs to that
|
|
230
|
+
organization.
|
|
201
231
|
|
|
202
232
|
:::
|
|
203
233
|
|
|
204
|
-
The following call
|
|
205
|
-
|
|
234
|
+
The following call sets all existing keys to expire on the specified date and
|
|
235
|
+
creates a new key without an expiration.
|
|
206
236
|
|
|
207
237
|
```shell
|
|
208
238
|
export ORG_ID=1234
|
|
@@ -210,8 +240,9 @@ export CONSUMER_NAME=my-consumer
|
|
|
210
240
|
curl \
|
|
211
241
|
https://dev.zuplo.com/v1/accounts/$ACCOUNT_NAME/key-buckets/$BUCKET_NAME/consumers/$CONSUMER_NAME/roll-key?tag.orgId=$ORG_ID \
|
|
212
242
|
--request POST \
|
|
213
|
-
--header "Authorization: Bearer $API_KEY"
|
|
214
|
-
--
|
|
243
|
+
--header "Authorization: Bearer $API_KEY" \
|
|
244
|
+
--header "Content-Type: application/json" \
|
|
245
|
+
--data '{"expiresOn":"2026-04-19T00:00:00.000Z"}'
|
|
215
246
|
```
|
|
216
247
|
|
|
217
248
|
## Reference
|
|
@@ -3,29 +3,39 @@ title: API Key Leak Detection
|
|
|
3
3
|
sidebar_label: Leak Detection
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
## API
|
|
6
|
+
## API key format enables leak detection
|
|
7
7
|
|
|
8
|
-
Zuplo
|
|
9
|
-
|
|
10
|
-
to
|
|
8
|
+
Zuplo API keys use a structured format — `zpka_<random>_<checksum>` — that is
|
|
9
|
+
specifically designed to support automated leak detection. The `zpka_` prefix
|
|
10
|
+
allows scanning tools to identify Zuplo keys using a simple regex pattern, and
|
|
11
|
+
the checksum suffix allows the scanner to verify that a matched string is a real
|
|
12
|
+
Zuplo key (not a false positive) without making a database call.
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
This format is what makes Zuplo a
|
|
15
|
+
[GitHub secret scanning partner](https://github.blog/changelog/2022-07-13-zuplo-is-now-a-github-secret-scanning-partner/).
|
|
16
|
+
Leak detection is available to all Zuplo customers, including free.
|
|
14
17
|
|
|
15
|
-
##
|
|
18
|
+
## How leak detection works
|
|
16
19
|
|
|
17
20
|
API keys should never be stored in source control. Accidentally committing API
|
|
18
|
-
keys
|
|
19
|
-
|
|
21
|
+
keys is a common attack vector that leads to compromises of organizations both
|
|
22
|
+
large and small.
|
|
20
23
|
|
|
21
24
|
Zuplo participates in
|
|
22
25
|
[GitHub's Secret Scanning](https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning)
|
|
23
|
-
program
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
program. When a Zuplo API key is committed to any GitHub repository — public or
|
|
27
|
+
private — the following flow executes:
|
|
28
|
+
|
|
29
|
+
1. **Detection** — GitHub's scanners match the `zpka_` prefix pattern across all
|
|
30
|
+
repository content, including code, config files, and commit history.
|
|
31
|
+
2. **Checksum verification** — GitHub verifies the key's checksum signature to
|
|
32
|
+
confirm it is a structurally valid Zuplo key, filtering out false positives.
|
|
33
|
+
3. **Notification to Zuplo** — GitHub sends the matched key to Zuplo's secret
|
|
34
|
+
scanning endpoint.
|
|
35
|
+
4. **Database lookup** — Zuplo checks the key against its key service to
|
|
36
|
+
determine if it is an active key and which consumer it belongs to.
|
|
37
|
+
5. **Customer notification** — Zuplo sends leak alerts via email and in-app
|
|
38
|
+
notification, including the repository URL where the key was found.
|
|
29
39
|
|
|
30
40
|
## Leak Notifications
|
|
31
41
|
|
|
@@ -40,22 +50,17 @@ send. If you need the full API Key please contact support.
|
|
|
40
50
|
|
|
41
51
|
:::
|
|
42
52
|
|
|
43
|
-
## Recommended
|
|
44
|
-
|
|
45
|
-
If you receive an alert that an API Key has been leaked, we recommend taking one
|
|
46
|
-
of the following actions immediately.
|
|
47
|
-
|
|
48
|
-
### Notify Your Customer
|
|
53
|
+
## Recommended actions
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
and they get a new key.
|
|
55
|
+
If you receive an alert that an API key has been leaked, take one of the
|
|
56
|
+
following actions immediately.
|
|
53
57
|
|
|
54
|
-
### Roll the API
|
|
58
|
+
### Roll the API key
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
The fastest way to respond is to roll the consumer's key. Rolling creates a new
|
|
61
|
+
key and sets an expiration on all existing keys for that consumer. You can set
|
|
62
|
+
the `expiresOn` value to give the consumer a short transition period to update
|
|
63
|
+
their integration, or omit it to revoke the old key immediately.
|
|
59
64
|
|
|
60
65
|
```bash
|
|
61
66
|
export ACCOUNT_NAME="your-account-name"
|
|
@@ -69,7 +74,26 @@ curl --request POST \
|
|
|
69
74
|
--header 'Content-Type: application/json' \
|
|
70
75
|
--data '
|
|
71
76
|
{
|
|
72
|
-
"expiresOn": "
|
|
77
|
+
"expiresOn": "2026-04-17T00:00:00.000Z"
|
|
73
78
|
}
|
|
74
79
|
'
|
|
75
80
|
```
|
|
81
|
+
|
|
82
|
+
:::caution
|
|
83
|
+
|
|
84
|
+
For leaked keys, keep the transition period as short as possible. A leaked key
|
|
85
|
+
remains valid until the expiration date you set. If the leak represents an
|
|
86
|
+
active threat, revoke the key immediately by setting `expiresOn` to a past date
|
|
87
|
+
or deleting the key directly.
|
|
88
|
+
|
|
89
|
+
:::
|
|
90
|
+
|
|
91
|
+
For more on key rotation patterns and choosing transition periods, see
|
|
92
|
+
[Roll a Consumer's Keys](./api-key-api.mdx#roll-a-consumers-keys).
|
|
93
|
+
|
|
94
|
+
### Notify your customer
|
|
95
|
+
|
|
96
|
+
If the leaked key belongs to an end-user who manages their own keys through your
|
|
97
|
+
developer portal, notify them and instruct them to roll the key themselves. The
|
|
98
|
+
developer portal provides a self-serve roll key flow so consumers can generate a
|
|
99
|
+
new key and revoke the old one without contacting your team.
|
|
@@ -3,14 +3,10 @@ title: API Keys Overview
|
|
|
3
3
|
sidebar_label: Overview
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Zuplo
|
|
7
|
-
minutes.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
[best practices of API Key implementation](https://zuplo.com/blog/2022/12/01/api-key-authentication)
|
|
11
|
-
- includes [API Key Leak Detection & Notification](./api-key-leak-detection.mdx)
|
|
12
|
-
- offers [out of the box and customizable solutions](./api-key-end-users.mdx)
|
|
13
|
-
for sharing API Keys
|
|
6
|
+
Zuplo provides a fully managed API key authentication system that you can add to
|
|
7
|
+
your API in minutes. Every key is validated at the edge across 300+ data
|
|
8
|
+
centers, so authentication is fast for your consumers and offloads work from
|
|
9
|
+
your backend.
|
|
14
10
|
|
|
15
11
|
:::tip
|
|
16
12
|
|
|
@@ -19,30 +15,88 @@ To start using Zuplo API Keys in only a few minutes
|
|
|
19
15
|
|
|
20
16
|
:::
|
|
21
17
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
18
|
+
## Why API keys?
|
|
19
|
+
|
|
20
|
+
API keys are the standard authentication method for API-first companies like
|
|
21
|
+
Stripe, Twilio, and SendGrid. They are the right choice when you need to
|
|
22
|
+
authenticate an organization, system, or service rather than an individual user
|
|
23
|
+
acting on their own behalf (which is where OAuth excels).
|
|
24
|
+
|
|
25
|
+
Compared to JWT-based auth, API keys offer simpler developer experience — a
|
|
26
|
+
single string in a header, easy to test with curl, and no token refresh flow.
|
|
27
|
+
API keys are opaque, meaning they don't leak claim structure the way a decoded
|
|
28
|
+
JWT does. And because each key maps to a consumer identity in Zuplo, you can
|
|
29
|
+
revoke access instantly — you don't need to wait for a token to expire.
|
|
30
|
+
|
|
31
|
+
For a deeper comparison, see the Zuplo blog post on
|
|
32
|
+
[API key authentication best practices](https://zuplo.com/blog/api-key-authentication).
|
|
33
|
+
|
|
34
|
+
## What you get with Zuplo API keys
|
|
35
|
+
|
|
36
|
+
- **Thoughtful key format** — keys use a `zpka_` prefix, cryptographically
|
|
37
|
+
random body, and checksum signature. The prefix enables
|
|
38
|
+
[GitHub secret scanning](./api-key-leak-detection.mdx), the checksum allows
|
|
39
|
+
instant format validation without a database call, and the underscore
|
|
40
|
+
formatting means a double-click selects the entire key. See
|
|
41
|
+
[API key format](../concepts/api-keys.md#api-key-format) for the full
|
|
42
|
+
breakdown.
|
|
43
|
+
- **Leak detection** — Zuplo is a
|
|
44
|
+
[GitHub secret scanning partner](./api-key-leak-detection.mdx). If a key is
|
|
45
|
+
committed to any GitHub repository, you are notified immediately.
|
|
46
|
+
- **Self-serve key management** — give your API consumers a
|
|
47
|
+
[developer portal](./api-key-end-users.mdx) where they can create, view, roll,
|
|
48
|
+
and revoke their own keys.
|
|
49
|
+
- **Edge validation** — keys are validated at the edge, keeping latency low and
|
|
50
|
+
your backend protected. See
|
|
51
|
+
[how validation works](../concepts/api-keys.md#how-validation-works) for
|
|
52
|
+
details on caching and replication.
|
|
53
|
+
- **Key rotation with transition periods** — the
|
|
54
|
+
[roll-key API](./api-key-api.mdx#roll-a-consumers-keys) creates a new key and
|
|
55
|
+
sets an expiration on existing keys, so consumers have time to migrate without
|
|
56
|
+
downtime.
|
|
57
|
+
|
|
58
|
+
## Fully managed global infrastructure
|
|
59
|
+
|
|
60
|
+
Zuplo builds and manages the API key infrastructure so you don't have to. The
|
|
61
|
+
service handles key storage, global replication, edge caching, and validation at
|
|
62
|
+
scale — supporting millions of keys and virtually unlimited throughput.
|
|
63
|
+
|
|
64
|
+
Keys replicate around the world in seconds. When a key is created, revoked, or
|
|
65
|
+
deleted, the change propagates to all 300+ edge locations within seconds,
|
|
66
|
+
ensuring your API is never open to unauthorized access for longer than the
|
|
67
|
+
configured cache TTL.
|
|
68
|
+
|
|
69
|
+
## How authentication works
|
|
70
|
+
|
|
71
|
+
When a request arrives at Zuplo with an API key, the
|
|
72
|
+
[API Key Authentication policy](../policies/api-key-inbound.mdx) validates it
|
|
73
|
+
through a multi-step process:
|
|
74
|
+
|
|
75
|
+
1. **Format check** — the key is checked for the correct `zpka_` prefix and
|
|
76
|
+
structure. Malformed keys are rejected immediately without any network call.
|
|
77
|
+
2. **Checksum validation** — the key's built-in checksum signature is verified.
|
|
78
|
+
This catches typos and garbage keys in microseconds.
|
|
79
|
+
3. **Cache lookup** — the edge checks its local cache for this key. If the key
|
|
80
|
+
was recently validated (or recently rejected), the cached result is used.
|
|
81
|
+
4. **Key service lookup** — if the key isn't cached, Zuplo's globally
|
|
82
|
+
distributed key service is queried. The result is then cached for the
|
|
83
|
+
configured TTL (default 60 seconds).
|
|
84
|
+
|
|
85
|
+
After successful validation, `request.user` is populated with the consumer's
|
|
86
|
+
identity and metadata, which downstream policies and handlers can use for
|
|
87
|
+
authorization, rate limiting, and routing. See
|
|
88
|
+
[how validation works](../concepts/api-keys.md#how-validation-works) for more
|
|
89
|
+
details.
|
|
90
|
+
|
|
91
|
+
:::note
|
|
92
|
+
|
|
93
|
+
The `cacheTtlSeconds` option on the API Key Authentication policy controls how
|
|
94
|
+
long validation results are cached at each edge location. Higher values reduce
|
|
95
|
+
latency but delay the effect of key revocation. A revoked key could still be
|
|
96
|
+
accepted for up to `cacheTtlSeconds` after revocation. The default of 60 seconds
|
|
97
|
+
is a good balance for most use cases.
|
|
35
98
|
|
|
36
|
-
|
|
37
|
-
unauthorized access. Authorization checks happen at the edge in 300+ data
|
|
38
|
-
centers around the world. This keeps load off your backend and keeps your API
|
|
39
|
-
fast for your end-users.
|
|
40
|
-
|
|
41
|
-
Zuplo manages all the complexity of replication, caching, and verifying your API
|
|
42
|
-
keys so you don't have to.
|
|
43
|
-
|
|
44
|
-
Adding API Key authentication using Zuplo takes only a few minutes.
|
|
45
|
-
[See the quickstart to get started](../articles/step-3-add-api-key-auth.mdx).
|
|
99
|
+
:::
|
|
46
100
|
|
|
47
101
|
## Key Concepts
|
|
48
102
|
|
|
@@ -92,9 +146,9 @@ API Keys are the actual string value used to authenticate with an API. Unlike
|
|
|
92
146
|
some other forms of bearer tokens, API Keys don't contain any actual data within
|
|
93
147
|
the key itself.
|
|
94
148
|
|
|
95
|
-
Zuplo API Keys
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
149
|
+
Zuplo API Keys use a structured format: a `zpka_` prefix, a cryptographically
|
|
150
|
+
random body, and a checksum signature. This format enables
|
|
151
|
+
[leak detection via GitHub secret scanning](./api-key-leak-detection.mdx) and
|
|
152
|
+
allows instant format validation without a database call. Zuplo's API Key
|
|
153
|
+
management service also supports custom key formats (enterprise plan required),
|
|
154
|
+
though custom formats lose leak detection support.
|
|
@@ -19,9 +19,9 @@ private API. The benefits of a secure tunnel are:
|
|
|
19
19
|
|
|
20
20
|
## How does the Tunnel Work?
|
|
21
21
|
|
|
22
|
-
The Zuplo tunnel can run on virtually any infrastructure. The most
|
|
23
|
-
users install the tunnel is as a Docker container, but
|
|
24
|
-
|
|
22
|
+
The Zuplo tunnel can run on virtually any Linux-based infrastructure. The most
|
|
23
|
+
common way users install the tunnel is as a Docker container, but it can also
|
|
24
|
+
run directly on a Linux virtual machine or bare-metal server. The tunnel itself
|
|
25
25
|
is a lightweight service that when started makes an outbound connection to the
|
|
26
26
|
Zuplo network and then through to your Zuplo Gateway.
|
|
27
27
|
|
|
@@ -4,6 +4,18 @@ title: Tunnel Setup & Use
|
|
|
4
4
|
|
|
5
5
|
<EnterpriseFeature name="Secure tunneling" />
|
|
6
6
|
|
|
7
|
+
:::caution{title="Platform Requirement"}
|
|
8
|
+
|
|
9
|
+
The Zuplo tunnel service is officially supported on **Linux** only. The
|
|
10
|
+
[`zuplo/tunnel` Docker image](https://hub.docker.com/r/zuplo/tunnel) is a Linux
|
|
11
|
+
container, and any non-containerized deployment must target a Linux host. Cloud
|
|
12
|
+
container platforms (AWS ECS, Azure Container Instances, GCP Cloud Run,
|
|
13
|
+
Kubernetes) run Linux containers by default and require no extra configuration.
|
|
14
|
+
If you are deploying on a virtual machine or bare metal, ensure the host runs a
|
|
15
|
+
supported Linux distribution.
|
|
16
|
+
|
|
17
|
+
:::
|
|
18
|
+
|
|
7
19
|
## Setting up Tunnels
|
|
8
20
|
|
|
9
21
|
A tunnel is a way to expose your _internal services_ to the Zuplo gateway
|
|
@@ -7,6 +7,9 @@ work together. When setting up your tunnel it's common for traffic to initially
|
|
|
7
7
|
not reach your destination initially. This is almost always caused by
|
|
8
8
|
configurations (firewalls, VPCs, IAM rules, etc.) in your internal network.
|
|
9
9
|
|
|
10
|
+
As a quick sanity check, verify that the tunnel is running on a
|
|
11
|
+
[supported Linux host](./secure-tunnel.mdx) before investigating further.
|
|
12
|
+
|
|
10
13
|
## Tunnel status
|
|
11
14
|
|
|
12
15
|
As a first step, check if the tunnel is up and running. You can use the
|
|
@@ -69,18 +69,78 @@ authorization and routing. Keep it small.
|
|
|
69
69
|
**Tags** are key-value pairs used only for management (querying, filtering,
|
|
70
70
|
organizing consumers via the API). Tags are not sent to the runtime.
|
|
71
71
|
|
|
72
|
-
##
|
|
72
|
+
## When to use API keys
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
API keys are the right authentication method when you need to identify an
|
|
75
|
+
organization, system, or service calling your API. Companies like Stripe,
|
|
76
|
+
Twilio, and SendGrid use API keys because they offer a simple developer
|
|
77
|
+
experience — a single string in a header, easy to test with curl, and no token
|
|
78
|
+
refresh flow.
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
Use API keys when:
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
- Your API consumers are developers integrating server-to-server
|
|
83
|
+
- You want simple, low-friction authentication (no OAuth dance)
|
|
84
|
+
- You need to identify and rate-limit individual consumers
|
|
85
|
+
- You want instant revocation capability (unlike JWTs, which are valid until
|
|
86
|
+
expiry)
|
|
87
|
+
|
|
88
|
+
Use OAuth or JWT when:
|
|
89
|
+
|
|
90
|
+
- You need to authenticate on behalf of an individual end-user
|
|
91
|
+
- Your use case requires delegated authorization with scoped permissions
|
|
92
|
+
- You are building a user-facing login flow
|
|
93
|
+
|
|
94
|
+
API keys and JWT/OAuth are not mutually exclusive. Many APIs use API keys for
|
|
95
|
+
system-level access and OAuth for user-level actions.
|
|
96
|
+
|
|
97
|
+
## API key format
|
|
98
|
+
|
|
99
|
+
Zuplo API keys use a structured three-part format:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
zpka_<random>_<checksum>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Each part serves a specific purpose:
|
|
106
|
+
|
|
107
|
+
- **`zpka_` prefix** — identifies the string as a Zuplo API key. This enables
|
|
108
|
+
automated [leak detection](../articles/api-key-leak-detection.mdx) via GitHub
|
|
109
|
+
secret scanning (scanners match the prefix pattern), helps support teams
|
|
110
|
+
identify key types during debugging, and distinguishes Zuplo keys from other
|
|
111
|
+
credentials in logs and config files.
|
|
112
|
+
- **Random body** — a cryptographically random string that serves as the actual
|
|
113
|
+
credential. This portion is generated using a secure random source and
|
|
114
|
+
provides the entropy that makes each key unique.
|
|
115
|
+
- **Checksum signature** — a suffix that allows instant format validation. When
|
|
116
|
+
a request arrives, Zuplo can verify the checksum mathematically in
|
|
117
|
+
microseconds to confirm the key is structurally valid before making any
|
|
118
|
+
network call. This rejects typos, truncated keys, and garbage strings without
|
|
119
|
+
touching the database.
|
|
120
|
+
|
|
121
|
+
The underscore separators are also intentional — they ensure that a double-click
|
|
122
|
+
on the key in most text editors and terminals selects the entire string,
|
|
123
|
+
reducing the chance of accidentally copying a partial key.
|
|
124
|
+
|
|
125
|
+
### Leak detection
|
|
126
|
+
|
|
127
|
+
This key format is what makes Zuplo an official
|
|
128
|
+
[GitHub secret scanning partner](https://github.blog/changelog/2022-07-13-zuplo-is-now-a-github-secret-scanning-partner/).
|
|
129
|
+
If a Zuplo API key is committed to any GitHub repository, GitHub detects it,
|
|
130
|
+
verifies the checksum, and notifies Zuplo. You receive an alert with the token
|
|
131
|
+
and the repository URL where it was found. Leak detection is enabled
|
|
132
|
+
automatically for all keys using the standard format and is available to all
|
|
133
|
+
customers, including free.
|
|
134
|
+
|
|
135
|
+
See [API Key Leak Detection](../articles/api-key-leak-detection.mdx) for the
|
|
136
|
+
full scan flow and recommended response actions.
|
|
137
|
+
|
|
138
|
+
:::note
|
|
139
|
+
|
|
140
|
+
Enterprise customers can use custom key formats, but custom formats do not
|
|
141
|
+
support leak detection.
|
|
142
|
+
|
|
143
|
+
:::
|
|
84
144
|
|
|
85
145
|
## Self-serve key management
|
|
86
146
|
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Custom API Identity Plugin
|
|
3
|
+
sidebar_label: Custom API Identities
|
|
4
|
+
navigation_icon: fingerprint
|
|
5
|
+
description:
|
|
6
|
+
Learn how to create a custom API identity plugin for the Dev Portal to
|
|
7
|
+
authenticate API playground requests with OAuth JWT tokens or other custom
|
|
8
|
+
credentials.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
The Dev Portal API playground allows users to make authenticated requests to
|
|
12
|
+
your API. By default, users can enter credentials (such as API keys) directly in
|
|
13
|
+
the playground's Authorize dialog. However, when your API uses OAuth or OpenID
|
|
14
|
+
Connect for authentication, you need a custom API identity plugin to
|
|
15
|
+
automatically attach the user's access token to playground requests.
|
|
16
|
+
|
|
17
|
+
:::tip
|
|
18
|
+
|
|
19
|
+
If you use Zuplo's built-in API key management, you do not need to create a
|
|
20
|
+
custom identity plugin. API keys work automatically in the playground without
|
|
21
|
+
any additional configuration.
|
|
22
|
+
|
|
23
|
+
:::
|
|
24
|
+
|
|
25
|
+
## When to use a custom identity plugin
|
|
26
|
+
|
|
27
|
+
Use a custom API identity plugin when:
|
|
28
|
+
|
|
29
|
+
- Your API requires OAuth 2.0 or OpenID Connect bearer tokens for
|
|
30
|
+
authentication.
|
|
31
|
+
- You want to automatically attach the signed-in user's access token to
|
|
32
|
+
playground requests.
|
|
33
|
+
- You need to apply custom authentication logic, such as using a specific token
|
|
34
|
+
format or adding extra headers.
|
|
35
|
+
|
|
36
|
+
## Prerequisites
|
|
37
|
+
|
|
38
|
+
Before creating an identity plugin, configure an
|
|
39
|
+
[authentication provider](./zudoku/configuration/authentication.md) for the Dev
|
|
40
|
+
Portal. The authentication provider handles user sign-in and token management.
|
|
41
|
+
The identity plugin then bridges the user's session to the API playground.
|
|
42
|
+
|
|
43
|
+
For example, using OpenID Connect:
|
|
44
|
+
|
|
45
|
+
```ts title="zudoku.config.ts"
|
|
46
|
+
const config = {
|
|
47
|
+
authentication: {
|
|
48
|
+
type: "openid",
|
|
49
|
+
clientId: "<your-client-id>",
|
|
50
|
+
issuer: "https://your-idp.example.com",
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Creating an identity plugin
|
|
56
|
+
|
|
57
|
+
Use `createApiIdentityPlugin` from `zudoku/plugins` to define how the playground
|
|
58
|
+
authenticates API requests. The plugin provides a `getIdentities` function that
|
|
59
|
+
returns one or more identities, each with an `authorizeRequest` function that
|
|
60
|
+
modifies outgoing requests.
|
|
61
|
+
|
|
62
|
+
### Using `signRequest`
|
|
63
|
+
|
|
64
|
+
The simplest approach uses `context.authentication.signRequest()`, which
|
|
65
|
+
automatically attaches the user's access token to the request:
|
|
66
|
+
|
|
67
|
+
```ts title="zudoku.config.ts"
|
|
68
|
+
import { createApiIdentityPlugin } from "zudoku/plugins";
|
|
69
|
+
|
|
70
|
+
const config = {
|
|
71
|
+
authentication: {
|
|
72
|
+
type: "openid",
|
|
73
|
+
clientId: "<your-client-id>",
|
|
74
|
+
issuer: "https://your-idp.example.com",
|
|
75
|
+
},
|
|
76
|
+
plugins: [
|
|
77
|
+
createApiIdentityPlugin({
|
|
78
|
+
getIdentities: async (context) => [
|
|
79
|
+
{
|
|
80
|
+
id: "oauth-token",
|
|
81
|
+
label: "OAuth Token",
|
|
82
|
+
authorizeRequest: (request) => {
|
|
83
|
+
return context.authentication?.signRequest(request);
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
}),
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Using `getAccessToken`
|
|
93
|
+
|
|
94
|
+
If you need more control over how the token is applied, use
|
|
95
|
+
`context.authentication.getAccessToken()` to retrieve the token directly. This
|
|
96
|
+
is useful when you need to set a specific header format or add the token to a
|
|
97
|
+
query parameter:
|
|
98
|
+
|
|
99
|
+
```ts title="zudoku.config.ts"
|
|
100
|
+
import { createApiIdentityPlugin } from "zudoku/plugins";
|
|
101
|
+
|
|
102
|
+
const config = {
|
|
103
|
+
authentication: {
|
|
104
|
+
type: "openid",
|
|
105
|
+
clientId: "<your-client-id>",
|
|
106
|
+
issuer: "https://your-idp.example.com",
|
|
107
|
+
},
|
|
108
|
+
plugins: [
|
|
109
|
+
createApiIdentityPlugin({
|
|
110
|
+
getIdentities: async (context) => [
|
|
111
|
+
{
|
|
112
|
+
id: "jwt-bearer",
|
|
113
|
+
label: "JWT Bearer Token",
|
|
114
|
+
authorizeRequest: async (request) => {
|
|
115
|
+
const token = await context.authentication?.getAccessToken();
|
|
116
|
+
|
|
117
|
+
if (!token) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
"No access token available. Please sign in again.",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
request.headers.set("Authorization", `Bearer ${token}`);
|
|
124
|
+
return request;
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
}),
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## How it works
|
|
134
|
+
|
|
135
|
+
When a user signs in to the Dev Portal and makes a request from the API
|
|
136
|
+
playground:
|
|
137
|
+
|
|
138
|
+
1. The Dev Portal displays the configured identities as selectable options in
|
|
139
|
+
the playground.
|
|
140
|
+
2. When the user selects an identity and sends a request, the `authorizeRequest`
|
|
141
|
+
function runs before the request is sent.
|
|
142
|
+
3. The function modifies the request (typically by adding an `Authorization`
|
|
143
|
+
header) and returns it.
|
|
144
|
+
4. The playground sends the modified request to your API.
|
|
145
|
+
|
|
146
|
+
## The `ApiIdentity` interface
|
|
147
|
+
|
|
148
|
+
Each identity returned by `getIdentities` has the following properties:
|
|
149
|
+
|
|
150
|
+
| Property | Type | Description |
|
|
151
|
+
| ------------------ | --------------------------------------------------- | ------------------------------------------------------------------ |
|
|
152
|
+
| `id` | `string` | A unique identifier for the identity. |
|
|
153
|
+
| `label` | `string` | A human-readable name displayed in the playground identity picker. |
|
|
154
|
+
| `authorizeRequest` | `(request: Request) => Request \| Promise<Request>` | A function that adds authentication credentials to the request. |
|
|
155
|
+
|
|
156
|
+
## Multiple identities
|
|
157
|
+
|
|
158
|
+
You can return multiple identities from `getIdentities` to give users a choice
|
|
159
|
+
of authentication methods:
|
|
160
|
+
|
|
161
|
+
```ts title="zudoku.config.ts"
|
|
162
|
+
createApiIdentityPlugin({
|
|
163
|
+
getIdentities: async (context) => [
|
|
164
|
+
{
|
|
165
|
+
id: "oauth-token",
|
|
166
|
+
label: "OAuth Token",
|
|
167
|
+
authorizeRequest: (request) => {
|
|
168
|
+
return context.authentication?.signRequest(request);
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: "custom-header",
|
|
173
|
+
label: "Custom API Header",
|
|
174
|
+
authorizeRequest: (request) => {
|
|
175
|
+
request.headers.set("X-Custom-Auth", "my-value");
|
|
176
|
+
return request;
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Related pages
|
|
184
|
+
|
|
185
|
+
- [Authentication](./zudoku/configuration/authentication.md) - Configure a
|
|
186
|
+
sign-in provider for the Dev Portal.
|
|
187
|
+
- [OAuth Security Schemes](./zudoku/configuration/oauth-security-schemes.md) -
|
|
188
|
+
Learn how OAuth security schemes work with the Dev Portal.
|
|
189
|
+
- [Custom Plugins](./zudoku/custom-plugins.md) - Build other types of custom
|
|
190
|
+
plugins for the Dev Portal.
|
|
@@ -59,6 +59,85 @@ import { MonetizationInboundPolicy } from "@zuplo/runtime";
|
|
|
59
59
|
const meters = MonetizationInboundPolicy.getMeters(context);
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
## Subscription data (for customization)
|
|
63
|
+
|
|
64
|
+
The monetization policy validates the API key and attaches the **subscription**
|
|
65
|
+
to the request context. You can read it later in your pipeline/handler to
|
|
66
|
+
customize behavior (limits, feature flags, plan-based behavior, etc.).
|
|
67
|
+
|
|
68
|
+
### Read subscription data
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { MonetizationInboundPolicy } from "@zuplo/runtime";
|
|
72
|
+
|
|
73
|
+
const subscription = MonetizationInboundPolicy.getSubscriptionData(context);
|
|
74
|
+
|
|
75
|
+
if (!subscription) {
|
|
76
|
+
// The monetization policy may not have run yet, or the request was rejected earlier.
|
|
77
|
+
// Decide what behavior you want in this case.
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Common fields you’ll likely use
|
|
82
|
+
|
|
83
|
+
- **Identity**: `subscription.id`, `subscription.customerId`,
|
|
84
|
+
`subscription.name`
|
|
85
|
+
- **Plan**: `subscription.plan.key`, `subscription.plan.version`
|
|
86
|
+
- **Status & dates**: `subscription.status`, `subscription.activeFrom`,
|
|
87
|
+
`subscription.activeTo`, `subscription.nextBillingDate`
|
|
88
|
+
- **Entitlements**: `subscription.entitlements[meterName]` →
|
|
89
|
+
`{ balance, usage, overage, hasAccess }`
|
|
90
|
+
- **Payment** (when present): `subscription.paymentStatus.status`,
|
|
91
|
+
`subscription.paymentStatus.isFirstPayment`
|
|
92
|
+
|
|
93
|
+
### Example: gate a feature by plan
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { MonetizationInboundPolicy } from "@zuplo/runtime";
|
|
97
|
+
|
|
98
|
+
const subscription = MonetizationInboundPolicy.getSubscriptionData(context);
|
|
99
|
+
if (subscription?.plan.key !== "enterprise") {
|
|
100
|
+
return new Response("This feature requires the Enterprise plan.", {
|
|
101
|
+
status: 403,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Example: check entitlement access or remaining balance
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { MonetizationInboundPolicy } from "@zuplo/runtime";
|
|
110
|
+
|
|
111
|
+
const subscription = MonetizationInboundPolicy.getSubscriptionData(context);
|
|
112
|
+
const entitlement = subscription?.entitlements?.["requests"];
|
|
113
|
+
|
|
114
|
+
if (!entitlement?.hasAccess) {
|
|
115
|
+
return new Response("Your subscription does not include this meter.", {
|
|
116
|
+
status: 403,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// A simple “remaining” calculation you can use for UX or soft limits.
|
|
121
|
+
const remaining = entitlement.balance - entitlement.usage;
|
|
122
|
+
if (remaining <= 0) {
|
|
123
|
+
return new Response("Quota exceeded.", { status: 429 });
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Example: customize response headers
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { MonetizationInboundPolicy } from "@zuplo/runtime";
|
|
131
|
+
|
|
132
|
+
const subscription = MonetizationInboundPolicy.getSubscriptionData(context);
|
|
133
|
+
|
|
134
|
+
const nextResponse = new Response(response.body, response);
|
|
135
|
+
if (subscription) {
|
|
136
|
+
nextResponse.headers.set("x-zuplo-plan", subscription.plan.key);
|
|
137
|
+
}
|
|
138
|
+
return nextResponse;
|
|
139
|
+
```
|
|
140
|
+
|
|
62
141
|
## Meter merge behavior
|
|
63
142
|
|
|
64
143
|
- The final hook merges `options.meters` and request meter increments from
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The Monetization policy allows you to track and monetize the usage of your API
|
|
2
2
|
resources, declaratively and programmatically.
|
|
3
3
|
|
|
4
|
-
Follow our
|
|
5
|
-
[
|
|
6
|
-
|
|
4
|
+
Follow our official documentation
|
|
5
|
+
[API Monetization with Zuplo](https://zuplo.com/docs/articles/monetization) to
|
|
6
|
+
get started.
|
|
@@ -3,10 +3,15 @@ title: ZoneCache
|
|
|
3
3
|
sidebar_label: ZoneCache
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
The ZoneCache stores data in a shared cache
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
The ZoneCache stores data in a shared cache that is local to a single zone (data
|
|
7
|
+
center or cluster). Each zone maintains its own independent cache — data written
|
|
8
|
+
in one zone is not synchronized or replicated to other zones. If the same data
|
|
9
|
+
is needed in multiple zones, it must be written to each zone's cache
|
|
10
|
+
independently.
|
|
11
|
+
|
|
12
|
+
This makes ZoneCache ideal for caching configuration, reference data, or
|
|
13
|
+
expensive API responses to reduce latency. It is not suitable for transactional,
|
|
14
|
+
OLTP-style workloads where consistency across locations matters.
|
|
10
15
|
|
|
11
16
|
The ZoneCache can store any JSON serializable data. Each cached item has a
|
|
12
17
|
time-to-live (TTL) after which it expires and is removed from the cache. Each
|
|
@@ -112,15 +117,22 @@ promise rejections.
|
|
|
112
117
|
|
|
113
118
|
## When to use ZoneCache
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
ZoneCache works well for:
|
|
121
|
+
|
|
122
|
+
- **Configuration and reference data** — API keys, feature flags, routing rules,
|
|
123
|
+
or other config that changes infrequently
|
|
124
|
+
- **Expensive API responses** — results from slow or rate-limited upstream
|
|
125
|
+
services
|
|
126
|
+
- **Computed data** — pre-processed results that are costly to regenerate
|
|
127
|
+
|
|
128
|
+
:::caution{title="Not a distributed data store"}
|
|
119
129
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
130
|
+
ZoneCache is a per-zone cache with **no cross-zone synchronization**. A `put()`
|
|
131
|
+
in one data center has no effect on the cache in any other data center. Do not
|
|
132
|
+
use it as a database, key/value store, or for any OLTP-style read/write
|
|
133
|
+
workload. It is not appropriate for data where global consistency matters.
|
|
134
|
+
|
|
135
|
+
:::
|
|
124
136
|
|
|
125
137
|
## Limits
|
|
126
138
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zuplo",
|
|
3
|
-
"version": "6.68.
|
|
3
|
+
"version": "6.68.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The programmable API Gateway",
|
|
6
6
|
"author": "Zuplo, Inc.",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"zuplo": "zuplo.js"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@zuplo/cli": "6.68.
|
|
23
|
-
"@zuplo/core": "6.68.
|
|
24
|
-
"@zuplo/runtime": "6.68.
|
|
22
|
+
"@zuplo/cli": "6.68.18",
|
|
23
|
+
"@zuplo/core": "6.68.18",
|
|
24
|
+
"@zuplo/runtime": "6.68.18",
|
|
25
25
|
"@zuplo/test": "1.4.0"
|
|
26
26
|
}
|
|
27
27
|
}
|