@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.
Files changed (89) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +35 -27
  3. package/package.json +12 -3
  4. package/src/adapters/adapter.ts +41 -0
  5. package/src/{connectors → adapters}/jwt.ts +148 -21
  6. package/src/auth-resource.ts +89 -0
  7. package/src/boundary.ts +200 -0
  8. package/src/errors.ts +1 -18
  9. package/src/extraction.ts +22 -16
  10. package/src/index.ts +22 -10
  11. package/src/permit.ts +2 -2
  12. package/src/rules.ts +2 -2
  13. package/src/testing.ts +5 -5
  14. package/src/trails/auth-verify.ts +13 -15
  15. package/.turbo/turbo-build.log +0 -1
  16. package/.turbo/turbo-lint.log +0 -3
  17. package/.turbo/turbo-typecheck.log +0 -1
  18. package/dist/adapter.d.ts +0 -26
  19. package/dist/adapter.d.ts.map +0 -1
  20. package/dist/adapter.js +0 -2
  21. package/dist/adapter.js.map +0 -1
  22. package/dist/adapters/jwt.d.ts +0 -25
  23. package/dist/adapters/jwt.d.ts.map +0 -1
  24. package/dist/adapters/jwt.js +0 -148
  25. package/dist/adapters/jwt.js.map +0 -1
  26. package/dist/auth-gate.d.ts +0 -18
  27. package/dist/auth-gate.d.ts.map +0 -1
  28. package/dist/auth-gate.js +0 -56
  29. package/dist/auth-gate.js.map +0 -1
  30. package/dist/auth-layer.d.ts +0 -18
  31. package/dist/auth-layer.d.ts.map +0 -1
  32. package/dist/auth-layer.js +0 -56
  33. package/dist/auth-layer.js.map +0 -1
  34. package/dist/auth-provision.d.ts +0 -11
  35. package/dist/auth-provision.d.ts.map +0 -1
  36. package/dist/auth-provision.js +0 -22
  37. package/dist/auth-provision.js.map +0 -1
  38. package/dist/auth-service.d.ts +0 -10
  39. package/dist/auth-service.d.ts.map +0 -1
  40. package/dist/auth-service.js +0 -21
  41. package/dist/auth-service.js.map +0 -1
  42. package/dist/connectors/connector.d.ts +0 -26
  43. package/dist/connectors/connector.d.ts.map +0 -1
  44. package/dist/connectors/connector.js +0 -2
  45. package/dist/connectors/connector.js.map +0 -1
  46. package/dist/connectors/jwt.d.ts +0 -25
  47. package/dist/connectors/jwt.d.ts.map +0 -1
  48. package/dist/connectors/jwt.js +0 -148
  49. package/dist/connectors/jwt.js.map +0 -1
  50. package/dist/errors.d.ts +0 -15
  51. package/dist/errors.d.ts.map +0 -1
  52. package/dist/errors.js +0 -15
  53. package/dist/errors.js.map +0 -1
  54. package/dist/extraction.d.ts +0 -20
  55. package/dist/extraction.d.ts.map +0 -1
  56. package/dist/extraction.js +0 -2
  57. package/dist/extraction.js.map +0 -1
  58. package/dist/index.d.ts +0 -11
  59. package/dist/index.d.ts.map +0 -1
  60. package/dist/index.js +0 -11
  61. package/dist/index.js.map +0 -1
  62. package/dist/permit.d.ts +0 -26
  63. package/dist/permit.d.ts.map +0 -1
  64. package/dist/permit.js +0 -17
  65. package/dist/permit.js.map +0 -1
  66. package/dist/rules.d.ts +0 -47
  67. package/dist/rules.d.ts.map +0 -1
  68. package/dist/rules.js +0 -127
  69. package/dist/rules.js.map +0 -1
  70. package/dist/testing.d.ts +0 -20
  71. package/dist/testing.d.ts.map +0 -1
  72. package/dist/testing.js +0 -22
  73. package/dist/testing.js.map +0 -1
  74. package/dist/trails/auth-verify.d.ts +0 -22
  75. package/dist/trails/auth-verify.d.ts.map +0 -1
  76. package/dist/trails/auth-verify.js +0 -85
  77. package/dist/trails/auth-verify.js.map +0 -1
  78. package/src/__tests__/auth-gate.test.ts +0 -130
  79. package/src/__tests__/auth-provision.test.ts +0 -62
  80. package/src/__tests__/auth-verify.test.ts +0 -278
  81. package/src/__tests__/connector.test.ts +0 -338
  82. package/src/__tests__/permit.test.ts +0 -122
  83. package/src/__tests__/rules.test.ts +0 -239
  84. package/src/__tests__/testing.test.ts +0 -57
  85. package/src/auth-gate.ts +0 -80
  86. package/src/auth-provision.ts +0 -26
  87. package/src/connectors/connector.ts +0 -35
  88. package/tsconfig.json +0 -9
  89. package/tsconfig.tsbuildinfo +0 -1
