@dinko/adonis-mcp
adonis-oauth-mcp
Монорепозиторий для двух пакетов AdonisJS. Они версионируются и выпускаются вместе, поэтому изменения в контракте регистрации ресурсов никогда не требуют координации между репозиториями.
Пакет | Владение |
| Сервер авторизации OAuth 2.1: токен / одобрение / отказ, проверка redirect-URI, хранение кодов авторизации, метаданные сервера авторизации и универсальная конечная точка метаданных защищённого ресурса, управляемая реестром ресурсов. Ничего не знает о MCP. |
| MCP-сервер: обработчик запросов, контроллер, контракт инструментов, промежуточное ПО аутентификации. Регистрирует себя как защищённый ресурс OAuth, объявляя свой URL ресурса, области, клиентов, |
Зависимость работает в одну сторону: mcp → oauth. Ничто в oauth не может импортировать из mcp.
Структура
Каждый пакет следует соглашению о пакетах AdonisJS:
index.ts re-exports `configure` and `stubsRoot` (what `node ace configure` imports)
configure.ts the configure hook, driving codemods and stubs
stubs/ .stub templates rendered into the target app
src/ runtime code the app imports
providers/ service providers registered by the configure hook
services/ container services, for code that cannot use dependency injectionRelated MCP server: OAuth MCP Server
Разработка
npm install # links the workspaces
npm run build # tsc + copy stubs, per package
npm run typecheck
npm test # runs against build/, so build firstПри разработке устанавливайте в приложение из этого репозитория (npm link, file: или git-зависимость), а не из реестра.
@dinko/adonis-oauth
Сервер авторизации OAuth 2.1 с PKCE для приложений AdonisJS, которым необходимо выдавать токены доступа сторонним клиентам.
Пакет владеет протоколом. Приложение владеет тремя вещами, которые оно не может делегировать: экран согласия, какой токен выдавать и маршруты.
npm i @dinko/adonis-oauth
node ace configure @dinko/adonis-oauthНастройка генерирует три файла и никогда не перезаписывает существующий:
Файл | Что с ним делать |
| Объявите свои ресурсы, их клиентов и |
| Настройте столбец |
| Ваш отсюда: делегирует пакету и является местом, где вы добавляете всё, что он не покрывает. |
Маршруты
Не регистрируются автоматически — где они находятся и какое промежуточное ПО их защищает, решаете вы. Добавьте их в start/routes.ts:
router.get('.well-known/oauth-authorization-server', [OauthController, 'getAuthorizationServer'])
router.get('.well-known/oauth-protected-resource/:resource', [OauthController, 'getProtectedResource'])
router
.group(() => {
router.post('token', [OauthController, 'token'])
router
.group(() => {
router.post('authorize/approve', [OauthController, 'approveAuthorization'])
router.post('authorize/deny', [OauthController, 'denyAuthorization'])
})
.use(middleware.auth())
})
.prefix('oauth')Одобрение и отказ должны быть аутентифицированы: код авторизации привязан к пользователю, предоставляющему доступ. Конечная точка токена является публичной по спецификации, и именно там погашаются коды авторизации, поэтому это хорошее место для ограничения частоты запросов.
Возврат перенаправления
Одобрение и отказ по умолчанию отвечают 200 { redirect_to }, и экран согласия сам выполняет навигацию:
window.location.assign(response.redirect_to)Это то, что нужно экрану, отправляющему своё решение через fetch или axios. XHR следует за 302, повторно отправляя запрос, поэтому страница никогда не перенаправляется: пользователь остаётся на экране согласия, а запрос попадает на обратный вызов клиента с другого источника и не проходит CORS.
Установите redirectMode: 'http', когда экран согласия — это простая HTML-форма. В этом случае браузер выполняет навигацию по документу, поэтому он следует за 302 нативно, и пользователь попадает на клиента.
Выдача токенов
Тип токена зависит от запрашиваемого ресурса, поэтому это решение живёт в каждом ресурсе, а не в контроллере. После того как пакет проверил запрос, использовал код авторизации и проверил верификатор PKCE, он вызывает:
issueToken: async ({ userId, scopes, client, resource, ctx }) => {
const user = await User.find(userId)
if (!user) return null // rejects the exchange with invalid_grant
const expiresIn = 30 * 24 * 60 * 60
const token = await User.accessTokens.create(user, scopes, {
name: `oauth:${client.id}`,
expiresIn,
})
return { accessToken: token.value!.release(), expiresIn }
}userId — это то, что было сохранено вместе с кодом авторизации: пакет не знает о вашей модели пользователя и никогда не загружает её.
Экран согласия
Страница GET /oauth/authorize — ваша: Edge, Inertia или отдельный фронтенд. Пакет только проверяет запрос за ней:
const validation = server.validateAuthorizationRequest(request.qs())
if (!validation.valid) {
return view.render('oauth/authorize', { error: validation.error })
}
return view.render('oauth/authorize', {
client: validation.client,
requestedScopes: validation.scopes,
authorizationFields: validation.fields, // post these back to approve
})Необязательно: приложения, отображающие экран в другом месте, могут пропустить его, поскольку одобрение и отказ снова проверяют запрос самостоятельно.
Конфигурация
export default defineConfig({
issuer: env.get('APP_URL'),
authorizationEndpoint: `${env.get('APP_URL')}/oauth/authorize`,
tokenEndpoint: `${env.get('APP_URL')}/oauth/token`,
// optional
redirectMode: 'json', // or 'http'
tokenEndpointAuthMethods: ['none'],
authorizationCodeTtlSeconds: 10 * 60,
authorizationCodesTable: 'oauth_authorization_codes',
authenticatedUserId: (ctx) => ctx.auth.user?.id, // defaults to this
resources: [mcpResource],
})Каждый ресурс объявляет:
Поле | |
| Слаг, используемый в |
| Канонический индикатор ресурса, который клиенты отправляют в параметре |
| Человекочитаемое имя, объявляемое через обнаружение. |
| Все области, которые понимает ресурс. |
|
|
| Выпускает токен доступа. |
Loopback redirect URIs (http://localhost/callback) совпадают на любом порту согласно RFC 8252, а redirectUriPatterns допускает один сегмент :param для клиентов, чей обратный вызов содержит идентификатор.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
MCP server for verifying EUDI/Talao wallet data via OIDC4VP (pull) for AI agents.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA self-hostable OAuth 2.0 server designed for the Model-Context-Protocol (MCP) that enables you to secure your MCP applications with a robust implementation you control.3,607112ISC
- FlicenseNot gradedqualityDmaintenanceA complete OAuth 2.1 server implementation for FastMCP with PKCE support, enabling secure authentication and authorization flows. Provides authorization code exchange, token management, and refresh capabilities for building authenticated MCP applications.
- AlicenseNot gradedqualityDmaintenanceDrop-in OAuth 2.1 + Dynamic Client Registration for MCP servers, providing authentication middleware and token verification.20MIT
- AlicenseNot gradedqualityCmaintenanceImplements an MCP server with OAuth 2.1 Protected Resource Metadata, enabling token-based authentication for MCP tools like ping.MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Dyoma3/adonis-oauth-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server