Skip to content

Commit 6024e32

Browse files
feat: v2 reroll key endpoint (#3785)
* feat: initial reroll struct * add basic test * test and fix ratelimit and add reroll event * cleanup file * docs changes * docs changes * docs changes * change auditlog * Update go/pkg/db/custom_types.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * rabbit fixes * rabbit fixes * run in parallel --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 39b97f3 commit 6024e32

File tree

42 files changed

+2408
-111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2408
-111
lines changed

apps/dashboard/lib/trpc/routers/audit/llm-search/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ ${validEventTypes.map((event) => ` - ${event}`).join("\n")}
215215
- workspace.create, workspace.update, workspace.delete, workspace.opt_in
216216
- gateway.create, llmGateway.create, llmGateway.delete
217217
- api.create, api.update, api.delete
218-
- key.create, key.update, key.delete
218+
- key.create, key.update, key.delete, key.reroll
219219
- ratelimitNamespace.create, ratelimitNamespace.update, ratelimitNamespace.delete
220220
- vercelIntegration.create, vercelIntegration.update, vercelIntegration.delete
221221
- vercelBinding.create, vercelBinding.update, vercelBinding.delete

go/apps/api/integration/harness.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func New(t *testing.T, config Config) *Harness {
8787
ctx: ctx,
8888
cancel: cancel,
8989
instanceAddrs: []string{},
90-
Seed: seed.New(t, db),
90+
Seed: seed.New(t, db, nil),
9191
dbDSN: mysqlHostDSN,
9292
DB: db,
9393
CH: ch,

go/apps/api/openapi/gen.go

Lines changed: 69 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/apps/api/openapi/openapi-generated.yaml

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,57 @@ components:
926926
"$ref": "#/components/schemas/Meta"
927927
data:
928928
"$ref": "#/components/schemas/V2KeysRemoveRolesResponseData"
929+
V2KeysRerollKeyRequestBody:
930+
type: object
931+
required:
932+
- keyId
933+
- expiration
934+
properties:
935+
keyId:
936+
type: string
937+
minLength: 3
938+
maxLength: 255
939+
pattern: "^[a-zA-Z0-9_]+$"
940+
description: |
941+
The database identifier of the key to reroll.
942+
943+
This is the unique ID returned when creating or listing keys, NOT the actual API key token.
944+
You can find this ID in:
945+
- The response from `keys.createKey`
946+
- Key verification responses
947+
- The Unkey dashboard
948+
- API key listing endpoints
949+
example: key_2cGKbMxRyIzhCxo1Idjz8q
950+
expiration:
951+
type: integer
952+
format: int64
953+
minimum: 0
954+
maximum: 4102444800000
955+
description: |
956+
Duration in milliseconds until the ORIGINAL key is revoked, starting from now.
957+
958+
This parameter controls the overlap period for key rotation:
959+
- Set to `0` to revoke the original key immediately
960+
- Positive values keep the original key active for the specified duration
961+
- Allows graceful migration by giving users time to update their credentials
962+
963+
Common overlap periods:
964+
- Immediate revocation: 0
965+
- 1 hour grace period: 3600000
966+
- 24 hours grace period: 86400000
967+
- 7 days grace period: 604800000
968+
- 30 days grace period: 2592000000
969+
example: 86400000
970+
V2KeysRerollKeyResponseBody:
971+
type: object
972+
required:
973+
- meta
974+
- data
975+
properties:
976+
meta:
977+
"$ref": "#/components/schemas/Meta"
978+
data:
979+
"$ref": "#/components/schemas/V2KeysRerollKeyResponseData"
929980
V2KeysSetPermissionsRequestBody:
930981
type: object
931982
required:
@@ -2476,6 +2527,43 @@ components:
24762527
- Changes take effect immediately for new verifications but cached sessions may retain old permissions briefly
24772528
items:
24782529
"$ref": "#/components/schemas/Role"
2530+
V2KeysRerollKeyResponseData:
2531+
type: object
2532+
properties:
2533+
keyId:
2534+
type: string
2535+
description: |
2536+
The unique identifier for the newly created key.
2537+
2538+
This is NOT the actual API key token, but a reference ID for management operations.
2539+
Store this ID to:
2540+
- Update or revoke the key later
2541+
- Track the key in your database
2542+
- Display in admin dashboards (safe to log)
2543+
2544+
Note: This is a new ID - the original key retains its own ID.
2545+
example: key_2cGKbMxRyIzhCxo1Idjz8q
2546+
key:
2547+
type: string
2548+
description: |
2549+
The newly generated API key token (the actual secret that authenticates requests).
2550+
2551+
**SECURITY CRITICAL:**
2552+
- This is the only time you'll receive the complete key
2553+
- Unkey stores only a hashed version (unless the original key was created with `recoverable=true`)
2554+
- Never log, store, or expose this value in your systems
2555+
- Transmit directly to the end user via secure channels only
2556+
- If lost and not recoverable, you must reroll or create a new key
2557+
2558+
The key format follows: `[prefix]_[random_bytes]`
2559+
- Prefix is extracted from the original key or uses API default
2560+
- Random bytes follow API configuration (default: 16 bytes)
2561+
2562+
This is NOT the keyId - it's the actual secret token used for authentication.
2563+
example: prod_2cGKbMxRjIzhCxo1IdjH3arELti7Sdyc8w6XYbvtcyuBowPT
2564+
required:
2565+
- keyId
2566+
- key
24792567
V2KeysSetPermissionsResponseData:
24802568
type: array
24812569
description: |-
@@ -4323,6 +4411,91 @@ paths:
43234411
tags:
43244412
- keys
43254413
x-speakeasy-name-override: removeRoles
4414+
/v2/keys.rerollKey:
4415+
post:
4416+
description: |
4417+
Generate a new API key while preserving the configuration from an existing key.
4418+
4419+
This operation creates a fresh key with a new token while maintaining all settings from the original key:
4420+
- Permissions and roles
4421+
- Custom metadata
4422+
- Rate limit configurations
4423+
- Identity associations
4424+
- Remaining credits
4425+
- Recovery settings
4426+
4427+
**Key Generation:**
4428+
- The system attempts to extract the prefix from the original key
4429+
- If prefix extraction fails, the default API prefix is used
4430+
- Key length follows the API's default byte configuration (or 16 bytes if not specified)
4431+
4432+
**Original Key Handling:**
4433+
- The original key will be revoked after the duration specified in `expiration`
4434+
- Set `expiration` to 0 to revoke immediately
4435+
- This allows for graceful key rotation with an overlap period
4436+
4437+
Common use cases include:
4438+
- Rotating keys for security compliance
4439+
- Issuing replacement keys for compromised credentials
4440+
- Creating backup keys with identical permissions
4441+
4442+
**Important:** Analytics and usage metrics are tracked at both the key level AND identity level. If the original key has an identity, the new key will inherit it, allowing you to track usage across both individual keys and the overall identity.
4443+
4444+
**Required Permissions**
4445+
4446+
Your root key must have:
4447+
- `api.*.create_key` or `api.<api_id>.create_key`
4448+
- `api.*.encrypt_key` or `api.<api_id>.encrypt_key` (only when the original key is recoverable)
4449+
operationId: rerollKey
4450+
requestBody:
4451+
content:
4452+
application/json:
4453+
schema:
4454+
$ref: '#/components/schemas/V2KeysRerollKeyRequestBody'
4455+
required: true
4456+
responses:
4457+
"200":
4458+
content:
4459+
application/json:
4460+
schema:
4461+
$ref: '#/components/schemas/V2KeysRerollKeyResponseBody'
4462+
description: Key rerolled successfully.
4463+
"400":
4464+
content:
4465+
application/json:
4466+
schema:
4467+
$ref: '#/components/schemas/BadRequestErrorResponse'
4468+
description: Bad request
4469+
"401":
4470+
content:
4471+
application/json:
4472+
schema:
4473+
$ref: '#/components/schemas/UnauthorizedErrorResponse'
4474+
description: Unauthorized
4475+
"403":
4476+
content:
4477+
application/json:
4478+
schema:
4479+
$ref: '#/components/schemas/ForbiddenErrorResponse'
4480+
description: Forbidden
4481+
"404":
4482+
content:
4483+
application/json:
4484+
schema:
4485+
$ref: '#/components/schemas/NotFoundErrorResponse'
4486+
description: Not found
4487+
"500":
4488+
content:
4489+
application/json:
4490+
schema:
4491+
$ref: '#/components/schemas/InternalServerErrorResponse'
4492+
description: Internal server error
4493+
security:
4494+
- rootKey: []
4495+
summary: Reroll Key
4496+
tags:
4497+
- keys
4498+
x-speakeasy-name-override: rerollKey
43264499
/v2/keys.setPermissions:
43274500
post:
43284501
description: |

go/apps/api/openapi/openapi-split.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ paths:
154154

155155
/v2/keys.createKey:
156156
$ref: "./spec/paths/v2/keys/createKey/index.yaml"
157+
/v2/keys.rerollKey:
158+
$ref: "./spec/paths/v2/keys/rerollKey/index.yaml"
157159
/v2/keys.updateKey:
158160
$ref: "./spec/paths/v2/keys/updateKey/index.yaml"
159161
/v2/keys.updateCredits:
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
type: object
2+
required:
3+
- keyId
4+
- expiration
5+
properties:
6+
keyId:
7+
type: string
8+
minLength: 3
9+
maxLength: 255
10+
pattern: "^[a-zA-Z0-9_]+$"
11+
description: |
12+
The database identifier of the key to reroll.
13+
14+
This is the unique ID returned when creating or listing keys, NOT the actual API key token.
15+
You can find this ID in:
16+
- The response from `keys.createKey`
17+
- Key verification responses
18+
- The Unkey dashboard
19+
- API key listing endpoints
20+
example: key_2cGKbMxRyIzhCxo1Idjz8q
21+
expiration:
22+
type: integer
23+
format: int64
24+
minimum: 0
25+
maximum: 4102444800000
26+
description: |
27+
Duration in milliseconds until the ORIGINAL key is revoked, starting from now.
28+
29+
This parameter controls the overlap period for key rotation:
30+
- Set to `0` to revoke the original key immediately
31+
- Positive values keep the original key active for the specified duration
32+
- Allows graceful migration by giving users time to update their credentials
33+
34+
Common overlap periods:
35+
- Immediate revocation: 0
36+
- 1 hour grace period: 3600000
37+
- 24 hours grace period: 86400000
38+
- 7 days grace period: 604800000
39+
- 30 days grace period: 2592000000
40+
example: 86400000
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
type: object
2+
required:
3+
- meta
4+
- data
5+
properties:
6+
meta:
7+
"$ref": "../../../../common/Meta.yaml"
8+
data:
9+
"$ref": "./V2KeysRerollKeyResponseData.yaml"
10+
examples:
11+
rerollKey:
12+
summary: Key rerolled successfully
13+
description: Successfully rerolled a key
14+
value:
15+
meta:
16+
requestId: req_abc123def456
17+
data:
18+
keyId: key_2cGKbMxRyIzhCxo1Idjz8q
19+
key: prod_2cGKbMxRjIzhCxo1IdjH3arELti7Sdyc8w6XYbvtcyuBowPT

0 commit comments

Comments
 (0)