@agentadmit/sdk 1.0.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/.github/workflows/publish.yml +53 -0
- package/LICENSE +56 -0
- package/README.md +203 -0
- package/dist/auth.d.ts +34 -0
- package/dist/auth.js +305 -0
- package/dist/config.d.ts +64 -0
- package/dist/config.js +92 -0
- package/dist/errors.d.ts +41 -0
- package/dist/errors.js +38 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +34 -0
- package/dist/keys.d.ts +12 -0
- package/dist/keys.js +29 -0
- package/dist/routes.d.ts +26 -0
- package/dist/routes.js +209 -0
- package/dist/storage.d.ts +62 -0
- package/dist/storage.js +161 -0
- package/package.json +55 -0
- package/src/auth.ts +356 -0
- package/src/config.ts +150 -0
- package/src/errors.ts +50 -0
- package/src/index.ts +27 -0
- package/src/keys.ts +33 -0
- package/src/routes.ts +245 -0
- package/src/storage.ts +228 -0
- package/tsconfig.json +19 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentadmit/config.ts
|
|
3
|
+
* Configuration loader for AgentAdmit Node.js SDK.
|
|
4
|
+
*
|
|
5
|
+
* IMPORTANT: AgentAdmit uses MANDATORY hosted introspection.
|
|
6
|
+
* All token validation goes through api.agentadmit.com.
|
|
7
|
+
* There is no self-hosted mode. No local JWT validation. No bypass.
|
|
8
|
+
* This is required for security, audit logging, and scope enforcement.
|
|
9
|
+
*/
|
|
10
|
+
export interface ScopeDefinition {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
category?: string;
|
|
14
|
+
role?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface DurationOption {
|
|
17
|
+
label: string;
|
|
18
|
+
seconds: number | null;
|
|
19
|
+
}
|
|
20
|
+
export interface TierDefinition {
|
|
21
|
+
name: string;
|
|
22
|
+
connections_limit: number;
|
|
23
|
+
api_calls_monthly?: number;
|
|
24
|
+
hard_cap: boolean;
|
|
25
|
+
overage_per_thousand?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface StorageConfig {
|
|
28
|
+
backend: 'mongodb' | 'memory';
|
|
29
|
+
uri?: string;
|
|
30
|
+
database?: string;
|
|
31
|
+
connections_collection?: string;
|
|
32
|
+
audit_log_collection?: string;
|
|
33
|
+
tokens_collection?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface AgentAdmitConfig {
|
|
36
|
+
app_name: string;
|
|
37
|
+
app_id: string;
|
|
38
|
+
api_key: string;
|
|
39
|
+
api_base_url: string;
|
|
40
|
+
agentadmit_api_url: string;
|
|
41
|
+
agentadmit_verify_url: string;
|
|
42
|
+
token_prefix_connection: string;
|
|
43
|
+
token_prefix_access: string;
|
|
44
|
+
algorithm: string;
|
|
45
|
+
audience: string;
|
|
46
|
+
connection_token_ttl: number;
|
|
47
|
+
scopes: ScopeDefinition[];
|
|
48
|
+
durations: DurationOption[];
|
|
49
|
+
tiers: TierDefinition[];
|
|
50
|
+
default_tier: string;
|
|
51
|
+
storage: StorageConfig;
|
|
52
|
+
route_prefix: string;
|
|
53
|
+
discovery_path: string;
|
|
54
|
+
user_lookup_field: string;
|
|
55
|
+
private_key_path: string;
|
|
56
|
+
public_key_path: string;
|
|
57
|
+
/** Max retries on 429 before throwing RateLimitError. Default: 3. */
|
|
58
|
+
max_retries: number;
|
|
59
|
+
}
|
|
60
|
+
export declare function loadConfig(configPath?: string): AgentAdmitConfig;
|
|
61
|
+
export declare function getConfig(): AgentAdmitConfig;
|
|
62
|
+
export declare function getScopeMetadata(): ScopeDefinition[];
|
|
63
|
+
export declare function getDurationOptions(): DurationOption[];
|
|
64
|
+
export declare function getTierLimits(tierName: string): TierDefinition | undefined;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* agentadmit/config.ts
|
|
4
|
+
* Configuration loader for AgentAdmit Node.js SDK.
|
|
5
|
+
*
|
|
6
|
+
* IMPORTANT: AgentAdmit uses MANDATORY hosted introspection.
|
|
7
|
+
* All token validation goes through api.agentadmit.com.
|
|
8
|
+
* There is no self-hosted mode. No local JWT validation. No bypass.
|
|
9
|
+
* This is required for security, audit logging, and scope enforcement.
|
|
10
|
+
*/
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.loadConfig = loadConfig;
|
|
16
|
+
exports.getConfig = getConfig;
|
|
17
|
+
exports.getScopeMetadata = getScopeMetadata;
|
|
18
|
+
exports.getDurationOptions = getDurationOptions;
|
|
19
|
+
exports.getTierLimits = getTierLimits;
|
|
20
|
+
const fs_1 = __importDefault(require("fs"));
|
|
21
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
22
|
+
const DEFAULT_CONFIG = {
|
|
23
|
+
app_name: 'My App',
|
|
24
|
+
app_id: '',
|
|
25
|
+
api_key: '',
|
|
26
|
+
api_base_url: 'http://localhost:3000',
|
|
27
|
+
agentadmit_api_url: 'https://api.agentadmit.com',
|
|
28
|
+
agentadmit_verify_url: 'https://api.agentadmit.com/v1/verify',
|
|
29
|
+
token_prefix_connection: 'ag_ct_',
|
|
30
|
+
token_prefix_access: 'ag_at_',
|
|
31
|
+
algorithm: 'RS256',
|
|
32
|
+
audience: 'agentadmit',
|
|
33
|
+
connection_token_ttl: 900,
|
|
34
|
+
scopes: [],
|
|
35
|
+
durations: [
|
|
36
|
+
{ label: '1 Hour', seconds: 3600 },
|
|
37
|
+
{ label: '24 Hours', seconds: 86400 },
|
|
38
|
+
{ label: '7 Days', seconds: 604800 },
|
|
39
|
+
{ label: '30 Days', seconds: 2592000 },
|
|
40
|
+
{ label: 'Until I Revoke', seconds: null },
|
|
41
|
+
],
|
|
42
|
+
tiers: [
|
|
43
|
+
{ name: 'trial', connections_limit: 3, hard_cap: true },
|
|
44
|
+
{ name: 'standard', connections_limit: 100, api_calls_monthly: 2000000, hard_cap: false },
|
|
45
|
+
],
|
|
46
|
+
default_tier: 'standard',
|
|
47
|
+
storage: {
|
|
48
|
+
backend: 'mongodb',
|
|
49
|
+
uri: 'mongodb://localhost:27017',
|
|
50
|
+
database: 'agentadmit',
|
|
51
|
+
connections_collection: 'agentadmit_connections',
|
|
52
|
+
audit_log_collection: 'agentadmit_audit_log',
|
|
53
|
+
tokens_collection: 'agentadmit_tokens',
|
|
54
|
+
},
|
|
55
|
+
route_prefix: '/agentadmit',
|
|
56
|
+
discovery_path: '/.well-known/agentadmit',
|
|
57
|
+
user_lookup_field: 'user_id',
|
|
58
|
+
max_retries: 3,
|
|
59
|
+
};
|
|
60
|
+
let _config = null;
|
|
61
|
+
function loadConfig(configPath = 'agentadmit.yaml') {
|
|
62
|
+
let resolvedPath = configPath;
|
|
63
|
+
if (!fs_1.default.existsSync(resolvedPath)) {
|
|
64
|
+
const envPath = process.env.AGENTADMIT_CONFIG;
|
|
65
|
+
if (envPath && fs_1.default.existsSync(envPath)) {
|
|
66
|
+
resolvedPath = envPath;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
throw new Error(`Config file not found: ${configPath}. Run 'agentadmit init' to generate one.`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const raw = js_yaml_1.default.load(fs_1.default.readFileSync(resolvedPath, 'utf-8')) || {};
|
|
73
|
+
_config = { ...DEFAULT_CONFIG, ...raw };
|
|
74
|
+
console.log(`[AgentAdmit] Config loaded: ${resolvedPath} (${_config.scopes.length} scopes)`);
|
|
75
|
+
return _config;
|
|
76
|
+
}
|
|
77
|
+
function getConfig() {
|
|
78
|
+
if (!_config) {
|
|
79
|
+
throw new Error('AgentAdmit config not loaded. Call loadConfig() first.');
|
|
80
|
+
}
|
|
81
|
+
return _config;
|
|
82
|
+
}
|
|
83
|
+
function getScopeMetadata() {
|
|
84
|
+
return getConfig().scopes;
|
|
85
|
+
}
|
|
86
|
+
function getDurationOptions() {
|
|
87
|
+
return getConfig().durations;
|
|
88
|
+
}
|
|
89
|
+
function getTierLimits(tierName) {
|
|
90
|
+
const config = getConfig();
|
|
91
|
+
return config.tiers.find(t => t.name === tierName) || config.tiers.find(t => t.name === config.default_tier);
|
|
92
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentadmit/errors.ts
|
|
3
|
+
* Custom error types for the AgentAdmit Node.js SDK.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Thrown when the AgentAdmit introspection endpoint returns HTTP 429
|
|
7
|
+
* and all retry attempts (with exponential backoff) have been exhausted.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { validateAgentToken, RateLimitError } from '@agentadmit/sdk';
|
|
12
|
+
*
|
|
13
|
+
* try {
|
|
14
|
+
* await validateAgentToken(token);
|
|
15
|
+
* } catch (err) {
|
|
16
|
+
* if (err instanceof RateLimitError) {
|
|
17
|
+
* res.status(429).json({
|
|
18
|
+
* error: 'rate_limited',
|
|
19
|
+
* retry_after: err.retryAfter,
|
|
20
|
+
* });
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare class RateLimitError extends Error {
|
|
26
|
+
/** Seconds to wait before retrying (from Retry-After header), or null. */
|
|
27
|
+
readonly retryAfter: number | null;
|
|
28
|
+
/** Total request limit for the current window (X-RateLimit-Limit), or null. */
|
|
29
|
+
readonly limit: number | null;
|
|
30
|
+
/** Requests remaining in the current window (X-RateLimit-Remaining), or null. */
|
|
31
|
+
readonly remaining: number | null;
|
|
32
|
+
/** Unix timestamp when the rate limit window resets (X-RateLimit-Reset), or null. */
|
|
33
|
+
readonly reset: number | null;
|
|
34
|
+
constructor(options?: {
|
|
35
|
+
message?: string;
|
|
36
|
+
retryAfter?: number | null;
|
|
37
|
+
limit?: number | null;
|
|
38
|
+
remaining?: number | null;
|
|
39
|
+
reset?: number | null;
|
|
40
|
+
});
|
|
41
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* agentadmit/errors.ts
|
|
4
|
+
* Custom error types for the AgentAdmit Node.js SDK.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.RateLimitError = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Thrown when the AgentAdmit introspection endpoint returns HTTP 429
|
|
10
|
+
* and all retry attempts (with exponential backoff) have been exhausted.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { validateAgentToken, RateLimitError } from '@agentadmit/sdk';
|
|
15
|
+
*
|
|
16
|
+
* try {
|
|
17
|
+
* await validateAgentToken(token);
|
|
18
|
+
* } catch (err) {
|
|
19
|
+
* if (err instanceof RateLimitError) {
|
|
20
|
+
* res.status(429).json({
|
|
21
|
+
* error: 'rate_limited',
|
|
22
|
+
* retry_after: err.retryAfter,
|
|
23
|
+
* });
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
class RateLimitError extends Error {
|
|
29
|
+
constructor(options = {}) {
|
|
30
|
+
super(options.message ?? 'AgentAdmit rate limit exceeded. Max retries exhausted.');
|
|
31
|
+
this.name = 'RateLimitError';
|
|
32
|
+
this.retryAfter = options.retryAfter ?? null;
|
|
33
|
+
this.limit = options.limit ?? null;
|
|
34
|
+
this.remaining = options.remaining ?? null;
|
|
35
|
+
this.reset = options.reset ?? null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.RateLimitError = RateLimitError;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agentadmit/sdk — Node.js SDK for AgentAdmit
|
|
3
|
+
*
|
|
4
|
+
* User-mediated AI agent authorization. Plug-and-play for Express and Next.js.
|
|
5
|
+
*/
|
|
6
|
+
export { loadConfig, getConfig, getScopeMetadata, getDurationOptions, getTierLimits } from './config';
|
|
7
|
+
export type { AgentAdmitConfig, ScopeDefinition, DurationOption, TierDefinition, StorageConfig } from './config';
|
|
8
|
+
export { generateKeyPair, loadPrivateKey, loadPublicKey } from './keys';
|
|
9
|
+
export { StorageBackend, MongoDBStorage, MemoryStorage, createStorage } from './storage';
|
|
10
|
+
export { validateAgentToken, requireScope, requireScopeIfAgent, resolveAuth, checkConnectionCap, setStorage, setUserVerifier, } from './auth';
|
|
11
|
+
export type { AgentContext } from './auth';
|
|
12
|
+
export { createAgentAdmitRouter } from './routes';
|
|
13
|
+
export { RateLimitError } from './errors';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @agentadmit/sdk — Node.js SDK for AgentAdmit
|
|
4
|
+
*
|
|
5
|
+
* User-mediated AI agent authorization. Plug-and-play for Express and Next.js.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.RateLimitError = exports.createAgentAdmitRouter = exports.setUserVerifier = exports.setStorage = exports.checkConnectionCap = exports.resolveAuth = exports.requireScopeIfAgent = exports.requireScope = exports.validateAgentToken = exports.createStorage = exports.MemoryStorage = exports.MongoDBStorage = exports.loadPublicKey = exports.loadPrivateKey = exports.generateKeyPair = exports.getTierLimits = exports.getDurationOptions = exports.getScopeMetadata = exports.getConfig = exports.loadConfig = void 0;
|
|
9
|
+
var config_1 = require("./config");
|
|
10
|
+
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
|
|
11
|
+
Object.defineProperty(exports, "getConfig", { enumerable: true, get: function () { return config_1.getConfig; } });
|
|
12
|
+
Object.defineProperty(exports, "getScopeMetadata", { enumerable: true, get: function () { return config_1.getScopeMetadata; } });
|
|
13
|
+
Object.defineProperty(exports, "getDurationOptions", { enumerable: true, get: function () { return config_1.getDurationOptions; } });
|
|
14
|
+
Object.defineProperty(exports, "getTierLimits", { enumerable: true, get: function () { return config_1.getTierLimits; } });
|
|
15
|
+
var keys_1 = require("./keys");
|
|
16
|
+
Object.defineProperty(exports, "generateKeyPair", { enumerable: true, get: function () { return keys_1.generateKeyPair; } });
|
|
17
|
+
Object.defineProperty(exports, "loadPrivateKey", { enumerable: true, get: function () { return keys_1.loadPrivateKey; } });
|
|
18
|
+
Object.defineProperty(exports, "loadPublicKey", { enumerable: true, get: function () { return keys_1.loadPublicKey; } });
|
|
19
|
+
var storage_1 = require("./storage");
|
|
20
|
+
Object.defineProperty(exports, "MongoDBStorage", { enumerable: true, get: function () { return storage_1.MongoDBStorage; } });
|
|
21
|
+
Object.defineProperty(exports, "MemoryStorage", { enumerable: true, get: function () { return storage_1.MemoryStorage; } });
|
|
22
|
+
Object.defineProperty(exports, "createStorage", { enumerable: true, get: function () { return storage_1.createStorage; } });
|
|
23
|
+
var auth_1 = require("./auth");
|
|
24
|
+
Object.defineProperty(exports, "validateAgentToken", { enumerable: true, get: function () { return auth_1.validateAgentToken; } });
|
|
25
|
+
Object.defineProperty(exports, "requireScope", { enumerable: true, get: function () { return auth_1.requireScope; } });
|
|
26
|
+
Object.defineProperty(exports, "requireScopeIfAgent", { enumerable: true, get: function () { return auth_1.requireScopeIfAgent; } });
|
|
27
|
+
Object.defineProperty(exports, "resolveAuth", { enumerable: true, get: function () { return auth_1.resolveAuth; } });
|
|
28
|
+
Object.defineProperty(exports, "checkConnectionCap", { enumerable: true, get: function () { return auth_1.checkConnectionCap; } });
|
|
29
|
+
Object.defineProperty(exports, "setStorage", { enumerable: true, get: function () { return auth_1.setStorage; } });
|
|
30
|
+
Object.defineProperty(exports, "setUserVerifier", { enumerable: true, get: function () { return auth_1.setUserVerifier; } });
|
|
31
|
+
var routes_1 = require("./routes");
|
|
32
|
+
Object.defineProperty(exports, "createAgentAdmitRouter", { enumerable: true, get: function () { return routes_1.createAgentAdmitRouter; } });
|
|
33
|
+
var errors_1 = require("./errors");
|
|
34
|
+
Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return errors_1.RateLimitError; } });
|
package/dist/keys.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentadmit/keys.ts
|
|
3
|
+
*
|
|
4
|
+
* DEPRECATED — AgentAdmit is a hosted service.
|
|
5
|
+
*
|
|
6
|
+
* All cryptographic operations (key generation, JWT signing, JWKS serving)
|
|
7
|
+
* are performed by the AgentAdmit hosted service. The SDK does NOT generate
|
|
8
|
+
* or load RSA key pairs. Calling any function in this module will throw.
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateKeyPair(_outputDir?: string): never;
|
|
11
|
+
export declare function loadPrivateKey(_keyPath?: string): never;
|
|
12
|
+
export declare function loadPublicKey(_keyPath?: string): never;
|
package/dist/keys.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* agentadmit/keys.ts
|
|
4
|
+
*
|
|
5
|
+
* DEPRECATED — AgentAdmit is a hosted service.
|
|
6
|
+
*
|
|
7
|
+
* All cryptographic operations (key generation, JWT signing, JWKS serving)
|
|
8
|
+
* are performed by the AgentAdmit hosted service. The SDK does NOT generate
|
|
9
|
+
* or load RSA key pairs. Calling any function in this module will throw.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.generateKeyPair = generateKeyPair;
|
|
13
|
+
exports.loadPrivateKey = loadPrivateKey;
|
|
14
|
+
exports.loadPublicKey = loadPublicKey;
|
|
15
|
+
function generateKeyPair(_outputDir) {
|
|
16
|
+
throw new Error('[AgentAdmit] generateKeyPair() is not supported. ' +
|
|
17
|
+
'AgentAdmit is a hosted service — all key management is handled server-side. ' +
|
|
18
|
+
'Remove any calls to generateKeyPair() from your codebase.');
|
|
19
|
+
}
|
|
20
|
+
function loadPrivateKey(_keyPath) {
|
|
21
|
+
throw new Error('[AgentAdmit] loadPrivateKey() is not supported. ' +
|
|
22
|
+
'AgentAdmit is a hosted service — JWT signing is handled by the hosted service. ' +
|
|
23
|
+
'Remove any calls to loadPrivateKey() from your codebase.');
|
|
24
|
+
}
|
|
25
|
+
function loadPublicKey(_keyPath) {
|
|
26
|
+
throw new Error('[AgentAdmit] loadPublicKey() is not supported. ' +
|
|
27
|
+
'AgentAdmit is a hosted service — token verification uses hosted introspection. ' +
|
|
28
|
+
'Remove any calls to loadPublicKey() from your codebase.');
|
|
29
|
+
}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentadmit/routes.ts
|
|
3
|
+
* Express router with all AgentAdmit endpoints.
|
|
4
|
+
*
|
|
5
|
+
* ALL token operations go through the AgentAdmit hosted service. The SDK does
|
|
6
|
+
* NOT sign JWTs, generate RSA keys, or serve JWKS endpoints. The hosted service
|
|
7
|
+
* owns all cryptographic operations.
|
|
8
|
+
*/
|
|
9
|
+
import { Router, Request } from 'express';
|
|
10
|
+
import { StorageBackend } from './storage';
|
|
11
|
+
interface RouterOptions {
|
|
12
|
+
storage: StorageBackend;
|
|
13
|
+
getCurrentUser: (req: Request) => Promise<Record<string, any> | null>;
|
|
14
|
+
determineRole?: (user: Record<string, any>) => string;
|
|
15
|
+
getUserTier?: (user: Record<string, any>) => string;
|
|
16
|
+
validateScopes?: (scopes: string[], user: Record<string, any>) => {
|
|
17
|
+
valid: boolean;
|
|
18
|
+
invalid: string[];
|
|
19
|
+
};
|
|
20
|
+
getEndpointsForScopes?: (scopes: string[]) => Record<string, any>[];
|
|
21
|
+
}
|
|
22
|
+
export declare function createAgentAdmitRouter(options: RouterOptions): {
|
|
23
|
+
wellknownRouter: Router;
|
|
24
|
+
agentadmitRouter: Router;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* agentadmit/routes.ts
|
|
4
|
+
* Express router with all AgentAdmit endpoints.
|
|
5
|
+
*
|
|
6
|
+
* ALL token operations go through the AgentAdmit hosted service. The SDK does
|
|
7
|
+
* NOT sign JWTs, generate RSA keys, or serve JWKS endpoints. The hosted service
|
|
8
|
+
* owns all cryptographic operations.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.createAgentAdmitRouter = createAgentAdmitRouter;
|
|
12
|
+
const express_1 = require("express");
|
|
13
|
+
const crypto_1 = require("crypto");
|
|
14
|
+
const config_1 = require("./config");
|
|
15
|
+
const auth_1 = require("./auth");
|
|
16
|
+
const AGENTADMIT_VERSION = '0.1';
|
|
17
|
+
/**
|
|
18
|
+
* Make an authenticated request to the AgentAdmit hosted service.
|
|
19
|
+
*/
|
|
20
|
+
async function callHostedService(path, body) {
|
|
21
|
+
const config = (0, config_1.getConfig)();
|
|
22
|
+
const url = `${config.agentadmit_api_url.replace(/\/$/, '')}${path}`;
|
|
23
|
+
const resp = await fetch(url, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: {
|
|
26
|
+
'Authorization': `Bearer ${config.api_key}`,
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
'X-App-Id': config.app_id,
|
|
29
|
+
},
|
|
30
|
+
body: JSON.stringify(body),
|
|
31
|
+
});
|
|
32
|
+
const data = await resp.json().catch(() => ({}));
|
|
33
|
+
return { status: resp.status, data };
|
|
34
|
+
}
|
|
35
|
+
function createAgentAdmitRouter(options) {
|
|
36
|
+
const config = (0, config_1.getConfig)();
|
|
37
|
+
const { storage, getCurrentUser } = options;
|
|
38
|
+
const determineRole = options.determineRole || (() => 'user');
|
|
39
|
+
const getUserTier = options.getUserTier || (() => config.default_tier);
|
|
40
|
+
const getEndpointsForScopes = options.getEndpointsForScopes || (() => []);
|
|
41
|
+
const validateScopes = options.validateScopes || ((scopes) => {
|
|
42
|
+
const validNames = new Set(config.scopes.map(s => s.name));
|
|
43
|
+
const invalid = scopes.filter(s => !validNames.has(s));
|
|
44
|
+
return { valid: invalid.length === 0, invalid };
|
|
45
|
+
});
|
|
46
|
+
const wellknownRouter = (0, express_1.Router)();
|
|
47
|
+
const agentadmitRouter = (0, express_1.Router)();
|
|
48
|
+
// Discovery
|
|
49
|
+
wellknownRouter.get('/.well-known/agentadmit', (_req, res) => {
|
|
50
|
+
const base = config.api_base_url.replace(/\/$/, '');
|
|
51
|
+
res.json({
|
|
52
|
+
agentadmit_version: AGENTADMIT_VERSION,
|
|
53
|
+
issuer: base,
|
|
54
|
+
app_name: config.app_name,
|
|
55
|
+
app_id: config.app_id,
|
|
56
|
+
api_base_url: base,
|
|
57
|
+
agentadmit_service_url: config.agentadmit_api_url,
|
|
58
|
+
scopes_endpoint: `${base}${config.route_prefix}/scopes`,
|
|
59
|
+
discovery_endpoint: `${base}${config.route_prefix}/discovery`,
|
|
60
|
+
connections_endpoint: `${base}${config.route_prefix}/connections`,
|
|
61
|
+
scopes_supported: config.scopes.map(s => s.name),
|
|
62
|
+
roles_supported: [...new Set(config.scopes.map(s => s.role || 'user'))],
|
|
63
|
+
duration_options: (0, config_1.getDurationOptions)(),
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
// Scopes
|
|
67
|
+
agentadmitRouter.get('/scopes', (_req, res) => {
|
|
68
|
+
res.json({
|
|
69
|
+
scopes: (0, config_1.getScopeMetadata)(),
|
|
70
|
+
roles: [...new Set(config.scopes.map(s => s.role || 'user'))],
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
// Durations
|
|
74
|
+
agentadmitRouter.get('/durations', (_req, res) => {
|
|
75
|
+
res.json({ durations: (0, config_1.getDurationOptions)() });
|
|
76
|
+
});
|
|
77
|
+
// Generate connection token — calls hosted service
|
|
78
|
+
agentadmitRouter.post('/connections/generate-token', async (req, res) => {
|
|
79
|
+
try {
|
|
80
|
+
const currentUser = await getCurrentUser(req);
|
|
81
|
+
if (!currentUser)
|
|
82
|
+
return res.status(401).json({ error: 'unauthorized' });
|
|
83
|
+
const { scopes, duration_seconds, label } = req.body;
|
|
84
|
+
if (!scopes || !Array.isArray(scopes)) {
|
|
85
|
+
return res.status(400).json({ error: 'invalid_request', error_description: 'scopes array required' });
|
|
86
|
+
}
|
|
87
|
+
const validation = validateScopes(scopes, currentUser);
|
|
88
|
+
if (!validation.valid) {
|
|
89
|
+
return res.status(400).json({ error: 'invalid_scope', invalid_scopes: validation.invalid });
|
|
90
|
+
}
|
|
91
|
+
const duration = duration_seconds || config.connection_token_ttl;
|
|
92
|
+
const userId = currentUser[config.user_lookup_field];
|
|
93
|
+
const role = determineRole(currentUser);
|
|
94
|
+
const userTier = getUserTier(currentUser);
|
|
95
|
+
await (0, auth_1.checkConnectionCap)(userId, userTier);
|
|
96
|
+
// Call AgentAdmit hosted service
|
|
97
|
+
const { status, data } = await callHostedService(`/api/v1/apps/${config.app_id}/token`, {
|
|
98
|
+
user_id: String(userId),
|
|
99
|
+
scopes,
|
|
100
|
+
duration_hours: Math.max(1, Math.floor(duration / 3600)),
|
|
101
|
+
label: label ?? null,
|
|
102
|
+
user_role: role,
|
|
103
|
+
metadata: { subscription_tier: userTier, app_name: config.app_name },
|
|
104
|
+
});
|
|
105
|
+
if (status !== 200 && status !== 201) {
|
|
106
|
+
console.error('[AgentAdmit] Hosted token generation failed:', status, data);
|
|
107
|
+
return res.status(502).json({ error: 'token_generation_failed', error_description: 'Authorization service could not generate token' });
|
|
108
|
+
}
|
|
109
|
+
// Store local record
|
|
110
|
+
// Use hosted service's connection_id if provided; generate a local fallback
|
|
111
|
+
// to prevent duplicate-key errors when hosting service omits it.
|
|
112
|
+
await storage.storeConnection({
|
|
113
|
+
connection_id: data.connection_id || `conn_${(0, crypto_1.randomBytes)(16).toString('base64url')}`,
|
|
114
|
+
user_id: String(userId),
|
|
115
|
+
scopes,
|
|
116
|
+
role,
|
|
117
|
+
agent_label: label,
|
|
118
|
+
duration_seconds: duration,
|
|
119
|
+
status: 'active',
|
|
120
|
+
});
|
|
121
|
+
res.json({
|
|
122
|
+
connection_token: data.token || data.connection_token,
|
|
123
|
+
expires_in: duration,
|
|
124
|
+
scopes,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
console.error('[AgentAdmit] Generate token error:', err);
|
|
129
|
+
res.status(500).json({ error: 'server_error', error_description: err.message });
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
// Token exchange — forwards to hosted service
|
|
133
|
+
agentadmitRouter.post('/token', async (req, res) => {
|
|
134
|
+
try {
|
|
135
|
+
const { grant_type, connection_token, agent_id, agent_label, agent_metadata } = req.body;
|
|
136
|
+
if (grant_type !== 'connection_token') {
|
|
137
|
+
return res.status(400).json({ error: 'unsupported_grant_type' });
|
|
138
|
+
}
|
|
139
|
+
if (!connection_token) {
|
|
140
|
+
return res.status(400).json({ error: 'invalid_request', error_description: 'connection_token required' });
|
|
141
|
+
}
|
|
142
|
+
// Forward to AgentAdmit hosted service
|
|
143
|
+
const { status, data } = await callHostedService('/api/v1/exchange', {
|
|
144
|
+
token: connection_token,
|
|
145
|
+
agent_label: agent_label ?? null,
|
|
146
|
+
agent_id: agent_id ?? null,
|
|
147
|
+
agent_metadata: agent_metadata ?? null,
|
|
148
|
+
});
|
|
149
|
+
if (status !== 200) {
|
|
150
|
+
return res.status(status < 500 ? status : 502).json(data);
|
|
151
|
+
}
|
|
152
|
+
// Add endpoint map if available
|
|
153
|
+
if (getEndpointsForScopes && data.scopes) {
|
|
154
|
+
data.endpoints = getEndpointsForScopes(data.scopes);
|
|
155
|
+
}
|
|
156
|
+
res.json(data);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
const status = err.statusCode || 500;
|
|
160
|
+
res.status(status).json(err.detail || { error: 'server_error', error_description: err.message });
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
// List connections
|
|
164
|
+
agentadmitRouter.get('/connections', async (req, res) => {
|
|
165
|
+
try {
|
|
166
|
+
const currentUser = await getCurrentUser(req);
|
|
167
|
+
if (!currentUser)
|
|
168
|
+
return res.status(401).json({ error: 'unauthorized' });
|
|
169
|
+
const userId = currentUser[config.user_lookup_field];
|
|
170
|
+
const connections = await storage.listConnections(userId);
|
|
171
|
+
res.json({ connections, total: connections.length });
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
res.status(500).json({ error: 'server_error' });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
// Delete connection — also notifies hosted service
|
|
178
|
+
agentadmitRouter.delete('/connections/:connectionId', async (req, res) => {
|
|
179
|
+
try {
|
|
180
|
+
const currentUser = await getCurrentUser(req);
|
|
181
|
+
if (!currentUser)
|
|
182
|
+
return res.status(401).json({ error: 'unauthorized' });
|
|
183
|
+
const userId = currentUser[config.user_lookup_field];
|
|
184
|
+
const conn = await storage.getConnection(req.params.connectionId);
|
|
185
|
+
if (!conn || conn.user_id !== String(userId)) {
|
|
186
|
+
return res.status(404).json({ error: 'not_found' });
|
|
187
|
+
}
|
|
188
|
+
if (conn.status !== 'active') {
|
|
189
|
+
return res.status(400).json({ error: 'already_revoked' });
|
|
190
|
+
}
|
|
191
|
+
// Notify hosted service
|
|
192
|
+
try {
|
|
193
|
+
await callHostedService('/api/v1/revoke', {
|
|
194
|
+
connection_id: req.params.connectionId,
|
|
195
|
+
reason: 'user_requested',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
console.warn('[AgentAdmit] Hosted revoke failed, revoking locally:', e);
|
|
200
|
+
}
|
|
201
|
+
await storage.revokeConnection(req.params.connectionId);
|
|
202
|
+
res.json({ revoked: true, connection_id: req.params.connectionId });
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
res.status(500).json({ error: 'server_error' });
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
return { wellknownRouter, agentadmitRouter };
|
|
209
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentadmit/storage.ts
|
|
3
|
+
* Abstract storage interface + MongoDB + Memory implementations.
|
|
4
|
+
*/
|
|
5
|
+
export interface StorageBackend {
|
|
6
|
+
storeConnection(connection: Record<string, any>): Promise<void>;
|
|
7
|
+
getConnection(connectionId: string): Promise<Record<string, any> | null>;
|
|
8
|
+
getActiveConnection(connectionId: string): Promise<Record<string, any> | null>;
|
|
9
|
+
updateConnection(connectionId: string, updates: Record<string, any>): Promise<boolean>;
|
|
10
|
+
revokeConnection(connectionId: string): Promise<boolean>;
|
|
11
|
+
listConnections(userId: string): Promise<Record<string, any>[]>;
|
|
12
|
+
countActiveConnections(userId: string): Promise<number>;
|
|
13
|
+
storeToken(tokenRecord: Record<string, any>): Promise<void>;
|
|
14
|
+
getToken(tokenHash: string): Promise<Record<string, any> | null>;
|
|
15
|
+
markTokenUsed(tokenHash: string): Promise<boolean>;
|
|
16
|
+
logAccess(entry: Record<string, any>): Promise<void>;
|
|
17
|
+
countAuditCalls(userId: string, periodStart: Date, periodEnd: Date): Promise<number>;
|
|
18
|
+
getUser(userId: string, lookupField: string): Promise<Record<string, any> | null>;
|
|
19
|
+
}
|
|
20
|
+
export declare class MongoDBStorage implements StorageBackend {
|
|
21
|
+
private db;
|
|
22
|
+
private connections;
|
|
23
|
+
private auditLog;
|
|
24
|
+
private tokens;
|
|
25
|
+
private users;
|
|
26
|
+
constructor(uri: string, database: string, connectionsCollection: string, auditLogCollection: string, tokensCollection: string);
|
|
27
|
+
setUsersCollection(name: string): void;
|
|
28
|
+
storeConnection(connection: Record<string, any>): Promise<void>;
|
|
29
|
+
getConnection(connectionId: string): Promise<Record<string, any> | null>;
|
|
30
|
+
getActiveConnection(connectionId: string): Promise<Record<string, any> | null>;
|
|
31
|
+
updateConnection(connectionId: string, updates: Record<string, any>): Promise<boolean>;
|
|
32
|
+
revokeConnection(connectionId: string): Promise<boolean>;
|
|
33
|
+
listConnections(userId: string): Promise<Record<string, any>[]>;
|
|
34
|
+
countActiveConnections(userId: string): Promise<number>;
|
|
35
|
+
storeToken(tokenRecord: Record<string, any>): Promise<void>;
|
|
36
|
+
getToken(tokenHash: string): Promise<Record<string, any> | null>;
|
|
37
|
+
markTokenUsed(tokenHash: string): Promise<boolean>;
|
|
38
|
+
logAccess(entry: Record<string, any>): Promise<void>;
|
|
39
|
+
countAuditCalls(userId: string, periodStart: Date, periodEnd: Date): Promise<number>;
|
|
40
|
+
getUser(userId: string, lookupField?: string): Promise<Record<string, any> | null>;
|
|
41
|
+
}
|
|
42
|
+
export declare class MemoryStorage implements StorageBackend {
|
|
43
|
+
private _connections;
|
|
44
|
+
private _tokens;
|
|
45
|
+
private _auditLog;
|
|
46
|
+
private _users;
|
|
47
|
+
storeConnection(connection: Record<string, any>): Promise<void>;
|
|
48
|
+
getConnection(connectionId: string): Promise<Record<string, any> | null>;
|
|
49
|
+
getActiveConnection(connectionId: string): Promise<Record<string, any> | null>;
|
|
50
|
+
updateConnection(connectionId: string, updates: Record<string, any>): Promise<boolean>;
|
|
51
|
+
revokeConnection(connectionId: string): Promise<boolean>;
|
|
52
|
+
listConnections(userId: string): Promise<Record<string, any>[]>;
|
|
53
|
+
countActiveConnections(userId: string): Promise<number>;
|
|
54
|
+
storeToken(tokenRecord: Record<string, any>): Promise<void>;
|
|
55
|
+
getToken(tokenHash: string): Promise<Record<string, any> | null>;
|
|
56
|
+
markTokenUsed(tokenHash: string): Promise<boolean>;
|
|
57
|
+
logAccess(entry: Record<string, any>): Promise<void>;
|
|
58
|
+
countAuditCalls(userId: string, periodStart: Date, periodEnd: Date): Promise<number>;
|
|
59
|
+
getUser(userId: string, lookupField?: string): Promise<Record<string, any> | null>;
|
|
60
|
+
addTestUser(userId: string, data: Record<string, any>): void;
|
|
61
|
+
}
|
|
62
|
+
export declare function createStorage(config: any): StorageBackend;
|