Commit a952852
Support gateway-owned auth resolution (#17608)
## Summary
- Add an interface-first model gateway shape with optional gateway-owned
`resolveAuth` hooks while preserving the existing `MastraModelGateway`
base class path.
- Route model auth through gateway `resolveAuth` before falling back to
legacy `getApiKey` behavior.
- Move MastraCode's gateway-routed OAuth/provider model construction
behind a custom gateway implementation.
- Add focused tests, minimal docs updates, and a changeset for
`@mastra/core` and `mastracode`.
## Test plan
- `pnpm build:core`
- `pnpm --filter ./packages/core check`
- `pnpm --filter ./packages/core test:unit
src/llm/model/model-auth-resolver.test.ts
src/llm/model/gateways/custom-gateway.test.ts`
- `pnpm --filter ./mastracode check`
- `pnpm --filter ./mastracode test src/agents/__tests__/model.test.ts`
- `pnpm --dir docs exec remark
src/content/en/models/gateways/custom-gateways.mdx
src/content/en/reference/core/mastra-model-gateway.mdx`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
Imagine you have different "gates" that route API requests to different
services. Previously, each gate just asked for an API key using the same
method. This PR lets each gate have its own "security desk" to handle
authentication first—checking if it knows how to get the API key
itself—before falling back to the standard process. This makes the
system more flexible and lets gates handle authentication however they
want.
## Summary
This PR introduces **gateway-owned authentication resolution** for
Mastra's model routing system, shifting from a single auth-resolution
path to a three-tier flow: explicit credentials → gateway-specific
`resolveAuth` hook → legacy `getApiKey` fallback.
### Core Feature: Gateway-Owned Auth Resolution
**New Interface-Based Gateway Contract**
- Introduces `MastraModelGatewayInterface` to support both class-based
and plain object gateways
- Adds optional `resolveAuth()` hook that runs before legacy `getApiKey`
during model authentication
- `MastraModelGateway` abstract class now implements the interface,
maintaining backward compatibility
- Adds `GatewayAuthRequest` (with `gatewayId`, `providerId`, `modelId`,
`routerId`) and `GatewayAuthResult` (returning `apiKey`, `bearerToken`,
`headers`, and `source`)
**Three-Tier Auth Resolution in `ModelRouterLanguageModel`**
- Centralizes auth logic into `resolveAuth()` method that threads
`GatewayAuthResult` through request handling
- First checks explicit credentials (passed directly); second checks
`gateway.resolveAuth()`; falls back to `gateway.getApiKey()` if both are
unavailable
- Auth results include a `source` field tracking the resolution path
(`'explicit'`, `'gateway'`, or `'legacy'`)
- Deprecated standalone `resolveModelAuth` helper (though exported in
new `model-auth-resolver.ts` module)
### MastraCode Gateway Refactoring
**New `MastraCode Gateway` Abstraction**
- Introduces `createMastraCodeGateway()` that implements
`MastraModelGatewayInterface` for provider-specific auth
- Exports `resolveAuth()` function that retrieves effective API keys
using `AuthStorage` and optional memory gateway credentials
- Encapsulates provider selection logic (OpenAI, Anthropic, GitHub
Copilot, Moonshot) with per-provider API key resolution
- Supports custom OpenAI-compatible providers and OAuth/API-key fallback
chains
- Simplifies `resolveModel` by delegating provider routing to the
gateway's `resolveLanguageModel` method
### Gateway Infrastructure Updates
**Utility Functions**
- `getGatewayId(gateway)`: Extracts `id` from gateway (via optional
`getId()` method or direct `id` property)
- `shouldEnableGateway(gateway)`: Checks if gateway is enabled (via
optional `shouldEnable()` method, defaults to `true`)
- `serializeGatewayForSpan(gateway)`: Serializes gateway for tracing
**Gateway Deduplication & Matching**
- `findGatewayForModel()` and related registry functions now use
`getGatewayId()` for consistent gateway identification
- Fixes `defaultGateways` deduplication to use gateway IDs instead of
registry keys
- Extends gateway support in `Mastra` to accept plain object gateways
via `addGateway()` and `listGateways()`
### Documentation & Types
- Updated `custom-gateways.mdx` to recommend
`MastraModelGatewayInterface` implementation pattern with plain object
examples
- Documented optional `resolveAuth()` hook with example showing `{
apiKey, source: 'gateway' }` return
- Updated `MastraModelGateway` reference to label it as "Base class and
interface"
- Added TypeScript autocomplete guidance and rewritten "Best practices"
section
- Expanded public API re-exports to include `GatewayAuthRequest`,
`GatewayAuthResult`, and gateway utility functions
### Tests & Validation
- **Custom gateway tests**: New `custom-gateway.test.ts` verifies plain
object gateways and `resolveAuth` precedence
- **Model auth resolver tests**: `model-auth-resolver.test.ts` validates
three-tier precedence (explicit → gateway → legacy)
- **MastraCode tests**: Updated `model.test.ts` with mocks for
`ModelRouterLanguageModel`, `resolveAuth`, and `resolveLanguageModel` to
validate gateway delegation
- **Regression fix**: `_llm-recorder` vite plugin regex anchored to path
boundaries to fix directory suffix matching
### Breaking Changes / API Shifts
- Gateway type parameters across `ModelRouterLanguageModel`,
`GatewayRegistry`, `Mastra` config shifted from `MastraModelGateway`
(class) to `MastraModelGatewayInterface` (interface)
- Gateways now expected to implement optional `resolveAuth()` for
authentication ownership; if absent, system falls back to legacy
`getApiKey()`
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>1 parent 053735a commit a952852
18 files changed
Lines changed: 1088 additions & 361 deletions
File tree
- .changeset
- docs/src/content/en
- models/gateways
- reference/core
- mastracode/src/agents
- __tests__
- packages
- _llm-recorder/src
- core/src
- llm
- model
- gateways
- mastra
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
28 | | - | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
29 | 65 | | |
30 | 66 | | |
31 | 67 | | |
| |||
48 | 84 | | |
49 | 85 | | |
50 | 86 | | |
51 | | - | |
| 87 | + | |
52 | 88 | | |
53 | 89 | | |
54 | 90 | | |
| |||
102 | 138 | | |
103 | 139 | | |
104 | 140 | | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
105 | 155 | | |
106 | 156 | | |
107 | 157 | | |
| |||
145 | 195 | | |
146 | 196 | | |
147 | 197 | | |
148 | | - | |
| 198 | + | |
149 | 199 | | |
150 | 200 | | |
151 | 201 | | |
| |||
155 | 205 | | |
156 | 206 | | |
157 | 207 | | |
158 | | - | |
| 208 | + | |
159 | 209 | | |
160 | | - | |
| 210 | + | |
161 | 211 | | |
162 | 212 | | |
163 | 213 | | |
164 | 214 | | |
165 | 215 | | |
166 | 216 | | |
167 | | - | |
| 217 | + | |
168 | 218 | | |
169 | 219 | | |
170 | 220 | | |
| |||
173 | 223 | | |
174 | 224 | | |
175 | 225 | | |
176 | | - | |
| 226 | + | |
177 | 227 | | |
178 | 228 | | |
179 | 229 | | |
180 | 230 | | |
181 | 231 | | |
182 | | - | |
| 232 | + | |
183 | 233 | | |
184 | 234 | | |
185 | 235 | | |
186 | 236 | | |
187 | 237 | | |
188 | 238 | | |
189 | | - | |
| 239 | + | |
190 | 240 | | |
191 | 241 | | |
192 | 242 | | |
| |||
517 | 567 | | |
518 | 568 | | |
519 | 569 | | |
520 | | - | |
| 570 | + | |
521 | 571 | | |
522 | | - | |
| 572 | + | |
523 | 573 | | |
524 | | - | |
| 574 | + | |
525 | 575 | | |
526 | | - | |
| 576 | + | |
527 | 577 | | |
528 | | - | |
| 578 | + | |
529 | 579 | | |
530 | | - | |
| 580 | + | |
531 | 581 | | |
532 | | - | |
| 582 | + | |
533 | 583 | | |
534 | 584 | | |
535 | 585 | | |
| |||
Lines changed: 25 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
| |||
24 | 26 | | |
25 | 27 | | |
26 | 28 | | |
27 | | - | |
| 29 | + | |
28 | 30 | | |
29 | 31 | | |
30 | 32 | | |
| |||
175 | 177 | | |
176 | 178 | | |
177 | 179 | | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
178 | 201 | | |
179 | 202 | | |
180 | 203 | | |
| |||
0 commit comments