package/dist/permit.js DELETED
@@ -1,17 +0,0 @@
1
- /**
2
- * Type-safe accessor for `ctx.permit` with a downcast to `Permit`.
3
- *
4
- * `TrailContext.permit` is typed as `BasePermit` (id + scopes). This accessor
5
- * returns the full `Permit` when the auth gate has set one. Safe because
6
- * the auth gate is the only writer and always sets a full `Permit`.
7
- *
8
- * @example
9
- * ```typescript
10
- * const permit = getPermit(ctx);
11
- * if (permit) {
12
- * console.log(permit.roles, permit.tenantId);
13
- * }
14
- * ```
15
- */
16
- export const getPermit = (ctx) => ctx.permit === undefined ? undefined : ctx.permit;
17
- //# sourceMappingURL=permit.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"permit.js","sourceRoot":"","sources":["../src/permit.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAA4B,EAAsB,EAAE,CAC5E,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,GAAG,CAAC,MAAiB,CAAC"}
package/dist/rules.d.ts DELETED
@@ -1,47 +0,0 @@
1
- import type { Trail } from '@ontrails/core';
2
- /** A single governance finding from a permit rule. */
3
- export interface PermitDiagnostic {
4
- readonly trailId: string;
5
- readonly rule: string;
6
- readonly severity: 'error' | 'warning';
7
- readonly message: string;
8
- }
9
- type AnyTrail = Trail<unknown, unknown>;
10
- type Rule = (trails: readonly AnyTrail[]) => readonly PermitDiagnostic[];
11
- /**
12
- * Destructive trails without a real permit declaration are a governance failure.
13
- *
14
- * Reports an error for every trail with `intent: 'destroy'` that has no
15
- * `permit` field or explicitly opts out with `permit: 'public'`.
16
- */
17
- export declare const destroyWithoutPermit: Rule;
18
- /**
19
- * Write trails without a permit declaration get a warning.
20
- *
21
- * Trails with `intent: 'write'` (or no intent, which defaults to write) that
22
- * lack a permit are flagged unless `permit: 'public'` is explicitly set.
23
- */
24
- export declare const writeWithoutPermit: Rule;
25
- /**
26
- * Warns for scopes that don't follow the `entity:action` convention.
27
- *
28
- * A valid scope contains exactly one colon separating a non-empty entity
29
- * and a non-empty action.
30
- */
31
- export declare const scopeNamingConsistency: Rule;
32
- /**
33
- * Warns for scopes that appear in only one trail's permit.
34
- *
35
- * Catches typos like `user:wirte` by surfacing scopes not shared with any
36
- * other trail. Only runs when at least 2 trails have permit declarations.
37
- */
38
- export declare const orphanScopeDetection: Rule;
39
- /**
40
- * Run all permit governance rules against a set of trails.
41
- *
42
- * Returns a flat array of diagnostics from every rule. An empty array
43
- * means the topo passes all permit governance checks.
44
- */
45
- export declare const validatePermits: (trails: readonly Trail<unknown, unknown>[]) => readonly PermitDiagnostic[];
46
- export {};
47
- //# sourceMappingURL=rules.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAM5C,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAMD,KAAK,QAAQ,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AACxC,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,KAAK,SAAS,gBAAgB,EAAE,CAAC;AAiBzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,IAU5B,CAAC;AAMR;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,IAU1B,CAAC;AAgBR;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,EAAE,IAUlC,CAAC;AA2CJ;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,IAMlC,CAAC;AAaF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAC1B,QAAQ,SAAS,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,KACzC,SAAS,gBAAgB,EAA8C,CAAC"}
package/dist/rules.js DELETED
@@ -1,127 +0,0 @@
1
- /** Check whether a trail has any permit declaration (scopes object or 'public'). */
2
- const hasPermit = (t) => t.permit !== undefined;
3
- /** Extract scopes from a trail's permit declaration, or empty array. */
4
- const getScopes = (t) => {
5
- if (t.permit !== undefined && t.permit !== 'public') {
6
- return t.permit.scopes;
7
- }
8
- return [];
9
- };
10
- // ---------------------------------------------------------------------------
11
- // Rule: destroyWithoutPermit
12
- // ---------------------------------------------------------------------------
13
- /**
14
- * Destructive trails without a real permit declaration are a governance failure.
15
- *
16
- * Reports an error for every trail with `intent: 'destroy'` that has no
17
- * `permit` field or explicitly opts out with `permit: 'public'`.
18
- */
19
- export const destroyWithoutPermit = (trails) => trails
20
- .filter((t) => t.intent === 'destroy' && (!hasPermit(t) || t.permit === 'public'))
21
- .map((t) => ({
22
- message: `Trail "${t.id}" has intent 'destroy' but no permit declaration`,
23
- rule: 'destroyWithoutPermit',
24
- severity: 'error',
25
- trailId: t.id,
26
- }));
27
- // ---------------------------------------------------------------------------
28
- // Rule: writeWithoutPermit
29
- // ---------------------------------------------------------------------------
30
- /**
31
- * Write trails without a permit declaration get a warning.
32
- *
33
- * Trails with `intent: 'write'` (or no intent, which defaults to write) that
34
- * lack a permit are flagged unless `permit: 'public'` is explicitly set.
35
- */
36
- export const writeWithoutPermit = (trails) => trails
37
- .filter((t) => (t.intent === 'write' || t.intent === undefined) && !hasPermit(t))
38
- .map((t) => ({
39
- message: `Trail "${t.id}" has write intent but no permit declaration`,
40
- rule: 'writeWithoutPermit',
41
- severity: 'warning',
42
- trailId: t.id,
43
- }));
44
- // ---------------------------------------------------------------------------
45
- // Rule: scopeNamingConsistency
46
- // ---------------------------------------------------------------------------
47
- /** Returns true when a scope follows the `entity:action` convention. */
48
- const isValidScopeFormat = (scope) => {
49
- const parts = scope.split(':');
50
- return (parts.length === 2 &&
51
- (parts[0]?.length ?? 0) > 0 &&
52
- (parts[1]?.length ?? 0) > 0);
53
- };
54
- /**
55
- * Warns for scopes that don't follow the `entity:action` convention.
56
- *
57
- * A valid scope contains exactly one colon separating a non-empty entity
58
- * and a non-empty action.
59
- */
60
- export const scopeNamingConsistency = (trails) => trails.flatMap((t) => getScopes(t)
61
- .filter((scope) => !isValidScopeFormat(scope))
62
- .map((scope) => ({
63
- message: `Scope "${scope}" on trail "${t.id}" does not follow entity:action convention`,
64
- rule: 'scopeNamingConsistency',
65
- severity: 'warning',
66
- trailId: t.id,
67
- })));
68
- // ---------------------------------------------------------------------------
69
- // Rule: orphanScopeDetection
70
- // ---------------------------------------------------------------------------
71
- /** Build a map of scope -> set of trail IDs that declare it. */
72
- const buildScopeMap = (trails) => {
73
- const map = new Map();
74
- for (const t of trails) {
75
- for (const scope of getScopes(t)) {
76
- const existing = map.get(scope);
77
- if (existing) {
78
- existing.add(t.id);
79
- }
80
- else {
81
- map.set(scope, new Set([t.id]));
82
- }
83
- }
84
- }
85
- return map;
86
- };
87
- /** Filter trails that have a scoped (non-public) permit declaration. */
88
- const trailsWithScopedPermit = (trails) => trails.filter((t) => t.permit !== undefined && t.permit !== 'public');
89
- /** Convert orphan scope map entries into diagnostics. */
90
- const orphanDiagnostics = (scopeMap) => [...scopeMap.entries()]
91
- .filter(([, ids]) => ids.size === 1)
92
- .map(([scope, ids]) => ({
93
- message: `Scope "${scope}" appears only on trail "${[...ids][0]}" — possible typo`,
94
- rule: 'orphanScopeDetection',
95
- severity: 'warning',
96
- trailId: [...ids][0] ?? '',
97
- }));
98
- /**
99
- * Warns for scopes that appear in only one trail's permit.
100
- *
101
- * Catches typos like `user:wirte` by surfacing scopes not shared with any
102
- * other trail. Only runs when at least 2 trails have permit declarations.
103
- */
104
- export const orphanScopeDetection = (trails) => {
105
- const scoped = trailsWithScopedPermit(trails);
106
- if (scoped.length < 2) {
107
- return [];
108
- }
109
- return orphanDiagnostics(buildScopeMap(scoped));
110
- };
111
- // ---------------------------------------------------------------------------
112
- // Top-level validator
113
- // ---------------------------------------------------------------------------
114
- const allRules = [
115
- destroyWithoutPermit,
116
- writeWithoutPermit,
117
- scopeNamingConsistency,
118
- orphanScopeDetection,
119
- ];
120
- /**
121
- * Run all permit governance rules against a set of trails.
122
- *
123
- * Returns a flat array of diagnostics from every rule. An empty array
124
- * means the topo passes all permit governance checks.
125
- */
126
- export const validatePermits = (trails) => allRules.flatMap((rule) => rule(trails));
127
- //# sourceMappingURL=rules.js.map
package/dist/rules.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
package/dist/testing.d.ts DELETED
@@ -1,20 +0,0 @@
1
- import type { PermitRequirement } from '@ontrails/core';
2
- import type { Permit } from './permit.js';
3
- /**
4
- * Mint a synthetic test permit with exactly the declared scopes.
5
- * No admin permit, no wildcard — tests get only what the trail declares.
6
- */
7
- export declare const mintTestPermit: (options?: {
8
- readonly id?: string;
9
- readonly scopes?: readonly string[];
10
- readonly roles?: readonly string[];
11
- readonly tenantId?: string;
12
- }) => Permit;
13
- /**
14
- * Create a test permit matching a trail's permit requirement.
15
- * Extracts scopes from the requirement and mints a permit with exactly those scopes.
16
- */
17
- export declare const mintPermitForTrail: (trail: {
18
- readonly permit?: PermitRequirement | undefined;
19
- }) => Permit | undefined;
20
- //# sourceMappingURL=testing.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,UAAU;IACvC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,KAAG,MAOF,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACjD,KAAG,MAAM,GAAG,SAKZ,CAAC"}
package/dist/testing.js DELETED
@@ -1,22 +0,0 @@
1
- /**
2
- * Mint a synthetic test permit with exactly the declared scopes.
3
- * No admin permit, no wildcard — tests get only what the trail declares.
4
- */
5
- export const mintTestPermit = (options) => ({
6
- id: options?.id ??
7
- `test-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
8
- scopes: [...(options?.scopes ?? [])],
9
- ...(options?.roles === undefined ? {} : { roles: [...options.roles] }),
10
- ...(options?.tenantId === undefined ? {} : { tenantId: options.tenantId }),
11
- });
12
- /**
13
- * Create a test permit matching a trail's permit requirement.
14
- * Extracts scopes from the requirement and mints a permit with exactly those scopes.
15
- */
16
- export const mintPermitForTrail = (trail) => {
17
- if (!trail.permit || trail.permit === 'public') {
18
- return undefined;
19
- }
20
- return mintTestPermit({ scopes: trail.permit.scopes });
21
- };
22
- //# sourceMappingURL=testing.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAK9B,EAAU,EAAE,CAAC,CAAC;IACb,EAAE,EACA,OAAO,EAAE,EAAE;QACX,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;IAChE,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;IACpC,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IACtE,GAAG,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;CAC3E,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAElC,EAAsB,EAAE;IACvB,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,cAAc,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AACzD,CAAC,CAAC"}
@@ -1,22 +0,0 @@
1
- /**
2
- * Infrastructure trail that verifies a bearer token and returns the resolved permit.
3
- *
4
- * Reads the auth connector from `authProvision` — the connector is configured
5
- * at bootstrap (e.g. JWT with HMAC secret). The mock connector always
6
- * succeeds with a null permit, so `testAll(app)` works without configuration.
7
- */
8
- export declare const authVerify: import("@ontrails/core").Trail<{
9
- token: string;
10
- }, {
11
- valid: boolean;
12
- error?: string | undefined;
13
- errorCode?: "expired_token" | "insufficient_scope" | "invalid_token" | "missing_credentials" | undefined;
14
- permit?: {
15
- id: string;
16
- scopes: string[];
17
- metadata?: Record<string, unknown> | undefined;
18
- roles?: string[] | undefined;
19
- tenantId?: string | undefined;
20
- } | undefined;
21
- }>;
22
- //# sourceMappingURL=auth-verify.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth-verify.d.ts","sourceRoot":"","sources":["../../src/trails/auth-verify.ts"],"names":[],"mappings":"AA4CA;;;;;;GAMG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;EAiDrB,CAAC"}
@@ -1,85 +0,0 @@
1
- import { Result, TRAILHEAD_KEY, trail } from '@ontrails/core';
2
- import { z } from 'zod';
3
- import { authProvision } from '../auth-provision.js';
4
- const permitSchema = z.object({
5
- id: z.string(),
6
- metadata: z.record(z.string(), z.unknown()).optional(),
7
- roles: z.array(z.string()).optional(),
8
- scopes: z.array(z.string()),
9
- tenantId: z.string().optional(),
10
- });
11
- const authErrorCodeSchema = z.enum([
12
- 'expired_token',
13
- 'insufficient_scope',
14
- 'invalid_token',
15
- 'missing_credentials',
16
- ]);
17
- const toOutputPermit = (permit) => ({
18
- ...(permit.metadata === undefined
19
- ? {}
20
- : { metadata: { ...permit.metadata } }),
21
- ...(permit.roles === undefined ? {} : { roles: [...permit.roles] }),
22
- ...(permit.tenantId === undefined ? {} : { tenantId: permit.tenantId }),
23
- id: permit.id,
24
- scopes: [...permit.scopes],
25
- });
26
- const isTrailhead = (value) => value === 'http' || value === 'mcp' || value === 'cli';
27
- const getTrailhead = (ctx) => {
28
- const trailhead = ctx.extensions?.[TRAILHEAD_KEY];
29
- return isTrailhead(trailhead) ? trailhead : 'http';
30
- };
31
- /**
32
- * Infrastructure trail that verifies a bearer token and returns the resolved permit.
33
- *
34
- * Reads the auth connector from `authProvision` — the connector is configured
35
- * at bootstrap (e.g. JWT with HMAC secret). The mock connector always
36
- * succeeds with a null permit, so `testAll(app)` works without configuration.
37
- */
38
- export const authVerify = trail('auth.verify', {
39
- blaze: async (input, ctx) => {
40
- const connector = authProvision.from(ctx);
41
- const result = await connector.authenticate({
42
- bearerToken: input.token,
43
- requestId: ctx.requestId,
44
- trailhead: getTrailhead(ctx),
45
- });
46
- if (result.isErr()) {
47
- return Result.ok({
48
- error: result.error.message,
49
- errorCode: result.error.code,
50
- valid: false,
51
- });
52
- }
53
- const permit = result.value;
54
- if (!permit) {
55
- return Result.ok({
56
- error: 'No credentials',
57
- errorCode: 'missing_credentials',
58
- valid: false,
59
- });
60
- }
61
- return Result.ok({
62
- permit: toOutputPermit(permit),
63
- valid: true,
64
- });
65
- },
66
- examples: [
67
- {
68
- input: { token: 'test-token' },
69
- name: 'Verify a token',
70
- },
71
- ],
72
- input: z.object({
73
- token: z.string().min(1).describe('Bearer token to verify'),
74
- }),
75
- intent: 'read',
76
- meta: { category: 'infrastructure' },
77
- output: z.object({
78
- error: z.string().optional(),
79
- errorCode: authErrorCodeSchema.optional(),
80
- permit: permitSchema.optional(),
81
- valid: z.boolean(),
82
- }),
83
- provisions: [authProvision],
84
- });
85
- //# sourceMappingURL=auth-verify.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth-verify.js","sourceRoot":"","sources":["../../src/trails/auth-verify.ts"],"names":[],"mappings":"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"}
@@ -1,130 +0,0 @@
1
- /* oxlint-disable require-await -- gate wrappers satisfy async interfaces without awaiting */
2
- import { describe, expect, test } from 'bun:test';
3
-
4
- import { Result, trail } from '@ontrails/core';
5
- import type { TrailContext } from '@ontrails/core';
6
- import { z } from 'zod';
7
-
8
- import { authGate } from '../auth-gate';
9
- import { PermitError } from '../errors';
10
-
11
- // ---------------------------------------------------------------------------
12
- // Helpers
13
- // ---------------------------------------------------------------------------
14
-
15
- const makeCtx = (permit?: {
16
- id: string;
17
- scopes: readonly string[];
18
- }): TrailContext => ({
19
- abortSignal: AbortSignal.timeout(5000),
20
- permit,
21
- requestId: 'test-auth',
22
- });
23
-
24
- const okImpl = async () => Result.ok({ done: true });
25
-
26
- // ---------------------------------------------------------------------------
27
- // Tests
28
- // ---------------------------------------------------------------------------
29
-
30
- describe('authGate', () => {
31
- test('has correct name and description', () => {
32
- expect(authGate.name).toBe('auth');
33
- expect(authGate.description).toBeDefined();
34
- });
35
-
36
- describe('pass-through cases', () => {
37
- test('passes through when trail has no permit field', async () => {
38
- const t = trail('test.nopermit', {
39
- blaze: okImpl,
40
- input: z.object({}),
41
- output: z.object({ done: z.boolean() }),
42
- });
43
-
44
- const wrapped = authGate.wrap(t, okImpl);
45
- const result = await wrapped({}, makeCtx());
46
-
47
- expect(result.isOk()).toBe(true);
48
- expect(result.unwrap()).toEqual({ done: true });
49
- });
50
-
51
- test('passes through when trail permit is public', async () => {
52
- const t = trail('test.public', {
53
- blaze: okImpl,
54
- input: z.object({}),
55
- output: z.object({ done: z.boolean() }),
56
- permit: 'public',
57
- });
58
-
59
- const wrapped = authGate.wrap(t, okImpl);
60
- const result = await wrapped({}, makeCtx());
61
-
62
- expect(result.isOk()).toBe(true);
63
- expect(result.unwrap()).toEqual({ done: true });
64
- });
65
- });
66
-
67
- describe('scope enforcement', () => {
68
- const scopedTrail = trail('test.scoped', {
69
- blaze: okImpl,
70
- input: z.object({}),
71
- output: z.object({ done: z.boolean() }),
72
- permit: { scopes: ['user:read'] },
73
- });
74
-
75
- test('passes when ctx.permit has matching scopes', async () => {
76
- const wrapped = authGate.wrap(scopedTrail, okImpl);
77
- const result = await wrapped(
78
- {},
79
- makeCtx({ id: 'usr-1', scopes: ['user:read'] })
80
- );
81
-
82
- expect(result.isOk()).toBe(true);
83
- expect(result.unwrap()).toEqual({ done: true });
84
- });
85
-
86
- test('returns error when ctx has no permit', async () => {
87
- const wrapped = authGate.wrap(scopedTrail, okImpl);
88
- const result = await wrapped({}, makeCtx());
89
-
90
- expect(result.isErr()).toBe(true);
91
- const err = (result as unknown as { error: PermitError }).error;
92
- expect(err).toBeInstanceOf(PermitError);
93
- expect(err.message).toContain('No permit');
94
- });
95
-
96
- test('returns error when permit is missing required scopes', async () => {
97
- const multiScopeTrail = trail('test.multi', {
98
- blaze: okImpl,
99
- input: z.object({}),
100
- output: z.object({ done: z.boolean() }),
101
- permit: { scopes: ['user:read', 'user:write'] },
102
- });
103
-
104
- const wrapped = authGate.wrap(multiScopeTrail, okImpl);
105
- const result = await wrapped(
106
- {},
107
- makeCtx({ id: 'usr-1', scopes: ['user:read'] })
108
- );
109
-
110
- expect(result.isErr()).toBe(true);
111
- const err = (result as unknown as { error: PermitError }).error;
112
- expect(err).toBeInstanceOf(PermitError);
113
- expect(err.message).toContain('user:write');
114
- });
115
-
116
- test('passes when permit has superset of required scopes', async () => {
117
- const wrapped = authGate.wrap(scopedTrail, okImpl);
118
- const result = await wrapped(
119
- {},
120
- makeCtx({
121
- id: 'usr-1',
122
- scopes: ['user:read', 'user:write', 'admin'],
123
- })
124
- );
125
-
126
- expect(result.isOk()).toBe(true);
127
- expect(result.unwrap()).toEqual({ done: true });
128
- });
129
- });
130
- });
@@ -1,62 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
-
3
- import type { ProvisionContext } from '@ontrails/core';
4
-
5
- import type { AuthConnector } from '../connectors/connector.js';
6
- import { authProvision } from '../auth-provision.js';
7
- import type { PermitExtractionInput } from '../extraction.js';
8
-
9
- /** Minimal extraction input for tests. */
10
- const testInput = (
11
- overrides?: Partial<PermitExtractionInput>
12
- ): PermitExtractionInput => ({
13
- requestId: 'test-svc-req',
14
- trailhead: 'http',
15
- ...overrides,
16
- });
17
-
18
- // ---------------------------------------------------------------------------
19
- // Helpers
20
- // ---------------------------------------------------------------------------
21
-
22
- const testSvcCtx: ProvisionContext = {
23
- config: undefined,
24
- cwd: '/tmp',
25
- env: {},
26
- workspaceRoot: '/tmp',
27
- };
28
-
29
- // ---------------------------------------------------------------------------
30
- // Tests
31
- // ---------------------------------------------------------------------------
32
-
33
- describe('authProvision', () => {
34
- test('has correct id and kind', () => {
35
- expect(authProvision.id).toBe('auth');
36
- expect(authProvision.kind).toBe('provision');
37
- });
38
-
39
- test('has infrastructure meta', () => {
40
- expect(authProvision.meta).toEqual({ category: 'infrastructure' });
41
- });
42
-
43
- test('mock returns an AuthConnector', async () => {
44
- const mock = authProvision.mock?.();
45
- expect(mock).toBeDefined();
46
-
47
- const connector = mock as AuthConnector;
48
- const result = await connector.authenticate(testInput());
49
- expect(result.isOk()).toBe(true);
50
- expect(result.unwrap()).toBeNull();
51
- });
52
-
53
- test('create returns Result.ok with an AuthConnector', async () => {
54
- const result = await authProvision.create(testSvcCtx);
55
- expect(result.isOk()).toBe(true);
56
-
57
- const connector = result.unwrap() as AuthConnector;
58
- const authResult = await connector.authenticate(testInput());
59
- expect(authResult.isOk()).toBe(true);
60
- expect(authResult.unwrap()).toBeNull();
61
- });
62
- });