@ontrails/permits 1.0.0-beta.14 → 1.0.0-beta.16
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/CHANGELOG.md +76 -0
- package/README.md +35 -27
- package/package.json +12 -3
- package/src/adapters/adapter.ts +41 -0
- package/src/{connectors → adapters}/jwt.ts +148 -21
- package/src/auth-resource.ts +89 -0
- package/src/boundary.ts +200 -0
- package/src/errors.ts +1 -18
- package/src/extraction.ts +22 -16
- package/src/index.ts +22 -10
- package/src/permit.ts +2 -2
- package/src/rules.ts +2 -2
- package/src/testing.ts +5 -5
- package/src/trails/auth-verify.ts +13 -15
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/dist/adapter.d.ts +0 -26
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js +0 -2
- package/dist/adapter.js.map +0 -1
- package/dist/adapters/jwt.d.ts +0 -25
- package/dist/adapters/jwt.d.ts.map +0 -1
- package/dist/adapters/jwt.js +0 -148
- package/dist/adapters/jwt.js.map +0 -1
- package/dist/auth-gate.d.ts +0 -18
- package/dist/auth-gate.d.ts.map +0 -1
- package/dist/auth-gate.js +0 -56
- package/dist/auth-gate.js.map +0 -1
- package/dist/auth-layer.d.ts +0 -18
- package/dist/auth-layer.d.ts.map +0 -1
- package/dist/auth-layer.js +0 -56
- package/dist/auth-layer.js.map +0 -1
- package/dist/auth-provision.d.ts +0 -11
- package/dist/auth-provision.d.ts.map +0 -1
- package/dist/auth-provision.js +0 -22
- package/dist/auth-provision.js.map +0 -1
- package/dist/auth-service.d.ts +0 -10
- package/dist/auth-service.d.ts.map +0 -1
- package/dist/auth-service.js +0 -21
- package/dist/auth-service.js.map +0 -1
- package/dist/connectors/connector.d.ts +0 -26
- package/dist/connectors/connector.d.ts.map +0 -1
- package/dist/connectors/connector.js +0 -2
- package/dist/connectors/connector.js.map +0 -1
- package/dist/connectors/jwt.d.ts +0 -25
- package/dist/connectors/jwt.d.ts.map +0 -1
- package/dist/connectors/jwt.js +0 -148
- package/dist/connectors/jwt.js.map +0 -1
- package/dist/errors.d.ts +0 -15
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -15
- package/dist/errors.js.map +0 -1
- package/dist/extraction.d.ts +0 -20
- package/dist/extraction.d.ts.map +0 -1
- package/dist/extraction.js +0 -2
- package/dist/extraction.js.map +0 -1
- package/dist/index.d.ts +0 -11
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -11
- package/dist/index.js.map +0 -1
- package/dist/permit.d.ts +0 -26
- package/dist/permit.d.ts.map +0 -1
- package/dist/permit.js +0 -17
- package/dist/permit.js.map +0 -1
- package/dist/rules.d.ts +0 -47
- package/dist/rules.d.ts.map +0 -1
- package/dist/rules.js +0 -127
- package/dist/rules.js.map +0 -1
- package/dist/testing.d.ts +0 -20
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -22
- package/dist/testing.js.map +0 -1
- package/dist/trails/auth-verify.d.ts +0 -22
- package/dist/trails/auth-verify.d.ts.map +0 -1
- package/dist/trails/auth-verify.js +0 -85
- package/dist/trails/auth-verify.js.map +0 -1
- package/src/__tests__/auth-gate.test.ts +0 -130
- package/src/__tests__/auth-provision.test.ts +0 -62
- package/src/__tests__/auth-verify.test.ts +0 -278
- package/src/__tests__/connector.test.ts +0 -338
- package/src/__tests__/permit.test.ts +0 -122
- package/src/__tests__/rules.test.ts +0 -239
- package/src/__tests__/testing.test.ts +0 -57
- package/src/auth-gate.ts +0 -80
- package/src/auth-provision.ts +0 -26
- package/src/connectors/connector.ts +0 -35
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
package/src/boundary.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnyResource,
|
|
3
|
+
BasePermit,
|
|
4
|
+
ResourceOverrideMap,
|
|
5
|
+
SurfaceConfigValues,
|
|
6
|
+
Topo,
|
|
7
|
+
} from '@ontrails/core';
|
|
8
|
+
import {
|
|
9
|
+
AuthError,
|
|
10
|
+
Result,
|
|
11
|
+
ValidationError,
|
|
12
|
+
basePermitSchema,
|
|
13
|
+
resolveResourceConfig,
|
|
14
|
+
} from '@ontrails/core';
|
|
15
|
+
|
|
16
|
+
import type { AuthAdapter } from './adapters/adapter.js';
|
|
17
|
+
import { authAdapterSchema, authErrorSchema } from './adapters/adapter.js';
|
|
18
|
+
import type { PermitExtractionInput } from './extraction.js';
|
|
19
|
+
import { permitExtractionInputSchema } from './extraction.js';
|
|
20
|
+
|
|
21
|
+
/** Resource id of the auth adapter resource provided by `@ontrails/permits`. */
|
|
22
|
+
export const AUTH_RESOURCE_ID = 'auth';
|
|
23
|
+
|
|
24
|
+
type LocatedAuthResource =
|
|
25
|
+
| { readonly kind: 'override'; readonly value: unknown }
|
|
26
|
+
| { readonly kind: 'declared'; readonly resource: AnyResource };
|
|
27
|
+
|
|
28
|
+
export interface ResolvePermitFromBearerTokenOptions {
|
|
29
|
+
readonly bearerToken: string;
|
|
30
|
+
readonly graph: Topo;
|
|
31
|
+
readonly requestId: string;
|
|
32
|
+
readonly surface: PermitExtractionInput['surface'];
|
|
33
|
+
readonly resources?: ResourceOverrideMap | undefined;
|
|
34
|
+
readonly configValues?: SurfaceConfigValues | undefined;
|
|
35
|
+
readonly headers?: Headers | undefined;
|
|
36
|
+
readonly sessionId?: string | undefined;
|
|
37
|
+
readonly cwd?: string | undefined;
|
|
38
|
+
readonly env?: Record<string, string | undefined> | undefined;
|
|
39
|
+
readonly workspaceRoot?: string | undefined;
|
|
40
|
+
readonly missingAuthResourceMessage?: string | undefined;
|
|
41
|
+
readonly nullPermitMessage?: string | undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Resolve the auth resource override or registered resource on the topo. */
|
|
45
|
+
const lookupAuthResource = (
|
|
46
|
+
graph: Topo,
|
|
47
|
+
resources: ResourceOverrideMap | undefined,
|
|
48
|
+
missingAuthResourceMessage: string | undefined
|
|
49
|
+
): Result<LocatedAuthResource, ValidationError> => {
|
|
50
|
+
if (resources !== undefined && Object.hasOwn(resources, AUTH_RESOURCE_ID)) {
|
|
51
|
+
return Result.ok({
|
|
52
|
+
kind: 'override',
|
|
53
|
+
value: resources[AUTH_RESOURCE_ID],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const declared = graph.getResource(AUTH_RESOURCE_ID);
|
|
57
|
+
if (declared !== undefined) {
|
|
58
|
+
return Result.ok({ kind: 'declared', resource: declared });
|
|
59
|
+
}
|
|
60
|
+
return Result.err(
|
|
61
|
+
new ValidationError(
|
|
62
|
+
missingAuthResourceMessage ??
|
|
63
|
+
'Bearer token auth requires an auth adapter. Register authResource from @ontrails/permits in your topo.'
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Materialize the auth adapter from an override or by invoking the declared
|
|
70
|
+
* resource's `create()` factory.
|
|
71
|
+
*/
|
|
72
|
+
const materializeAuthAdapter = async (
|
|
73
|
+
resolved: LocatedAuthResource,
|
|
74
|
+
options: Pick<
|
|
75
|
+
ResolvePermitFromBearerTokenOptions,
|
|
76
|
+
'configValues' | 'cwd' | 'env' | 'workspaceRoot'
|
|
77
|
+
>
|
|
78
|
+
): Promise<Result<AuthAdapter, Error>> => {
|
|
79
|
+
if (resolved.kind === 'override') {
|
|
80
|
+
const parsed = authAdapterSchema.safeParse(resolved.value);
|
|
81
|
+
if (!parsed.success) {
|
|
82
|
+
return Result.err(
|
|
83
|
+
new ValidationError(
|
|
84
|
+
'Override for resource "auth" does not expose an authenticate() function.'
|
|
85
|
+
)
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return Result.ok(resolved.value as AuthAdapter);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const cwd = options.cwd ?? process.cwd();
|
|
92
|
+
const configResult = resolveResourceConfig(
|
|
93
|
+
resolved.resource,
|
|
94
|
+
options.configValues
|
|
95
|
+
);
|
|
96
|
+
if (configResult.isErr()) {
|
|
97
|
+
return configResult;
|
|
98
|
+
}
|
|
99
|
+
const created = await resolved.resource.create({
|
|
100
|
+
config: configResult.value,
|
|
101
|
+
cwd,
|
|
102
|
+
env: options.env ?? {},
|
|
103
|
+
workspaceRoot: options.workspaceRoot ?? cwd,
|
|
104
|
+
});
|
|
105
|
+
if (created.isErr()) {
|
|
106
|
+
return created;
|
|
107
|
+
}
|
|
108
|
+
const parsed = authAdapterSchema.safeParse(created.value);
|
|
109
|
+
if (!parsed.success) {
|
|
110
|
+
return Result.err(
|
|
111
|
+
new ValidationError(
|
|
112
|
+
'Auth resource factory returned a value without an authenticate() function.'
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return Result.ok(created.value as AuthAdapter);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Resolve a surface-extracted bearer token to a `BasePermit`.
|
|
121
|
+
*
|
|
122
|
+
* Surfaces own credential extraction. This helper owns the shared auth
|
|
123
|
+
* adapter lookup, invocation, error normalization, and BasePermit projection.
|
|
124
|
+
*/
|
|
125
|
+
export const resolvePermitFromBearerToken = async (
|
|
126
|
+
options: ResolvePermitFromBearerTokenOptions
|
|
127
|
+
): Promise<Result<BasePermit, Error>> => {
|
|
128
|
+
const located = lookupAuthResource(
|
|
129
|
+
options.graph,
|
|
130
|
+
options.resources,
|
|
131
|
+
options.missingAuthResourceMessage
|
|
132
|
+
);
|
|
133
|
+
if (located.isErr()) {
|
|
134
|
+
return located;
|
|
135
|
+
}
|
|
136
|
+
const adapterResult = await materializeAuthAdapter(located.value, options);
|
|
137
|
+
if (adapterResult.isErr()) {
|
|
138
|
+
return adapterResult;
|
|
139
|
+
}
|
|
140
|
+
const inputResult = permitExtractionInputSchema.safeParse({
|
|
141
|
+
bearerToken: options.bearerToken,
|
|
142
|
+
...(options.headers === undefined ? {} : { headers: options.headers }),
|
|
143
|
+
requestId: options.requestId,
|
|
144
|
+
...(options.sessionId === undefined
|
|
145
|
+
? {}
|
|
146
|
+
: { sessionId: options.sessionId }),
|
|
147
|
+
surface: options.surface,
|
|
148
|
+
});
|
|
149
|
+
if (!inputResult.success) {
|
|
150
|
+
return Result.err(
|
|
151
|
+
new ValidationError('Invalid bearer token extraction input.', {
|
|
152
|
+
context: { issues: inputResult.error.issues },
|
|
153
|
+
})
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
let authResult: Awaited<ReturnType<AuthAdapter['authenticate']>>;
|
|
157
|
+
try {
|
|
158
|
+
authResult = await adapterResult.value.authenticate(inputResult.data);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
const errorOptions =
|
|
161
|
+
error instanceof Error
|
|
162
|
+
? { cause: error, context: { code: 'invalid_token' } }
|
|
163
|
+
: { context: { code: 'invalid_token' } };
|
|
164
|
+
return Result.err(
|
|
165
|
+
new AuthError('Auth adapter threw while authenticating bearer token', {
|
|
166
|
+
...errorOptions,
|
|
167
|
+
})
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (authResult.isErr()) {
|
|
171
|
+
const parsedError = authErrorSchema.safeParse(authResult.error);
|
|
172
|
+
const { code, message } = parsedError.success
|
|
173
|
+
? parsedError.data
|
|
174
|
+
: {
|
|
175
|
+
code: 'invalid_token' as const,
|
|
176
|
+
message: 'Auth adapter returned a malformed error',
|
|
177
|
+
};
|
|
178
|
+
return Result.err(new AuthError(message, { context: { code } }));
|
|
179
|
+
}
|
|
180
|
+
if (authResult.value === null) {
|
|
181
|
+
return Result.err(
|
|
182
|
+
new AuthError(
|
|
183
|
+
options.nullPermitMessage ??
|
|
184
|
+
'Auth adapter did not produce a permit for bearer token',
|
|
185
|
+
{
|
|
186
|
+
context: { code: 'missing_credentials' },
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
const permit = basePermitSchema.safeParse(authResult.value);
|
|
192
|
+
if (!permit.success) {
|
|
193
|
+
return Result.err(
|
|
194
|
+
new AuthError('Auth adapter returned a malformed permit', {
|
|
195
|
+
context: { code: 'invalid_token', issues: permit.error.issues },
|
|
196
|
+
})
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return Result.ok(permit.data);
|
|
200
|
+
};
|
package/src/errors.ts
CHANGED
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Error returned when permit scope enforcement fails.
|
|
5
|
-
*
|
|
6
|
-
* Extends `PermissionError` (category `'permission'`, HTTP 403) because
|
|
7
|
-
* it represents an *authorization* failure — the caller's identity is known
|
|
8
|
-
* but lacks the required scopes.
|
|
9
|
-
*/
|
|
10
|
-
export class PermitError extends PermissionError {
|
|
11
|
-
constructor(
|
|
12
|
-
message: string,
|
|
13
|
-
options?: { cause?: Error; context?: Record<string, unknown> }
|
|
14
|
-
) {
|
|
15
|
-
super(message, options);
|
|
16
|
-
this.name = 'PermitError';
|
|
17
|
-
}
|
|
18
|
-
}
|
|
1
|
+
export { PermitError } from '@ontrails/core';
|
package/src/extraction.ts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const permitExtractionInputSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
/** Bearer token from Authorization header or equivalent. */
|
|
6
|
+
bearerToken: z.string().optional(),
|
|
7
|
+
/** Raw headers (HTTP surface only, typically). */
|
|
8
|
+
headers: z.instanceof(Headers).optional(),
|
|
9
|
+
/** Correlation ID for tracing. */
|
|
10
|
+
requestId: z.string(),
|
|
11
|
+
/** Session identifier from transport handshake. */
|
|
12
|
+
sessionId: z.string().optional(),
|
|
13
|
+
/** Which surface produced this extraction. */
|
|
14
|
+
surface: z.enum(['http', 'mcp', 'cli']),
|
|
15
|
+
})
|
|
16
|
+
.readonly();
|
|
17
|
+
|
|
1
18
|
/**
|
|
2
|
-
* Normalized input for auth
|
|
19
|
+
* Normalized input for auth adapters.
|
|
3
20
|
*
|
|
4
|
-
* Each
|
|
5
|
-
*
|
|
6
|
-
*
|
|
21
|
+
* Each surface extracts raw credentials from its transport and normalizes them
|
|
22
|
+
* into this shape. No surface types (Request, McpSession, etc.) cross into
|
|
23
|
+
* core -- only this schema-derived contract.
|
|
7
24
|
*/
|
|
8
|
-
export
|
|
9
|
-
/** Which trailhead produced this extraction */
|
|
10
|
-
readonly trailhead: 'http' | 'mcp' | 'cli';
|
|
11
|
-
/** Bearer token from Authorization header or equivalent */
|
|
12
|
-
readonly bearerToken?: string;
|
|
13
|
-
/** Session identifier from transport handshake */
|
|
14
|
-
readonly sessionId?: string;
|
|
15
|
-
/** Raw headers (HTTP trailhead only, typically) */
|
|
16
|
-
readonly headers?: Headers;
|
|
17
|
-
/** Correlation ID for tracing */
|
|
18
|
-
readonly requestId: string;
|
|
19
|
-
}
|
|
25
|
+
export type PermitExtractionInput = z.infer<typeof permitExtractionInputSchema>;
|
package/src/index.ts
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
export {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
authAdapterSchema,
|
|
3
|
+
authErrorSchema,
|
|
4
|
+
type AuthAdapter,
|
|
4
5
|
type AuthError,
|
|
5
|
-
} from './
|
|
6
|
+
} from './adapters/adapter.js';
|
|
6
7
|
export {
|
|
7
|
-
|
|
8
|
-
type
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export {
|
|
8
|
+
createJwtAdapter,
|
|
9
|
+
type JwtAlgorithm,
|
|
10
|
+
type JwtAdapterOptions,
|
|
11
|
+
} from './adapters/jwt.js';
|
|
12
|
+
export {
|
|
13
|
+
authResource,
|
|
14
|
+
authResourceConfigSchema,
|
|
15
|
+
type AuthResourceConfig,
|
|
16
|
+
} from './auth-resource.js';
|
|
17
|
+
export {
|
|
18
|
+
AUTH_RESOURCE_ID,
|
|
19
|
+
resolvePermitFromBearerToken,
|
|
20
|
+
type ResolvePermitFromBearerTokenOptions,
|
|
21
|
+
} from './boundary.js';
|
|
12
22
|
export { authVerify } from './trails/auth-verify.js';
|
|
13
23
|
export { PermitError } from './errors.js';
|
|
14
|
-
export {
|
|
24
|
+
export {
|
|
25
|
+
permitExtractionInputSchema,
|
|
26
|
+
type PermitExtractionInput,
|
|
27
|
+
} from './extraction.js';
|
|
15
28
|
export { type Permit, getPermit } from './permit.js';
|
|
16
29
|
export { validatePermits, type PermitDiagnostic } from './rules.js';
|
|
17
|
-
export { mintTestPermit, mintPermitForTrail } from './testing.js';
|
package/src/permit.ts
CHANGED
|
@@ -11,8 +11,8 @@ export interface Permit extends BasePermit {
|
|
|
11
11
|
* Type-safe accessor for `ctx.permit` with a downcast to `Permit`.
|
|
12
12
|
*
|
|
13
13
|
* `TrailContext.permit` is typed as `BasePermit` (id + scopes). This accessor
|
|
14
|
-
* returns the full `Permit` when the auth
|
|
15
|
-
* the auth
|
|
14
|
+
* returns the full `Permit` when the auth layer has set one. Safe because
|
|
15
|
+
* the auth layer is the only writer and always sets a full `Permit`.
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
18
|
* ```typescript
|
package/src/rules.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface PermitDiagnostic {
|
|
|
16
16
|
// Helpers
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
|
|
19
|
-
type AnyTrail = Trail<unknown, unknown>;
|
|
19
|
+
type AnyTrail = Trail<unknown, unknown, unknown>;
|
|
20
20
|
type Rule = (trails: readonly AnyTrail[]) => readonly PermitDiagnostic[];
|
|
21
21
|
|
|
22
22
|
/** Check whether a trail has any permit declaration (scopes object or 'public'). */
|
|
@@ -179,5 +179,5 @@ const allRules: readonly Rule[] = [
|
|
|
179
179
|
* means the topo passes all permit governance checks.
|
|
180
180
|
*/
|
|
181
181
|
export const validatePermits = (
|
|
182
|
-
trails: readonly Trail<unknown, unknown>[]
|
|
182
|
+
trails: readonly Trail<unknown, unknown, unknown>[]
|
|
183
183
|
): readonly PermitDiagnostic[] => allRules.flatMap((rule) => rule(trails));
|
package/src/testing.ts
CHANGED
|
@@ -3,10 +3,10 @@ import type { PermitRequirement } from '@ontrails/core';
|
|
|
3
3
|
import type { Permit } from './permit.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Create a synthetic test permit with exactly the declared scopes.
|
|
7
7
|
* No admin permit, no wildcard — tests get only what the trail declares.
|
|
8
8
|
*/
|
|
9
|
-
export const
|
|
9
|
+
export const createTestPermit = (options?: {
|
|
10
10
|
readonly id?: string;
|
|
11
11
|
readonly scopes?: readonly string[];
|
|
12
12
|
readonly roles?: readonly string[];
|
|
@@ -22,13 +22,13 @@ export const mintTestPermit = (options?: {
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Create a test permit matching a trail's permit requirement.
|
|
25
|
-
* Extracts scopes from the requirement and
|
|
25
|
+
* Extracts scopes from the requirement and creates a permit with exactly those scopes.
|
|
26
26
|
*/
|
|
27
|
-
export const
|
|
27
|
+
export const createPermitForTrail = (trail: {
|
|
28
28
|
readonly permit?: PermitRequirement | undefined;
|
|
29
29
|
}): Permit | undefined => {
|
|
30
30
|
if (!trail.permit || trail.permit === 'public') {
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
33
|
-
return
|
|
33
|
+
return createTestPermit({ scopes: trail.permit.scopes });
|
|
34
34
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Result,
|
|
1
|
+
import { Result, SURFACE_KEY, trail } from '@ontrails/core';
|
|
2
2
|
import type { TrailContext } from '@ontrails/core';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { authResource } from '../auth-resource.js';
|
|
6
6
|
import type { PermitExtractionInput } from '../extraction.js';
|
|
7
7
|
import type { Permit } from '../permit.js';
|
|
8
8
|
|
|
@@ -30,32 +30,30 @@ const toOutputPermit = (permit: Permit) => ({
|
|
|
30
30
|
scopes: [...permit.scopes],
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const isSurfaceName = (
|
|
34
34
|
value: unknown
|
|
35
|
-
): value is PermitExtractionInput['
|
|
35
|
+
): value is PermitExtractionInput['surface'] =>
|
|
36
36
|
value === 'http' || value === 'mcp' || value === 'cli';
|
|
37
37
|
|
|
38
|
-
const
|
|
39
|
-
ctx
|
|
40
|
-
):
|
|
41
|
-
const trailhead = ctx.extensions?.[TRAILHEAD_KEY];
|
|
42
|
-
return isTrailhead(trailhead) ? trailhead : 'http';
|
|
38
|
+
const getSurface = (ctx: TrailContext): PermitExtractionInput['surface'] => {
|
|
39
|
+
const surface = ctx.extensions?.[SURFACE_KEY];
|
|
40
|
+
return isSurfaceName(surface) ? surface : 'http';
|
|
43
41
|
};
|
|
44
42
|
|
|
45
43
|
/**
|
|
46
44
|
* Infrastructure trail that verifies a bearer token and returns the resolved permit.
|
|
47
45
|
*
|
|
48
|
-
* Reads the auth
|
|
49
|
-
* at bootstrap (e.g. JWT with HMAC secret). The mock
|
|
46
|
+
* Reads the auth adapter from `authResource` — the adapter is configured
|
|
47
|
+
* at bootstrap (e.g. JWT with HMAC secret). The mock adapter always
|
|
50
48
|
* succeeds with a null permit, so `testAll(app)` works without configuration.
|
|
51
49
|
*/
|
|
52
50
|
export const authVerify = trail('auth.verify', {
|
|
53
51
|
blaze: async (input, ctx) => {
|
|
54
|
-
const
|
|
55
|
-
const result = await
|
|
52
|
+
const adapter = authResource.from(ctx);
|
|
53
|
+
const result = await adapter.authenticate({
|
|
56
54
|
bearerToken: input.token,
|
|
57
55
|
requestId: ctx.requestId,
|
|
58
|
-
|
|
56
|
+
surface: getSurface(ctx),
|
|
59
57
|
});
|
|
60
58
|
|
|
61
59
|
if (result.isErr()) {
|
|
@@ -97,5 +95,5 @@ export const authVerify = trail('auth.verify', {
|
|
|
97
95
|
permit: permitSchema.optional(),
|
|
98
96
|
valid: z.boolean(),
|
|
99
97
|
}),
|
|
100
|
-
|
|
98
|
+
resources: [authResource],
|
|
101
99
|
});
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
$ tsc -b
|
package/.turbo/turbo-lint.log
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
$ tsc --noEmit
|
package/dist/adapter.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { Result } from '@ontrails/core';
|
|
2
|
-
import type { PermitExtractionInput } from './extraction.js';
|
|
3
|
-
import type { Permit } from './permit.js';
|
|
4
|
-
/**
|
|
5
|
-
* @deprecated Use {@link PermitExtractionInput} instead. Kept as an alias
|
|
6
|
-
* for backward compatibility during migration.
|
|
7
|
-
*/
|
|
8
|
-
export type AuthCredentials = PermitExtractionInput;
|
|
9
|
-
/** Errors from auth adapters. */
|
|
10
|
-
export interface AuthError {
|
|
11
|
-
readonly code: 'expired_token' | 'insufficient_scope' | 'invalid_token' | 'missing_credentials';
|
|
12
|
-
readonly message: string;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Auth adapter port. Given extraction input, produce a permit or an error.
|
|
16
|
-
*
|
|
17
|
-
* The adapter receives the full {@link PermitExtractionInput} — surface,
|
|
18
|
-
* headers, requestId, and credential fields — so it can make richer
|
|
19
|
-
* decisions (e.g., rate-limit by surface or correlate via requestId).
|
|
20
|
-
*
|
|
21
|
-
* Deliberately narrow — no session management, no token refresh.
|
|
22
|
-
*/
|
|
23
|
-
export interface AuthAdapter {
|
|
24
|
-
readonly authenticate: (input: PermitExtractionInput) => Promise<Result<Permit | null, AuthError>>;
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=adapter.d.ts.map
|
package/dist/adapter.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAEpD,iCAAiC;AACjC,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EACT,eAAe,GACf,oBAAoB,GACpB,eAAe,GACf,qBAAqB,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,YAAY,EAAE,CACrB,KAAK,EAAE,qBAAqB,KACzB,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;CAChD"}
|
package/dist/adapter.js
DELETED
package/dist/adapter.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":""}
|
package/dist/adapters/jwt.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { AuthAdapter } from '../adapter.js';
|
|
2
|
-
/** Configuration for the JWT auth adapter. */
|
|
3
|
-
export interface JwtAdapterOptions {
|
|
4
|
-
/** HMAC secret for HS256 verification. */
|
|
5
|
-
readonly secret?: string;
|
|
6
|
-
/** JWKS endpoint for RS256/ES256 (not yet implemented). */
|
|
7
|
-
readonly jwksUrl?: string;
|
|
8
|
-
/** Expected issuer claim. */
|
|
9
|
-
readonly issuer?: string;
|
|
10
|
-
/** Expected audience claim. */
|
|
11
|
-
readonly audience?: string;
|
|
12
|
-
/** Claim containing scopes (default: 'scope'). */
|
|
13
|
-
readonly scopesClaim?: string;
|
|
14
|
-
/** Claim containing roles (default: 'roles'). */
|
|
15
|
-
readonly rolesClaim?: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Create a JWT auth adapter using Bun's native crypto.
|
|
19
|
-
*
|
|
20
|
-
* Verifies HS256-signed JWTs, extracts claims into a Permit, and checks
|
|
21
|
-
* issuer/audience when configured. Returns `Result.ok(null)` when no
|
|
22
|
-
* credentials are provided.
|
|
23
|
-
*/
|
|
24
|
-
export declare const createJwtAdapter: (options: JwtAdapterOptions) => AuthAdapter;
|
|
25
|
-
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/adapters/jwt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,eAAe,CAAC;AAI5D,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,6BAA6B;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,+BAA+B;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iDAAiD;IACjD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AA2LD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,iBAAiB,KAAG,WAe7D,CAAC"}
|
package/dist/adapters/jwt.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { Result } from '@ontrails/core';
|
|
2
|
-
// ---------------------------------------------------------------------------
|
|
3
|
-
// Helpers (defined before callers)
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
|
-
const authErr = (code, message) => Result.err({ code, message });
|
|
6
|
-
/** Base64url-decode a string to bytes. */
|
|
7
|
-
const base64urlDecode = (input) => {
|
|
8
|
-
const padded = input
|
|
9
|
-
.replaceAll('-', '+')
|
|
10
|
-
.replaceAll('_', '/')
|
|
11
|
-
.padEnd(input.length + ((4 - (input.length % 4)) % 4), '=');
|
|
12
|
-
const binary = atob(padded);
|
|
13
|
-
const bytes = new Uint8Array(binary.length);
|
|
14
|
-
for (let i = 0; i < binary.length; i += 1) {
|
|
15
|
-
bytes[i] = binary.codePointAt(i) ?? 0;
|
|
16
|
-
}
|
|
17
|
-
return bytes;
|
|
18
|
-
};
|
|
19
|
-
/** Decode a JWT payload without verifying the signature. */
|
|
20
|
-
const decodePayload = (token) => {
|
|
21
|
-
const parts = token.split('.');
|
|
22
|
-
if (parts.length !== 3) {
|
|
23
|
-
return undefined;
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
const json = new TextDecoder().decode(base64urlDecode(parts[1] ?? ''));
|
|
27
|
-
return JSON.parse(json);
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return undefined;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
/** Import a secret as an HMAC CryptoKey. */
|
|
34
|
-
const importHmacKey = (secret) => {
|
|
35
|
-
const encoder = new TextEncoder();
|
|
36
|
-
return crypto.subtle.importKey('raw', encoder.encode(secret), { hash: 'SHA-256', name: 'HMAC' }, false, ['verify']);
|
|
37
|
-
};
|
|
38
|
-
/** Verify the HMAC-SHA256 signature of a JWT. */
|
|
39
|
-
const verifyHmacSignature = (token, key) => {
|
|
40
|
-
const lastDot = token.lastIndexOf('.');
|
|
41
|
-
if (lastDot === -1) {
|
|
42
|
-
return Promise.resolve(false);
|
|
43
|
-
}
|
|
44
|
-
const data = token.slice(0, lastDot);
|
|
45
|
-
const signature = base64urlDecode(token.slice(lastDot + 1));
|
|
46
|
-
const encoder = new TextEncoder();
|
|
47
|
-
return crypto.subtle.verify('HMAC', key, signature.buffer, encoder.encode(data));
|
|
48
|
-
};
|
|
49
|
-
/** Validate standard claims (exp, iss, aud). */
|
|
50
|
-
const validateClaims = (payload, options) => {
|
|
51
|
-
if (payload.exp !== undefined &&
|
|
52
|
-
payload.exp < Math.floor(Date.now() / 1000)) {
|
|
53
|
-
return { code: 'expired_token', message: 'Token has expired' };
|
|
54
|
-
}
|
|
55
|
-
if (options.issuer && payload.iss !== options.issuer) {
|
|
56
|
-
return { code: 'invalid_token', message: 'Issuer mismatch' };
|
|
57
|
-
}
|
|
58
|
-
if (options.audience) {
|
|
59
|
-
const { aud } = payload;
|
|
60
|
-
const matches = Array.isArray(aud)
|
|
61
|
-
? aud.includes(options.audience)
|
|
62
|
-
: aud === options.audience;
|
|
63
|
-
if (!matches) {
|
|
64
|
-
return { code: 'invalid_token', message: 'Audience mismatch' };
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return undefined;
|
|
68
|
-
};
|
|
69
|
-
/** Extract scopes from a payload claim (space-separated string or array). */
|
|
70
|
-
const extractScopes = (payload, claim) => {
|
|
71
|
-
const raw = payload[claim];
|
|
72
|
-
if (typeof raw === 'string') {
|
|
73
|
-
return raw.split(' ').filter(Boolean);
|
|
74
|
-
}
|
|
75
|
-
if (Array.isArray(raw)) {
|
|
76
|
-
return raw.filter((s) => typeof s === 'string' && s.length > 0);
|
|
77
|
-
}
|
|
78
|
-
return [];
|
|
79
|
-
};
|
|
80
|
-
/** Extract roles from a payload claim (string array). */
|
|
81
|
-
const extractRoles = (payload, claim) => {
|
|
82
|
-
const raw = payload[claim];
|
|
83
|
-
if (!Array.isArray(raw)) {
|
|
84
|
-
return undefined;
|
|
85
|
-
}
|
|
86
|
-
return raw.filter((r) => typeof r === 'string');
|
|
87
|
-
};
|
|
88
|
-
/** Build a Permit from a validated JWT payload. */
|
|
89
|
-
const buildPermit = (payload, options) => {
|
|
90
|
-
if (!payload.sub) {
|
|
91
|
-
return authErr('invalid_token', 'Missing subject claim (sub)');
|
|
92
|
-
}
|
|
93
|
-
const roles = extractRoles(payload, options.rolesClaim ?? 'roles');
|
|
94
|
-
return Result.ok({
|
|
95
|
-
id: payload.sub,
|
|
96
|
-
scopes: extractScopes(payload, options.scopesClaim ?? 'scope'),
|
|
97
|
-
...(roles ? { roles } : {}),
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
/** Verify the signature and return the decoded payload, or an error. */
|
|
101
|
-
const decodeAndVerify = async (token, secret) => {
|
|
102
|
-
const payload = decodePayload(token);
|
|
103
|
-
if (!payload) {
|
|
104
|
-
return authErr('invalid_token', 'Malformed JWT');
|
|
105
|
-
}
|
|
106
|
-
try {
|
|
107
|
-
const key = await importHmacKey(secret);
|
|
108
|
-
const valid = await verifyHmacSignature(token, key);
|
|
109
|
-
return valid
|
|
110
|
-
? Result.ok(payload)
|
|
111
|
-
: authErr('invalid_token', 'Invalid signature');
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
return authErr('invalid_token', 'Malformed token signature');
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
/** Validate claims and build a permit from a verified payload. */
|
|
118
|
-
const payloadToPermit = (payload, options) => {
|
|
119
|
-
const claimError = validateClaims(payload, options);
|
|
120
|
-
if (claimError) {
|
|
121
|
-
return Result.err(claimError);
|
|
122
|
-
}
|
|
123
|
-
return buildPermit(payload, options);
|
|
124
|
-
};
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
// Factory
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
/**
|
|
129
|
-
* Create a JWT auth adapter using Bun's native crypto.
|
|
130
|
-
*
|
|
131
|
-
* Verifies HS256-signed JWTs, extracts claims into a Permit, and checks
|
|
132
|
-
* issuer/audience when configured. Returns `Result.ok(null)` when no
|
|
133
|
-
* credentials are provided.
|
|
134
|
-
*/
|
|
135
|
-
export const createJwtAdapter = (options) => {
|
|
136
|
-
const authenticate = async (input) => {
|
|
137
|
-
if (!input.bearerToken) {
|
|
138
|
-
return Result.ok(null);
|
|
139
|
-
}
|
|
140
|
-
if (!options.secret) {
|
|
141
|
-
return authErr('invalid_token', 'No secret configured');
|
|
142
|
-
}
|
|
143
|
-
const decoded = await decodeAndVerify(input.bearerToken, options.secret);
|
|
144
|
-
return decoded.isErr() ? decoded : payloadToPermit(decoded.value, options);
|
|
145
|
-
};
|
|
146
|
-
return { authenticate };
|
|
147
|
-
};
|
|
148
|
-
//# sourceMappingURL=jwt.js.map
|
package/dist/adapters/jwt.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/adapters/jwt.ts"],"names":[],"mappings":"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"}
|