memory-journal-mcp 6.1.2 → 6.2.1
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/README.md +44 -28
- package/dist/{chunk-X4SWFATC.js → chunk-BI4ZNSKA.js} +38 -24
- package/dist/{chunk-HCEWINSB.js → chunk-N6EBIDN7.js} +99 -102
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/tools-WPRY5MJ6.js +2 -0
- package/package.json +10 -1
- package/skills/github-commander/SKILL.md +151 -0
- package/skills/github-commander/config/project-config.example.md +125 -0
- package/skills/github-commander/workflows/code-quality-audit.md +80 -0
- package/skills/github-commander/workflows/full-audit.md +134 -0
- package/skills/github-commander/workflows/issue-triage.md +239 -0
- package/skills/github-commander/workflows/milestone-sprint.md +81 -0
- package/skills/github-commander/workflows/perf-audit.md +142 -0
- package/skills/github-commander/workflows/pr-review.md +123 -0
- package/skills/github-commander/workflows/security-audit.md +170 -0
- package/skills/github-commander/workflows/update-deps.md +109 -0
- package/.dockerignore +0 -139
- package/.gitattributes +0 -20
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -95
- package/.github/ISSUE_TEMPLATE/config.yml +0 -11
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -110
- package/.github/ISSUE_TEMPLATE/question.md +0 -78
- package/.github/aw/actions-lock.json +0 -14
- package/.github/copilot-instructions.md +0 -122
- package/.github/dependabot.yml +0 -93
- package/.github/pull_request_template.md +0 -135
- package/.github/workflows/README.md +0 -133
- package/.github/workflows/agentics-maintenance.yml +0 -141
- package/.github/workflows/auto-release.yml +0 -68
- package/.github/workflows/ci-health-monitor.lock.yml +0 -1121
- package/.github/workflows/ci-health-monitor.md +0 -87
- package/.github/workflows/codeql.yml +0 -41
- package/.github/workflows/dependabot-auto-merge.yml +0 -42
- package/.github/workflows/dependency-maintenance.lock.yml +0 -1182
- package/.github/workflows/dependency-maintenance.md +0 -147
- package/.github/workflows/docker-publish.yml +0 -254
- package/.github/workflows/docs-drift-detector.lock.yml +0 -1142
- package/.github/workflows/docs-drift-detector.md +0 -115
- package/.github/workflows/lint-and-test.yml +0 -60
- package/.github/workflows/publish-npm.yml +0 -85
- package/.github/workflows/secrets-scanning.yml +0 -32
- package/.github/workflows/security-update.yml +0 -127
- package/.gitleaks.toml +0 -9
- package/.prettierignore +0 -21
- package/.prettierrc +0 -33
- package/.scout-ignore +0 -12
- package/.trivyignore +0 -21
- package/CHANGELOG.md +0 -1814
- package/CODE_OF_CONDUCT.md +0 -133
- package/CONTRIBUTING.md +0 -263
- package/DOCKER_README.md +0 -331
- package/Dockerfile +0 -128
- package/SECURITY.md +0 -227
- package/UNRELEASED.md +0 -1
- package/dist/tools-T4U5A3X4.js +0 -2
- package/docker-compose.yml +0 -71
- package/docs/README.md +0 -18
- package/docs/agentic-journal-synergy.md +0 -175
- package/docs/copilot-setup.md +0 -72
- package/eslint.config.js +0 -110
- package/mcp-config-example.json +0 -21
- package/playwright.config.ts +0 -35
- package/releases/v2.1.0.md +0 -220
- package/releases/v2.2.0.md +0 -168
- package/releases/v3.0.0.md +0 -237
- package/releases/v3.1.0.md +0 -104
- package/releases/v3.1.1.md +0 -42
- package/releases/v3.1.2.md +0 -40
- package/releases/v3.1.3.md +0 -64
- package/releases/v3.1.4.md +0 -32
- package/releases/v3.1.5.md +0 -44
- package/releases/v4.0.0.md +0 -71
- package/releases/v4.1.0.md +0 -88
- package/releases/v4.2.0.md +0 -90
- package/releases/v4.3.0.md +0 -92
- package/releases/v4.3.1.md +0 -69
- package/releases/v4.4.0.md +0 -120
- package/releases/v4.4.1.md +0 -33
- package/releases/v4.4.2.md +0 -31
- package/releases/v4.5.0.md +0 -116
- package/releases/v5.0.0.md +0 -105
- package/releases/v5.0.1.md +0 -25
- package/releases/v5.1.0.md +0 -83
- package/releases/v5.1.1.md +0 -10
- package/releases/v6.0.0.md +0 -48
- package/releases/v6.0.1.md +0 -36
- package/releases/v6.1.0.md +0 -68
- package/releases/v6.1.1.md +0 -30
- package/releases/v6.1.2.md +0 -23
- package/scripts/generate-server-instructions.ts +0 -306
- package/scripts/server-instructions-function-body.ts +0 -107
- package/scripts/server-instructions-gotchas.ts +0 -45
- package/server.json +0 -42
- package/social-preview.png +0 -0
- package/src/auth/auth-context.ts +0 -78
- package/src/auth/authorization-server-discovery.ts +0 -263
- package/src/auth/errors.ts +0 -215
- package/src/auth/index.ts +0 -58
- package/src/auth/middleware.ts +0 -392
- package/src/auth/oauth-resource-server.ts +0 -170
- package/src/auth/scope-map.ts +0 -46
- package/src/auth/scopes.ts +0 -256
- package/src/auth/token-validator.ts +0 -293
- package/src/auth/transport-agnostic.ts +0 -164
- package/src/auth/types.ts +0 -372
- package/src/cli.ts +0 -279
- package/src/codemode/api-constants.ts +0 -263
- package/src/codemode/api.ts +0 -302
- package/src/codemode/auto-return.ts +0 -65
- package/src/codemode/index.ts +0 -47
- package/src/codemode/sandbox-factory.ts +0 -144
- package/src/codemode/sandbox.ts +0 -220
- package/src/codemode/security.ts +0 -155
- package/src/codemode/types.ts +0 -228
- package/src/codemode/worker-sandbox.ts +0 -277
- package/src/codemode/worker-script.ts +0 -239
- package/src/constants/icons.ts +0 -183
- package/src/constants/server-instructions.md +0 -166
- package/src/constants/server-instructions.ts +0 -514
- package/src/database/adapter-factory.ts +0 -16
- package/src/database/core/entry-columns.ts +0 -10
- package/src/database/core/interfaces.ts +0 -188
- package/src/database/core/schema.ts +0 -152
- package/src/database/sqlite-adapter/backup.ts +0 -167
- package/src/database/sqlite-adapter/entries/crud.ts +0 -233
- package/src/database/sqlite-adapter/entries/importance.ts +0 -76
- package/src/database/sqlite-adapter/entries/index.ts +0 -142
- package/src/database/sqlite-adapter/entries/search.ts +0 -294
- package/src/database/sqlite-adapter/entries/shared.ts +0 -102
- package/src/database/sqlite-adapter/entries/statistics.ts +0 -162
- package/src/database/sqlite-adapter/index.ts +0 -265
- package/src/database/sqlite-adapter/native-connection.ts +0 -301
- package/src/database/sqlite-adapter/relationships.ts +0 -70
- package/src/database/sqlite-adapter/tags.ts +0 -182
- package/src/filtering/tool-filter.ts +0 -312
- package/src/github/github-integration/client.ts +0 -114
- package/src/github/github-integration/index.ts +0 -297
- package/src/github/github-integration/insights.ts +0 -155
- package/src/github/github-integration/issues.ts +0 -213
- package/src/github/github-integration/milestones.ts +0 -262
- package/src/github/github-integration/projects.ts +0 -414
- package/src/github/github-integration/pull-requests.ts +0 -235
- package/src/github/github-integration/repository.ts +0 -110
- package/src/github/github-integration/types.ts +0 -43
- package/src/handlers/prompts/github.ts +0 -210
- package/src/handlers/prompts/index.ts +0 -97
- package/src/handlers/prompts/workflow.ts +0 -361
- package/src/handlers/resources/core/briefing/context-section.ts +0 -182
- package/src/handlers/resources/core/briefing/github-section.ts +0 -354
- package/src/handlers/resources/core/briefing/index.ts +0 -106
- package/src/handlers/resources/core/briefing/user-message.ts +0 -114
- package/src/handlers/resources/core/health.ts +0 -75
- package/src/handlers/resources/core/index.ts +0 -31
- package/src/handlers/resources/core/instructions.ts +0 -45
- package/src/handlers/resources/core/utilities.ts +0 -310
- package/src/handlers/resources/github.ts +0 -340
- package/src/handlers/resources/graph.ts +0 -218
- package/src/handlers/resources/help.ts +0 -410
- package/src/handlers/resources/index.ts +0 -143
- package/src/handlers/resources/shared.ts +0 -219
- package/src/handlers/resources/team.ts +0 -134
- package/src/handlers/resources/templates.ts +0 -334
- package/src/handlers/tools/admin.ts +0 -351
- package/src/handlers/tools/analytics.ts +0 -346
- package/src/handlers/tools/backup.ts +0 -272
- package/src/handlers/tools/codemode.ts +0 -188
- package/src/handlers/tools/core.ts +0 -359
- package/src/handlers/tools/error-fields-mixin.ts +0 -10
- package/src/handlers/tools/export.ts +0 -150
- package/src/handlers/tools/github/copilot-tools.ts +0 -72
- package/src/handlers/tools/github/helpers.ts +0 -125
- package/src/handlers/tools/github/insights-tools.ts +0 -112
- package/src/handlers/tools/github/issue-tools.ts +0 -442
- package/src/handlers/tools/github/kanban-tools.ts +0 -153
- package/src/handlers/tools/github/milestone-tools.ts +0 -371
- package/src/handlers/tools/github/mutation-tools.ts +0 -17
- package/src/handlers/tools/github/read-tools.ts +0 -302
- package/src/handlers/tools/github/schemas.ts +0 -435
- package/src/handlers/tools/github.ts +0 -39
- package/src/handlers/tools/index.ts +0 -255
- package/src/handlers/tools/relationships.ts +0 -390
- package/src/handlers/tools/schemas.ts +0 -165
- package/src/handlers/tools/search.ts +0 -448
- package/src/handlers/tools/team/admin-tools.ts +0 -164
- package/src/handlers/tools/team/analytics-tools.ts +0 -233
- package/src/handlers/tools/team/backup-tools.ts +0 -83
- package/src/handlers/tools/team/core-tools.ts +0 -197
- package/src/handlers/tools/team/export-tools.ts +0 -130
- package/src/handlers/tools/team/helpers.ts +0 -66
- package/src/handlers/tools/team/index.ts +0 -45
- package/src/handlers/tools/team/relationship-tools.ts +0 -219
- package/src/handlers/tools/team/schemas.ts +0 -558
- package/src/handlers/tools/team/search-tools.ts +0 -145
- package/src/handlers/tools/team/vector-tools.ts +0 -261
- package/src/index.ts +0 -57
- package/src/server/mcp-server.ts +0 -446
- package/src/server/registration.ts +0 -141
- package/src/server/scheduler.ts +0 -283
- package/src/transports/http/handlers.ts +0 -78
- package/src/transports/http/index.ts +0 -8
- package/src/transports/http/security.ts +0 -147
- package/src/transports/http/server/index.ts +0 -397
- package/src/transports/http/server/legacy-sse.ts +0 -87
- package/src/transports/http/server/stateful.ts +0 -222
- package/src/transports/http/server/stateless.ts +0 -42
- package/src/transports/http/types.ts +0 -132
- package/src/types/entities.ts +0 -145
- package/src/types/error-types.ts +0 -92
- package/src/types/errors.ts +0 -200
- package/src/types/filtering.ts +0 -55
- package/src/types/github.ts +0 -216
- package/src/types/index.ts +0 -348
- package/src/utils/error-helpers.ts +0 -78
- package/src/utils/errors/error-response-fields.ts +0 -29
- package/src/utils/errors/suggestions.ts +0 -94
- package/src/utils/github-helpers.ts +0 -33
- package/src/utils/logger.ts +0 -107
- package/src/utils/mcp-logger.ts +0 -155
- package/src/utils/progress-utils.ts +0 -100
- package/src/utils/query-helpers.ts +0 -78
- package/src/utils/resource-annotations.ts +0 -75
- package/src/utils/security-utils.ts +0 -198
- package/src/utils/vector-index-helpers.ts +0 -24
- package/src/vector/vector-search-manager.ts +0 -409
- package/src/version.ts +0 -15
- package/test-server/README.md +0 -193
- package/test-server/code-map.md +0 -399
- package/test-server/test-agent-experience.md +0 -213
- package/test-server/test-filter-instructions.mjs +0 -295
- package/test-server/test-instruction-levels.mjs +0 -102
- package/test-server/test-preflight.md +0 -55
- package/test-server/test-prompts.mjs +0 -185
- package/test-server/test-scheduler.mjs +0 -174
- package/test-server/test-tool-annotations.mjs +0 -115
- package/test-server/test-tools-codemode.md +0 -632
- package/test-server/test-tools-codemode2.md +0 -1218
- package/test-server/test-tools-team.md +0 -215
- package/test-server/test-tools.md +0 -429
- package/test-server/test-tools2.md +0 -361
- package/test-server/test-tools3.md +0 -396
- package/test-server/tool-reference.md +0 -231
- package/tests/README.md +0 -54
- package/tests/auth/auth-context.test.ts +0 -162
- package/tests/auth/authorization-server-discovery.test.ts +0 -265
- package/tests/auth/errors.test.ts +0 -170
- package/tests/auth/middleware.test.ts +0 -585
- package/tests/auth/oauth-resource-server.test.ts +0 -173
- package/tests/auth/scope-map.test.ts +0 -66
- package/tests/auth/scopes.test.ts +0 -347
- package/tests/auth/token-validator.test.ts +0 -271
- package/tests/codemode/api.test.ts +0 -396
- package/tests/codemode/auto-return.test.ts +0 -167
- package/tests/codemode/codemode-tool-handlers.test.ts +0 -197
- package/tests/codemode/sandbox-factory.test.ts +0 -152
- package/tests/codemode/sandbox.test.ts +0 -190
- package/tests/codemode/security.test.ts +0 -242
- package/tests/codemode/worker-sandbox.test.ts +0 -106
- package/tests/constants/icons.test.ts +0 -101
- package/tests/constants/server-instructions.test.ts +0 -514
- package/tests/database/crud-workflow-branches.test.ts +0 -418
- package/tests/database/database-branches.test.ts +0 -132
- package/tests/database/entries-auth-branches.test.ts +0 -390
- package/tests/database/native-connection.test.ts +0 -249
- package/tests/database/shared-helpers.test.ts +0 -103
- package/tests/database/sqlite-adapter.bench.ts +0 -63
- package/tests/database/sqlite-adapter.test.ts +0 -690
- package/tests/database/tags.test.ts +0 -134
- package/tests/e2e/README.md +0 -39
- package/tests/e2e/auth.spec.ts +0 -106
- package/tests/e2e/codemode-abuse.spec.ts +0 -75
- package/tests/e2e/health.spec.ts +0 -63
- package/tests/e2e/helpers.ts +0 -139
- package/tests/e2e/oauth-discovery.spec.ts +0 -102
- package/tests/e2e/oauth-scopes.spec.ts +0 -222
- package/tests/e2e/payloads-admin.spec.ts +0 -76
- package/tests/e2e/payloads-analytics.spec.ts +0 -37
- package/tests/e2e/payloads-backup-restore.spec.ts +0 -102
- package/tests/e2e/payloads-backup.spec.ts +0 -44
- package/tests/e2e/payloads-codemode-api.spec.ts +0 -131
- package/tests/e2e/payloads-codemode-readonly.spec.ts +0 -116
- package/tests/e2e/payloads-codemode.spec.ts +0 -116
- package/tests/e2e/payloads-core.spec.ts +0 -82
- package/tests/e2e/payloads-error-contracts.spec.ts +0 -159
- package/tests/e2e/payloads-export.spec.ts +0 -46
- package/tests/e2e/payloads-github-degradation.spec.ts +0 -73
- package/tests/e2e/payloads-github.spec.ts +0 -176
- package/tests/e2e/payloads-relationships.spec.ts +0 -56
- package/tests/e2e/payloads-search.spec.ts +0 -64
- package/tests/e2e/payloads-team-happy.spec.ts +0 -231
- package/tests/e2e/payloads-team.spec.ts +0 -174
- package/tests/e2e/prompts-expanded.spec.ts +0 -137
- package/tests/e2e/prompts.spec.ts +0 -62
- package/tests/e2e/protocols.spec.ts +0 -134
- package/tests/e2e/rate-limiting.spec.ts +0 -291
- package/tests/e2e/resources-briefing-env.spec.ts +0 -106
- package/tests/e2e/resources-complete.spec.ts +0 -180
- package/tests/e2e/resources-expanded.spec.ts +0 -83
- package/tests/e2e/resources-instructions-levels.spec.ts +0 -145
- package/tests/e2e/resources-templates.spec.ts +0 -123
- package/tests/e2e/resources.spec.ts +0 -103
- package/tests/e2e/scheduler.spec.ts +0 -79
- package/tests/e2e/security.spec.ts +0 -112
- package/tests/e2e/session-advanced.spec.ts +0 -152
- package/tests/e2e/sessions.spec.ts +0 -95
- package/tests/e2e/stateless.spec.ts +0 -79
- package/tests/e2e/streaming.spec.ts +0 -176
- package/tests/e2e/tool-filtering-presets.spec.ts +0 -192
- package/tests/e2e/tool-filtering.spec.ts +0 -77
- package/tests/e2e/tools.spec.ts +0 -111
- package/tests/filtering/tool-filter.test.ts +0 -314
- package/tests/github/client-issues-errors.test.ts +0 -433
- package/tests/github/github-integration-branches.test.ts +0 -490
- package/tests/github/github-integration.test.ts +0 -1015
- package/tests/github/github-managers-branches.test.ts +0 -907
- package/tests/github/pull-requests.test.ts +0 -334
- package/tests/handlers/analytics-branches.test.ts +0 -222
- package/tests/handlers/backup-branches.test.ts +0 -270
- package/tests/handlers/briefing-context-section.test.ts +0 -388
- package/tests/handlers/briefing-github-section.test.ts +0 -392
- package/tests/handlers/briefing-user-message.test.ts +0 -405
- package/tests/handlers/codemode-tools.test.ts +0 -85
- package/tests/handlers/copilot-tools.test.ts +0 -126
- package/tests/handlers/error-path-coverage.test.ts +0 -324
- package/tests/handlers/export-tools.test.ts +0 -203
- package/tests/handlers/github-resource-handlers.test.ts +0 -929
- package/tests/handlers/github-tool-handlers.test.ts +0 -1452
- package/tests/handlers/handler-error-branches.test.ts +0 -346
- package/tests/handlers/help-resource.test.ts +0 -92
- package/tests/handlers/prompt-handler-coverage.test.ts +0 -108
- package/tests/handlers/prompt-handlers.test.ts +0 -131
- package/tests/handlers/resource-handler-coverage.test.ts +0 -281
- package/tests/handlers/resource-handlers.test.ts +0 -357
- package/tests/handlers/resource-prompt-branches.test.ts +0 -495
- package/tests/handlers/search-tool-handlers.test.ts +0 -379
- package/tests/handlers/targeted-gap-closure.test.ts +0 -387
- package/tests/handlers/team-admin.test.ts +0 -291
- package/tests/handlers/team-analytics.test.ts +0 -220
- package/tests/handlers/team-core.test.ts +0 -148
- package/tests/handlers/team-data.test.ts +0 -198
- package/tests/handlers/team-relationships.test.ts +0 -271
- package/tests/handlers/team-resource-handlers.test.ts +0 -161
- package/tests/handlers/team-search.test.ts +0 -134
- package/tests/handlers/team-tool-handlers.test.ts +0 -301
- package/tests/handlers/team-vector.test.ts +0 -213
- package/tests/handlers/template-github-branches.test.ts +0 -676
- package/tests/handlers/tool-annotations.test.ts +0 -90
- package/tests/handlers/tool-handler-coverage.test.ts +0 -514
- package/tests/handlers/tool-handlers.test.ts +0 -510
- package/tests/handlers/tool-output-schemas.test.ts +0 -116
- package/tests/handlers/vector-tool-handlers.test.ts +0 -238
- package/tests/security/sql-injection.test.ts +0 -284
- package/tests/server/mcp-server.bench.ts +0 -55
- package/tests/server/mcp-server.test.ts +0 -1326
- package/tests/server/scheduler.test.ts +0 -400
- package/tests/transports/http-legacy-sse.test.ts +0 -275
- package/tests/transports/http-security.test.ts +0 -322
- package/tests/transports/http-stateful.test.ts +0 -487
- package/tests/transports/http-transport-server.test.ts +0 -301
- package/tests/transports/http-transport.test.ts +0 -771
- package/tests/utils/github-helpers.test.ts +0 -58
- package/tests/utils/logger.test.ts +0 -180
- package/tests/utils/mcp-logger.test.ts +0 -211
- package/tests/utils/progress-utils.test.ts +0 -156
- package/tests/utils/query-helpers.test.ts +0 -80
- package/tests/utils/security-utils.test.ts +0 -82
- package/tests/vector/vector-search-branches.test.ts +0 -111
- package/tests/vector/vector-search-manager.test.ts +0 -375
- package/tests/vector/vector-search.bench.ts +0 -48
- package/tsconfig.json +0 -42
- package/tsup.config.ts +0 -19
- package/vitest.config.ts +0 -25
|
@@ -1,585 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* memory-journal-mcp — OAuth Middleware Unit Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for Express middleware, token extraction, path matching,
|
|
5
|
-
* scope enforcement, error handling, and transport-agnostic utilities.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
9
|
-
import {
|
|
10
|
-
extractBearerToken,
|
|
11
|
-
requireScope,
|
|
12
|
-
requireAnyScope,
|
|
13
|
-
requireToolScope,
|
|
14
|
-
oauthErrorHandler,
|
|
15
|
-
createAuthMiddleware,
|
|
16
|
-
createAuthenticatedContext,
|
|
17
|
-
validateAuth,
|
|
18
|
-
formatOAuthError,
|
|
19
|
-
} from '../../src/auth/middleware.js'
|
|
20
|
-
import {
|
|
21
|
-
TokenMissingError,
|
|
22
|
-
InvalidTokenError,
|
|
23
|
-
InsufficientScopeError,
|
|
24
|
-
} from '../../src/auth/errors.js'
|
|
25
|
-
import { TokenValidator } from '../../src/auth/token-validator.js'
|
|
26
|
-
|
|
27
|
-
// =============================================================================
|
|
28
|
-
// extractBearerToken
|
|
29
|
-
// =============================================================================
|
|
30
|
-
|
|
31
|
-
describe('extractBearerToken', () => {
|
|
32
|
-
it('should extract token from valid Bearer header', () => {
|
|
33
|
-
expect(extractBearerToken('Bearer abc123')).toBe('abc123')
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('should return null for missing header', () => {
|
|
37
|
-
expect(extractBearerToken(undefined)).toBeNull()
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('should return null for non-Bearer scheme', () => {
|
|
41
|
-
expect(extractBearerToken('Basic abc123')).toBeNull()
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('should return null for empty token', () => {
|
|
45
|
-
expect(extractBearerToken('Bearer ')).toBeNull()
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('should return null for malformed header', () => {
|
|
49
|
-
expect(extractBearerToken('Bearer')).toBeNull()
|
|
50
|
-
expect(extractBearerToken('Bearer a b c')).toBeNull()
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('should be case-insensitive for scheme', () => {
|
|
54
|
-
expect(extractBearerToken('bearer abc123')).toBe('abc123')
|
|
55
|
-
expect(extractBearerToken('BEARER abc123')).toBe('abc123')
|
|
56
|
-
})
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
// =============================================================================
|
|
60
|
-
// requireScope
|
|
61
|
-
// =============================================================================
|
|
62
|
-
|
|
63
|
-
describe('requireScope', () => {
|
|
64
|
-
it('should call next when scope is present', () => {
|
|
65
|
-
const middleware = requireScope('read')
|
|
66
|
-
|
|
67
|
-
const req = { auth: { scopes: ['read', 'write'] } } as never
|
|
68
|
-
const res = { status: vi.fn().mockReturnThis(), json: vi.fn(), setHeader: vi.fn() } as never
|
|
69
|
-
const next = vi.fn()
|
|
70
|
-
|
|
71
|
-
middleware(req, res, next)
|
|
72
|
-
expect(next).toHaveBeenCalled()
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('should return 403 when scope is missing', () => {
|
|
76
|
-
const middleware = requireScope('admin')
|
|
77
|
-
|
|
78
|
-
const req = { auth: { scopes: ['read'] } } as never
|
|
79
|
-
const statusMock = vi.fn().mockReturnThis()
|
|
80
|
-
const res = { status: statusMock, json: vi.fn(), setHeader: vi.fn() } as never
|
|
81
|
-
const next = vi.fn()
|
|
82
|
-
|
|
83
|
-
middleware(req, res, next)
|
|
84
|
-
expect(next).not.toHaveBeenCalled()
|
|
85
|
-
expect(statusMock).toHaveBeenCalledWith(403)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('should return 401 when no auth context', () => {
|
|
89
|
-
const middleware = requireScope('read')
|
|
90
|
-
|
|
91
|
-
const req = {} as never
|
|
92
|
-
const statusMock = vi.fn().mockReturnThis()
|
|
93
|
-
const res = { status: statusMock, json: vi.fn() } as never
|
|
94
|
-
const next = vi.fn()
|
|
95
|
-
|
|
96
|
-
middleware(req, res, next)
|
|
97
|
-
expect(statusMock).toHaveBeenCalledWith(401)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('should grant access for admin scope', () => {
|
|
101
|
-
const middleware = requireScope('read')
|
|
102
|
-
|
|
103
|
-
const req = { auth: { scopes: ['admin'] } } as never
|
|
104
|
-
const res = { status: vi.fn().mockReturnThis(), json: vi.fn(), setHeader: vi.fn() } as never
|
|
105
|
-
const next = vi.fn()
|
|
106
|
-
|
|
107
|
-
middleware(req, res, next)
|
|
108
|
-
expect(next).toHaveBeenCalled()
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it('should grant access for full scope', () => {
|
|
112
|
-
const middleware = requireScope('admin')
|
|
113
|
-
|
|
114
|
-
const req = { auth: { scopes: ['full'] } } as never
|
|
115
|
-
const res = { status: vi.fn().mockReturnThis(), json: vi.fn(), setHeader: vi.fn() } as never
|
|
116
|
-
const next = vi.fn()
|
|
117
|
-
|
|
118
|
-
middleware(req, res, next)
|
|
119
|
-
expect(next).toHaveBeenCalled()
|
|
120
|
-
})
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
// =============================================================================
|
|
124
|
-
// requireAnyScope
|
|
125
|
-
// =============================================================================
|
|
126
|
-
|
|
127
|
-
describe('requireAnyScope', () => {
|
|
128
|
-
it('should call next when any scope matches', () => {
|
|
129
|
-
const middleware = requireAnyScope(['read', 'write'])
|
|
130
|
-
|
|
131
|
-
const req = { auth: { scopes: ['write'] } } as never
|
|
132
|
-
const res = { status: vi.fn().mockReturnThis(), json: vi.fn(), setHeader: vi.fn() } as never
|
|
133
|
-
const next = vi.fn()
|
|
134
|
-
|
|
135
|
-
middleware(req, res, next)
|
|
136
|
-
expect(next).toHaveBeenCalled()
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('should return 403 when no scopes match', () => {
|
|
140
|
-
const middleware = requireAnyScope(['admin'])
|
|
141
|
-
|
|
142
|
-
const req = { auth: { scopes: ['read'] } } as never
|
|
143
|
-
const statusMock = vi.fn().mockReturnThis()
|
|
144
|
-
const res = { status: statusMock, json: vi.fn(), setHeader: vi.fn() } as never
|
|
145
|
-
const next = vi.fn()
|
|
146
|
-
|
|
147
|
-
middleware(req, res, next)
|
|
148
|
-
expect(next).not.toHaveBeenCalled()
|
|
149
|
-
expect(statusMock).toHaveBeenCalledWith(403)
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
it('should return 401 when no auth context', () => {
|
|
153
|
-
const middleware = requireAnyScope(['read'])
|
|
154
|
-
|
|
155
|
-
const req = {} as never
|
|
156
|
-
const statusMock = vi.fn().mockReturnThis()
|
|
157
|
-
const res = { status: statusMock, json: vi.fn() } as never
|
|
158
|
-
const next = vi.fn()
|
|
159
|
-
|
|
160
|
-
middleware(req, res, next)
|
|
161
|
-
expect(statusMock).toHaveBeenCalledWith(401)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('should grant access for full scope', () => {
|
|
165
|
-
const middleware = requireAnyScope(['admin'])
|
|
166
|
-
|
|
167
|
-
const req = { auth: { scopes: ['full'] } } as never
|
|
168
|
-
const res = { status: vi.fn().mockReturnThis(), json: vi.fn(), setHeader: vi.fn() } as never
|
|
169
|
-
const next = vi.fn()
|
|
170
|
-
|
|
171
|
-
middleware(req, res, next)
|
|
172
|
-
expect(next).toHaveBeenCalled()
|
|
173
|
-
})
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
// =============================================================================
|
|
177
|
-
// requireToolScope
|
|
178
|
-
// =============================================================================
|
|
179
|
-
|
|
180
|
-
describe('requireToolScope', () => {
|
|
181
|
-
it('should call next when sufficient scope for tool', () => {
|
|
182
|
-
// create_entry is a core tool → requires read scope
|
|
183
|
-
const middleware = requireToolScope('create_entry')
|
|
184
|
-
|
|
185
|
-
const req = { auth: { scopes: ['read'] } } as never
|
|
186
|
-
const res = { status: vi.fn().mockReturnThis(), json: vi.fn(), setHeader: vi.fn() } as never
|
|
187
|
-
const next = vi.fn()
|
|
188
|
-
|
|
189
|
-
middleware(req, res, next)
|
|
190
|
-
expect(next).toHaveBeenCalled()
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
it('should return 401 when no auth context', () => {
|
|
194
|
-
const middleware = requireToolScope('create_entry')
|
|
195
|
-
|
|
196
|
-
const req = {} as never
|
|
197
|
-
const statusMock = vi.fn().mockReturnThis()
|
|
198
|
-
const res = { status: statusMock, json: vi.fn() } as never
|
|
199
|
-
const next = vi.fn()
|
|
200
|
-
|
|
201
|
-
middleware(req, res, next)
|
|
202
|
-
expect(statusMock).toHaveBeenCalledWith(401)
|
|
203
|
-
})
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
// =============================================================================
|
|
207
|
-
// oauthErrorHandler
|
|
208
|
-
// =============================================================================
|
|
209
|
-
|
|
210
|
-
describe('oauthErrorHandler', () => {
|
|
211
|
-
it('should handle OAuthError', () => {
|
|
212
|
-
const error = new TokenMissingError()
|
|
213
|
-
|
|
214
|
-
const req = {} as never
|
|
215
|
-
const statusMock = vi.fn().mockReturnThis()
|
|
216
|
-
const jsonMock = vi.fn()
|
|
217
|
-
const setHeaderMock = vi.fn()
|
|
218
|
-
const res = { status: statusMock, json: jsonMock, setHeader: setHeaderMock } as never
|
|
219
|
-
const next = vi.fn()
|
|
220
|
-
|
|
221
|
-
oauthErrorHandler(error, req, res, next)
|
|
222
|
-
expect(statusMock).toHaveBeenCalledWith(401)
|
|
223
|
-
expect(next).not.toHaveBeenCalled()
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
it('should pass non-OAuth errors to next', () => {
|
|
227
|
-
const error = new Error('generic')
|
|
228
|
-
|
|
229
|
-
const req = {} as never
|
|
230
|
-
const res = { status: vi.fn().mockReturnThis(), json: vi.fn(), setHeader: vi.fn() } as never
|
|
231
|
-
const next = vi.fn()
|
|
232
|
-
|
|
233
|
-
oauthErrorHandler(error, req, res, next)
|
|
234
|
-
expect(next).toHaveBeenCalledWith(error)
|
|
235
|
-
})
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
// =============================================================================
|
|
239
|
-
// createAuthenticatedContext
|
|
240
|
-
// =============================================================================
|
|
241
|
-
|
|
242
|
-
describe('createAuthenticatedContext', () => {
|
|
243
|
-
const mockValidator = Object.create(TokenValidator.prototype) as TokenValidator
|
|
244
|
-
mockValidator.validate = vi.fn()
|
|
245
|
-
|
|
246
|
-
beforeEach(() => {
|
|
247
|
-
vi.clearAllMocks()
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
it('should return unauthenticated context when no token', async () => {
|
|
251
|
-
const ctx = await createAuthenticatedContext(undefined, mockValidator)
|
|
252
|
-
expect(ctx.authenticated).toBe(false)
|
|
253
|
-
expect(ctx.scopes).toEqual([])
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
it('should return unauthenticated context for invalid token', async () => {
|
|
257
|
-
vi.mocked(mockValidator.validate).mockResolvedValueOnce({
|
|
258
|
-
valid: false,
|
|
259
|
-
error: 'Invalid token',
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
const ctx = await createAuthenticatedContext('Bearer invalid', mockValidator)
|
|
263
|
-
expect(ctx.authenticated).toBe(false)
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
it('should return authenticated context for valid token', async () => {
|
|
267
|
-
vi.mocked(mockValidator.validate).mockResolvedValueOnce({
|
|
268
|
-
valid: true,
|
|
269
|
-
claims: {
|
|
270
|
-
sub: 'user-1',
|
|
271
|
-
scopes: ['read', 'write'],
|
|
272
|
-
exp: 0,
|
|
273
|
-
iat: 0,
|
|
274
|
-
},
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
const ctx = await createAuthenticatedContext('Bearer valid-token', mockValidator)
|
|
278
|
-
expect(ctx.authenticated).toBe(true)
|
|
279
|
-
expect(ctx.scopes).toEqual(['read', 'write'])
|
|
280
|
-
})
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
// =============================================================================
|
|
284
|
-
// validateAuth
|
|
285
|
-
// =============================================================================
|
|
286
|
-
|
|
287
|
-
describe('validateAuth', () => {
|
|
288
|
-
const mockValidator = Object.create(TokenValidator.prototype) as TokenValidator
|
|
289
|
-
mockValidator.validate = vi.fn()
|
|
290
|
-
|
|
291
|
-
beforeEach(() => {
|
|
292
|
-
vi.clearAllMocks()
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
it('should throw TokenMissingError when required and no token', async () => {
|
|
296
|
-
await expect(validateAuth(undefined, mockValidator, { required: true })).rejects.toThrow(
|
|
297
|
-
TokenMissingError
|
|
298
|
-
)
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
it('should return unauthenticated when not required and no token', async () => {
|
|
302
|
-
const ctx = await validateAuth(undefined, mockValidator, { required: false })
|
|
303
|
-
expect(ctx.authenticated).toBe(false)
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
it('should throw InvalidTokenError for invalid token', async () => {
|
|
307
|
-
vi.mocked(mockValidator.validate).mockResolvedValueOnce({
|
|
308
|
-
valid: false,
|
|
309
|
-
error: 'Invalid token',
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
await expect(
|
|
313
|
-
validateAuth('Bearer invalid', mockValidator, { required: true })
|
|
314
|
-
).rejects.toThrow(InvalidTokenError)
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
it('should throw InsufficientScopeError for missing required scope', async () => {
|
|
318
|
-
vi.mocked(mockValidator.validate).mockResolvedValueOnce({
|
|
319
|
-
valid: true,
|
|
320
|
-
claims: {
|
|
321
|
-
sub: 'user-1',
|
|
322
|
-
scopes: ['read'],
|
|
323
|
-
exp: 0,
|
|
324
|
-
iat: 0,
|
|
325
|
-
},
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
await expect(
|
|
329
|
-
validateAuth('Bearer valid', mockValidator, {
|
|
330
|
-
required: true,
|
|
331
|
-
requiredScopes: ['admin'],
|
|
332
|
-
})
|
|
333
|
-
).rejects.toThrow(InsufficientScopeError)
|
|
334
|
-
})
|
|
335
|
-
|
|
336
|
-
it('should return authenticated context for valid token with required scope', async () => {
|
|
337
|
-
vi.mocked(mockValidator.validate).mockResolvedValueOnce({
|
|
338
|
-
valid: true,
|
|
339
|
-
claims: {
|
|
340
|
-
sub: 'user-1',
|
|
341
|
-
scopes: ['read', 'admin'],
|
|
342
|
-
exp: 0,
|
|
343
|
-
iat: 0,
|
|
344
|
-
},
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
const ctx = await validateAuth('Bearer valid', mockValidator, {
|
|
348
|
-
required: true,
|
|
349
|
-
requiredScopes: ['admin'],
|
|
350
|
-
})
|
|
351
|
-
expect(ctx.authenticated).toBe(true)
|
|
352
|
-
expect(ctx.scopes).toContain('admin')
|
|
353
|
-
})
|
|
354
|
-
})
|
|
355
|
-
|
|
356
|
-
// =============================================================================
|
|
357
|
-
// formatOAuthError
|
|
358
|
-
// =============================================================================
|
|
359
|
-
|
|
360
|
-
describe('formatOAuthError', () => {
|
|
361
|
-
it('should format TokenMissingError', () => {
|
|
362
|
-
const result = formatOAuthError(new TokenMissingError())
|
|
363
|
-
expect(result.status).toBe(401)
|
|
364
|
-
expect(result.body).toHaveProperty('error', 'invalid_token')
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
it('should format InvalidTokenError', () => {
|
|
368
|
-
const result = formatOAuthError(new InvalidTokenError('Token is bad'))
|
|
369
|
-
expect(result.status).toBe(401)
|
|
370
|
-
expect(result.body).toHaveProperty('error', 'invalid_token')
|
|
371
|
-
})
|
|
372
|
-
|
|
373
|
-
it('should format InsufficientScopeError', () => {
|
|
374
|
-
const result = formatOAuthError(new InsufficientScopeError('admin'))
|
|
375
|
-
expect(result.status).toBe(403)
|
|
376
|
-
expect(result.body).toHaveProperty('error', 'insufficient_scope')
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
it('should format generic errors as 500', () => {
|
|
380
|
-
const result = formatOAuthError(new Error('Unknown'))
|
|
381
|
-
expect(result.status).toBe(500)
|
|
382
|
-
expect(result.body).toHaveProperty('error', 'server_error')
|
|
383
|
-
})
|
|
384
|
-
})
|
|
385
|
-
|
|
386
|
-
// =============================================================================
|
|
387
|
-
// createAuthMiddleware
|
|
388
|
-
// =============================================================================
|
|
389
|
-
|
|
390
|
-
describe('createAuthMiddleware', () => {
|
|
391
|
-
function makeReqResNext(overrides: Record<string, unknown> = {}) {
|
|
392
|
-
const req = {
|
|
393
|
-
path: '/mcp',
|
|
394
|
-
headers: {} as Record<string, string>,
|
|
395
|
-
...overrides,
|
|
396
|
-
} as never
|
|
397
|
-
const statusMock = vi.fn().mockReturnThis()
|
|
398
|
-
const jsonMock = vi.fn()
|
|
399
|
-
const setHeaderMock = vi.fn()
|
|
400
|
-
const res = { status: statusMock, json: jsonMock, setHeader: setHeaderMock } as never
|
|
401
|
-
const next = vi.fn()
|
|
402
|
-
return { req, res, next, statusMock, jsonMock, setHeaderMock }
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const mockValidator = Object.create(TokenValidator.prototype) as TokenValidator
|
|
406
|
-
mockValidator.validate = vi.fn()
|
|
407
|
-
|
|
408
|
-
const mockResourceServer = {
|
|
409
|
-
getResourceUri: () => 'https://mcp.example.com',
|
|
410
|
-
getWWWAuthenticateHeader: (error?: string, description?: string) =>
|
|
411
|
-
`Bearer realm="mcp", error="${error ?? ''}", error_description="${description ?? ''}"`,
|
|
412
|
-
} as never
|
|
413
|
-
|
|
414
|
-
beforeEach(() => {
|
|
415
|
-
vi.clearAllMocks()
|
|
416
|
-
})
|
|
417
|
-
|
|
418
|
-
it('should bypass auth for .well-known paths', async () => {
|
|
419
|
-
const middleware = createAuthMiddleware({
|
|
420
|
-
tokenValidator: mockValidator,
|
|
421
|
-
resourceServer: mockResourceServer,
|
|
422
|
-
})
|
|
423
|
-
const { req, res, next } = makeReqResNext({
|
|
424
|
-
path: '/.well-known/oauth-authorization-server',
|
|
425
|
-
})
|
|
426
|
-
|
|
427
|
-
await middleware(req, res, next)
|
|
428
|
-
expect(next).toHaveBeenCalled()
|
|
429
|
-
})
|
|
430
|
-
|
|
431
|
-
it('should bypass auth for exact public paths', async () => {
|
|
432
|
-
const middleware = createAuthMiddleware({
|
|
433
|
-
tokenValidator: mockValidator,
|
|
434
|
-
resourceServer: mockResourceServer,
|
|
435
|
-
publicPaths: ['/health'],
|
|
436
|
-
})
|
|
437
|
-
const { req, res, next } = makeReqResNext({ path: '/health' })
|
|
438
|
-
|
|
439
|
-
await middleware(req, res, next)
|
|
440
|
-
expect(next).toHaveBeenCalled()
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
it('should bypass auth for wildcard public paths', async () => {
|
|
444
|
-
const middleware = createAuthMiddleware({
|
|
445
|
-
tokenValidator: mockValidator,
|
|
446
|
-
resourceServer: mockResourceServer,
|
|
447
|
-
publicPaths: ['/api/*'],
|
|
448
|
-
})
|
|
449
|
-
const { req, res, next } = makeReqResNext({ path: '/api/users' })
|
|
450
|
-
|
|
451
|
-
await middleware(req, res, next)
|
|
452
|
-
expect(next).toHaveBeenCalled()
|
|
453
|
-
})
|
|
454
|
-
|
|
455
|
-
it('should bypass auth for wildcard prefix exact match', async () => {
|
|
456
|
-
const middleware = createAuthMiddleware({
|
|
457
|
-
tokenValidator: mockValidator,
|
|
458
|
-
resourceServer: mockResourceServer,
|
|
459
|
-
publicPaths: ['/api/*'],
|
|
460
|
-
})
|
|
461
|
-
const { req, res, next } = makeReqResNext({ path: '/api' })
|
|
462
|
-
|
|
463
|
-
await middleware(req, res, next)
|
|
464
|
-
expect(next).toHaveBeenCalled()
|
|
465
|
-
})
|
|
466
|
-
|
|
467
|
-
it('should return 401 when no token provided', async () => {
|
|
468
|
-
const middleware = createAuthMiddleware({
|
|
469
|
-
tokenValidator: mockValidator,
|
|
470
|
-
resourceServer: mockResourceServer,
|
|
471
|
-
})
|
|
472
|
-
const { req, res, next, statusMock, jsonMock } = makeReqResNext({
|
|
473
|
-
path: '/mcp',
|
|
474
|
-
headers: {},
|
|
475
|
-
})
|
|
476
|
-
|
|
477
|
-
await middleware(req, res, next)
|
|
478
|
-
expect(next).not.toHaveBeenCalled()
|
|
479
|
-
expect(statusMock).toHaveBeenCalledWith(401)
|
|
480
|
-
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ error: 'unauthorized' }))
|
|
481
|
-
})
|
|
482
|
-
|
|
483
|
-
it('should return 401 when token is invalid', async () => {
|
|
484
|
-
vi.mocked(mockValidator.validate).mockResolvedValueOnce({
|
|
485
|
-
valid: false,
|
|
486
|
-
error: 'Token expired',
|
|
487
|
-
})
|
|
488
|
-
|
|
489
|
-
const middleware = createAuthMiddleware({
|
|
490
|
-
tokenValidator: mockValidator,
|
|
491
|
-
resourceServer: mockResourceServer,
|
|
492
|
-
})
|
|
493
|
-
const { req, res, next, statusMock, jsonMock } = makeReqResNext({
|
|
494
|
-
path: '/mcp',
|
|
495
|
-
headers: { authorization: 'Bearer bad-token' },
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
await middleware(req, res, next)
|
|
499
|
-
expect(next).not.toHaveBeenCalled()
|
|
500
|
-
expect(statusMock).toHaveBeenCalledWith(401)
|
|
501
|
-
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ error: 'invalid_token' }))
|
|
502
|
-
})
|
|
503
|
-
|
|
504
|
-
it('should attach auth context and call next() for valid token', async () => {
|
|
505
|
-
vi.mocked(mockValidator.validate).mockResolvedValueOnce({
|
|
506
|
-
valid: true,
|
|
507
|
-
claims: { sub: 'user-42', scopes: ['read', 'write'], exp: 0, iat: 0 },
|
|
508
|
-
})
|
|
509
|
-
|
|
510
|
-
const middleware = createAuthMiddleware({
|
|
511
|
-
tokenValidator: mockValidator,
|
|
512
|
-
resourceServer: mockResourceServer,
|
|
513
|
-
})
|
|
514
|
-
|
|
515
|
-
const rawReq = {
|
|
516
|
-
path: '/mcp',
|
|
517
|
-
headers: { authorization: 'Bearer good-token' },
|
|
518
|
-
} as unknown as Record<string, unknown>
|
|
519
|
-
const statusMock = vi.fn().mockReturnThis()
|
|
520
|
-
const res = { status: statusMock, json: vi.fn(), setHeader: vi.fn() } as never
|
|
521
|
-
const next = vi.fn()
|
|
522
|
-
|
|
523
|
-
await middleware(rawReq as never, res, next)
|
|
524
|
-
expect(next).toHaveBeenCalled()
|
|
525
|
-
expect(rawReq['auth']).toBeDefined()
|
|
526
|
-
expect((rawReq['auth'] as { sub: string }).sub).toBe('user-42')
|
|
527
|
-
})
|
|
528
|
-
|
|
529
|
-
it('should return 500 when valid=true but claims is undefined', async () => {
|
|
530
|
-
vi.mocked(mockValidator.validate).mockResolvedValueOnce({
|
|
531
|
-
valid: true,
|
|
532
|
-
claims: undefined,
|
|
533
|
-
} as never)
|
|
534
|
-
|
|
535
|
-
const middleware = createAuthMiddleware({
|
|
536
|
-
tokenValidator: mockValidator,
|
|
537
|
-
resourceServer: mockResourceServer,
|
|
538
|
-
})
|
|
539
|
-
const { req, res, next, statusMock, jsonMock } = makeReqResNext({
|
|
540
|
-
path: '/mcp',
|
|
541
|
-
headers: { authorization: 'Bearer weird-token' },
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
await middleware(req, res, next)
|
|
545
|
-
expect(next).not.toHaveBeenCalled()
|
|
546
|
-
expect(statusMock).toHaveBeenCalledWith(500)
|
|
547
|
-
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ error: 'internal_error' }))
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
it('should not bypass non-matching paths', async () => {
|
|
551
|
-
const middleware = createAuthMiddleware({
|
|
552
|
-
tokenValidator: mockValidator,
|
|
553
|
-
resourceServer: mockResourceServer,
|
|
554
|
-
publicPaths: ['/health', '/api/*'],
|
|
555
|
-
})
|
|
556
|
-
const { req, res, next, statusMock } = makeReqResNext({
|
|
557
|
-
path: '/secret',
|
|
558
|
-
headers: {},
|
|
559
|
-
})
|
|
560
|
-
|
|
561
|
-
await middleware(req, res, next)
|
|
562
|
-
expect(next).not.toHaveBeenCalled()
|
|
563
|
-
expect(statusMock).toHaveBeenCalledWith(401)
|
|
564
|
-
})
|
|
565
|
-
})
|
|
566
|
-
|
|
567
|
-
// =============================================================================
|
|
568
|
-
// requireToolScope — scope-denied branch
|
|
569
|
-
// =============================================================================
|
|
570
|
-
|
|
571
|
-
describe('requireToolScope - scope denied', () => {
|
|
572
|
-
it('should return 403 when user lacks required scope for tool', () => {
|
|
573
|
-
// backup_journal requires admin scope
|
|
574
|
-
const middleware = requireToolScope('backup_journal')
|
|
575
|
-
|
|
576
|
-
const req = { auth: { scopes: ['read'] } } as never
|
|
577
|
-
const statusMock = vi.fn().mockReturnThis()
|
|
578
|
-
const res = { status: statusMock, json: vi.fn(), setHeader: vi.fn() } as never
|
|
579
|
-
const next = vi.fn()
|
|
580
|
-
|
|
581
|
-
middleware(req, res, next)
|
|
582
|
-
expect(next).not.toHaveBeenCalled()
|
|
583
|
-
expect(statusMock).toHaveBeenCalledWith(403)
|
|
584
|
-
})
|
|
585
|
-
})
|