feat(api): scoped, optionally expiring API tokens (#6201)

* security(api): add scoped expiring API tokens

* security(api): make scoped token lifecycle enforceable

---------

Co-authored-by: n0ctal <293235942+n0ctal@users.noreply.github.com>
This commit is contained in:
n0ctal
2026-08-15 18:31:49 +05:00
committed by GitHub
parent aecbad3ab1
commit 1230559e69
19 changed files with 785 additions and 94 deletions
+4
View File
@@ -220,15 +220,19 @@ export const EXAMPLES: Record<string, unknown> = {
"ApiToken": {
"createdAt": 0,
"enabled": false,
"expiresAt": 0,
"id": 0,
"name": "",
"scope": "",
"token": ""
},
"ApiTokenView": {
"createdAt": 1736000000,
"enabled": true,
"expiresAt": 0,
"id": 2,
"name": "central-panel-a",
"scope": "admin",
"token": "new-token-string"
},
"Client": {
+21 -1
View File
@@ -937,12 +937,19 @@ export const SCHEMAS: Record<string, unknown> = {
"enabled": {
"type": "boolean"
},
"expiresAt": {
"format": "int64",
"type": "integer"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"scope": {
"type": "string"
},
"token": {
"description": "SHA-256 hash; the plaintext is shown only once at creation",
"type": "string"
@@ -951,8 +958,10 @@ export const SCHEMAS: Record<string, unknown> = {
"required": [
"createdAt",
"enabled",
"expiresAt",
"id",
"name",
"scope",
"token"
],
"type": "object"
@@ -968,6 +977,11 @@ export const SCHEMAS: Record<string, unknown> = {
"example": true,
"type": "boolean"
},
"expiresAt": {
"example": 0,
"format": "int64",
"type": "integer"
},
"id": {
"example": 2,
"type": "integer"
@@ -976,6 +990,10 @@ export const SCHEMAS: Record<string, unknown> = {
"example": "central-panel-a",
"type": "string"
},
"scope": {
"example": "admin",
"type": "string"
},
"token": {
"example": "new-token-string",
"type": "string"
@@ -984,8 +1002,10 @@ export const SCHEMAS: Record<string, unknown> = {
"required": [
"createdAt",
"enabled",
"expiresAt",
"id",
"name"
"name",
"scope"
],
"type": "object"
},
+4
View File
@@ -229,16 +229,20 @@ export interface AllSettingView {
export interface ApiToken {
createdAt: number;
enabled: boolean;
expiresAt: number;
id: number;
name: string;
scope: string;
token: string;
}
export interface ApiTokenView {
createdAt: number;
enabled: boolean;
expiresAt: number;
id: number;
name: string;
scope: string;
token?: string;
}
+4
View File
@@ -245,8 +245,10 @@ export type AllSettingView = z.infer<typeof AllSettingViewSchema>;
export const ApiTokenSchema = z.object({
createdAt: z.number().int(),
enabled: z.boolean(),
expiresAt: z.number().int(),
id: z.number().int(),
name: z.string(),
scope: z.string(),
token: z.string(),
});
export type ApiToken = z.infer<typeof ApiTokenSchema>;
@@ -254,8 +256,10 @@ export type ApiToken = z.infer<typeof ApiTokenSchema>;
export const ApiTokenViewSchema = z.object({
createdAt: z.number().int(),
enabled: z.boolean(),
expiresAt: z.number().int(),
id: z.number().int(),
name: z.string(),
scope: z.string(),
token: z.string().optional(),
});
export type ApiTokenView = z.infer<typeof ApiTokenViewSchema>;