Skip to content

Commit dfdd3d6

Browse files
Merge pull request #1 from runtimed/feature/api-key-provider
Set up the initial repository and implement ApiKeyProvider
2 parents 56f2d25 + 5b235e6 commit dfdd3d6

File tree

11 files changed

+324
-0
lines changed

11 files changed

+324
-0
lines changed

.github/workflows/ci.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
node-version: [22.x, 24.x]
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Node.js ${{ matrix.node-version }}
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: ${{ matrix.node-version }}
25+
cache: 'npm'
26+
27+
- name: Install dependencies
28+
run: npm ci
29+
30+
- name: Check formatting
31+
run: npm run format:check
32+
33+
- name: Build project
34+
run: npm run build

.github/workflows/publish.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Publish Package to npmjs
2+
on:
3+
release:
4+
types: [published]
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
permissions:
9+
contents: read
10+
id-token: write
11+
environment: production
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-node@v4
15+
with:
16+
node-version: '24.x'
17+
registry-url: 'https://registry.npmjs.org'
18+
- run: npm ci
19+
- run: npm run build
20+
- name: Publish the package
21+
run: npm publish --provenance --access public

.prettierignore

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Build outputs
2+
dist/
3+
build/
4+
*.min.js
5+
6+
# Dependencies
7+
node_modules/
8+
9+
# Package files
10+
package-lock.json
11+
yarn.lock
12+
pnpm-lock.yaml
13+
14+
# Generated files
15+
*.d.ts.map
16+
17+
# Logs
18+
*.log
19+
20+
# Environment files
21+
.env
22+
.env.local
23+
.env.*.local
24+
25+
# IDE files
26+
.vscode/
27+
.idea/
28+
29+
# OS files
30+
.DS_Store
31+
Thumbs.db

.prettierrc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"semi": true,
3+
"trailingComma": "es5",
4+
"singleQuote": true,
5+
"printWidth": 120,
6+
"tabWidth": 2,
7+
"useTabs": false,
8+
"bracketSpacing": true,
9+
"arrowParens": "avoid",
10+
"endOfLine": "lf"
11+
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# extensions
2+
23
Interface and shared code for building extensions for the runt ecosystem

package-lock.json

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

package.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"name": "@runtimed/extensions",
3+
"version": "0.1.0",
4+
"description": "Interfaces and shared code for building extensions to the runt ecosystem",
5+
"keywords": [
6+
"runtimed",
7+
"runt"
8+
],
9+
"homepage": "https://github.com/runtimed/extensions#readme",
10+
"bugs": {
11+
"url": "https://github.com/runtimed/extensions/issues"
12+
},
13+
"repository": {
14+
"type": "git",
15+
"url": "git+https://github.com/runtimed/extensions.git"
16+
},
17+
"license": "BSD-3-Clause",
18+
"author": "Anil Kulkarni",
19+
"type": "module",
20+
"main": "dist/index.js",
21+
"types": "./dist/index.d.ts",
22+
"exports": {
23+
".": {
24+
"import": "./dist/index.js",
25+
"types": "./dist/index.d.ts"
26+
}
27+
},
28+
"files": [
29+
"dist",
30+
"README.md",
31+
"LICENSE"
32+
],
33+
"scripts": {
34+
"test": "echo \"Error: no test specified\" && exit 1",
35+
"format": "prettier --write .",
36+
"format:check": "prettier --check .",
37+
"build": "npx tsc"
38+
},
39+
"devDependencies": {
40+
"jose": "^6.0.12",
41+
"prettier": "^3.6.2",
42+
"typescript": "^5.9.2"
43+
},
44+
"peerDependencies": {
45+
"@cloudflare/workers-types": "^4.20250810.0"
46+
}
47+
}

src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { ApiKeyProvider } from './providers/api_key';
2+
export type * from './providers/shared';
3+
4+
export type BackendExtension = {
5+
apiKey?: ApiKeyProvider;
6+
};

src/providers/api_key.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { Response as WorkerResponse } from '@cloudflare/workers-types';
2+
import type { Passport, ProviderContext, Scope, Resource } from './shared';
3+
4+
export enum ApiKeyCapabilities {
5+
Revoke = 'revoke',
6+
Delete = 'delete',
7+
CreateWithResources = 'create_with_resources',
8+
}
9+
10+
export type CreateApiKeyRequest = {
11+
scopes: Scope[];
12+
resources?: Resource[];
13+
expiresAt: string;
14+
name?: string;
15+
userGenerated: boolean;
16+
};
17+
18+
export type ApiKey = CreateApiKeyRequest & {
19+
id: string;
20+
userId: string;
21+
revoked: boolean;
22+
};
23+
24+
export type ApiKeyProvider = {
25+
capabilities: Set<ApiKeyCapabilities>;
26+
overrideHandler?: (context: ProviderContext) => Promise<false | WorkerResponse>; // Any routes the provider wants to fully implement. Return false for any route not handled
27+
isApiKey(context: ProviderContext): boolean; // Returns true if the auth token appears to be an API key (whether or not it is valid)
28+
validateApiKey(context: ProviderContext): Promise<Passport>; // Ensure the API key is valid, and returns the Passport. Raise an error otherwise
29+
createApiKey: (context: ProviderContext, request: CreateApiKeyRequest) => Promise<string>;
30+
getApiKey: (context: ProviderContext, id: string) => Promise<ApiKey>; // Returns the API key matching the specific api key id
31+
listApiKeys: (context: ProviderContext) => Promise<ApiKey[]>; // Return a list of all API keys for a user
32+
revokeApiKey: (context: ProviderContext, id: string) => Promise<void>; // set the revoked flag for an API key, such that it will no longer be valid
33+
deleteApiKey: (context: ProviderContext, id: string) => Promise<void>; // Delete an API key from the database
34+
};

src/providers/shared.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { JWTPayload } from 'jose';
2+
import type {
3+
Request as WorkerRequest,
4+
IncomingRequestCfProperties,
5+
D1Database,
6+
ExecutionContext,
7+
} from '@cloudflare/workers-types';
8+
9+
export type ProviderEnv = {
10+
DB: D1Database;
11+
AUTH_ISSUER: string;
12+
};
13+
14+
export enum AuthType {
15+
OAuth = 'oauth',
16+
ApiKey = 'api_key',
17+
}
18+
19+
export type Scope = 'runt:read' | 'runt:execute';
20+
21+
export type User = {
22+
id: string;
23+
email: string;
24+
name?: string;
25+
givenName?: string;
26+
familyName: string;
27+
};
28+
29+
export type Resource = {
30+
id: string;
31+
type: string;
32+
};
33+
34+
export type Passport = {
35+
type: AuthType;
36+
user: User;
37+
claims: JWTPayload;
38+
scopes: Scope[] | null;
39+
resources: Resource[] | null;
40+
};
41+
42+
export type ProviderContext = {
43+
request: WorkerRequest<unknown, IncomingRequestCfProperties<unknown>>;
44+
env: ProviderEnv;
45+
ctx: ExecutionContext;
46+
bearerToken: string | null;
47+
passport: Passport | null;
48+
};

0 commit comments

Comments
 (0)