FoxAuth
dev.foxauth由 foxauth.dev 發布官方發布者v1.5.0更新於 Nov 5, 2026
Administer a FoxAuth OAuth 2.1 / OpenID Connect server: projects, apps, users, keys and settings.
概覽
透過 MCP 管理 FoxAuth OAuth 2.1 / OpenID Connect 伺服器:專案、應用程式、使用者、簽章金鑰與設定。
- 可以做什麼
- 管理專案、團隊、OAuth 用戶端與 API、管理員、終端使用者及其群組
- 管理上游身分提供者、SCIM 佈建連線、SMTP 與伺服器設定
- 無需重新啟動即可產生、提升與停用簽章金鑰
- 讀取僅附加的稽核軌跡與錯誤存放區
- 以 OAuth 2.1 受保護資源的形式使用與管理主控台相同的管理 API
- 適用情境
- 適合讓助理直接操作 FoxAuth 身分部署的情境,例如建立或調整 OAuth 用戶端、管理使用者與群組、輪換簽章金鑰或修改設定,而不必在主控台中手動完成。
- 執行需求
- 遠端 streamable HTTP 端點 auth.foxauth.dev,需透過 OAuth 驗證(必要)。伺服器的 MCP 控制面預設關閉,須在主控台的「設定 → 管理」中啟用。助理以授權它的管理員權限行事。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 FoxAuth,將其新增到工作區。
- 連接 所需的 憑證。它們會被私密儲存,絕不會在公開頁面上展示。
- 為需要使用其工具的對話啟用該服務。
Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。
交給代理程式安裝
把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。
閱讀 https://sourceweft.com/mcp/dev.foxauth%2Fadmin/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。其他 MCP 客戶端
把它新增到你客戶端的 mcpServers 設定中。 客戶端會在首次連接時完成 OAuth 登入。
{
"mcpServers": {
"foxauth": {
"type": "http",
"url": "https://auth.foxauth.dev/mcp"
}
}
}README
OAuth-server.ts
Source-available OAuth 2.0 and OpenID Connect authorization server — built with Bun and TypeScript.
[CI status] [Coverage status] [Security scanning status] [OpenSSF Scorecard]
Website · Features · Docs · Compare · OAuth 2.0 · OpenID Connect · Bun · FSL-1.1-ALv2 License
OAuth-server.ts is a source-available (FSL-1.1-ALv2), standards-compliant authorization server written in TypeScript and powered by Bun and Elysia. It implements OAuth 2.0 and OpenID Connect from the ground up, giving you complete control over your identity infrastructure — with no vendor lock-in.
Features
Most protocol capabilities are governed by a named feature flag, and a flag that is off means the capability's endpoint is not served at all. So this list is split by what you get on a default install versus what you switch on deliberately — the distinction is load-bearing, not editorial.
Out of the box
- Authorization Code flow with PKCE — PKCE is mandatory for every client, public or confidential, per OAuth 2.1
- OpenID Connect — OIDC Core 1.0 with ID tokens, UserInfo endpoint, and discovery
- Authorization-response issuer identification —
isson every authorization response (RFC 9207) - Resource Indicators — audience-restricted tokens via
resource(RFC 8707) - Refresh Token grant — with rotation and reuse detection; offered while
offline_accessis among the supportedscopes, which it is by default - RP-initiated logout — by GET or form POST, with a confirmation step
- JWT tokens — access and ID tokens signed with the algorithms your key store holds;
bun run db:setupprovisions an initial RS256 key, and ES256 and EdDSA keys are supported. Keys live in the database, are generated on first run, and are published at/jwks— and each project with an address of its own publishes its own at its ownjwks_uri - Database-backed clients — clients live in this server's own store and are created through the admin API, dynamic registration, or
bun run db:setup. There is no static client configuration file - Administration console — projects, teams, OAuth clients and APIs, administrators, end-users and their groups, upstream identity providers, SCIM provisioning connections, settings, SMTP, signing keys and an append-only audit trail
- End-user self-service — email verification and password reset, each with per-identity attempt and cooldown caps
- TOTP second factor — per-project, optionally enforced for the admin console (RFC 6238)
- Pairwise subject identifiers — per-sector
subvalues, salted from the database - Per-origin rate limiting — tiered by route class, on by default
- Sign-in brute-force throttle — persisted per-address failure counters with escalating lockouts, and no way to tell a throttled refusal from a wrong password
- Security headers — HSTS,
Permissions-Policy, framing and content-type protections - CORS closed by data — an origin is readable only if it is listed on the project owning the calling client
- Pluggable storage — adapter architecture with MongoDB, PostgreSQL and in-memory implementations
- Consent & login UI — built-in authentication and consent screens (React + Ant Design)
- Scope-based access control — fine-grained, per-client scope enforcement
- Extensible — 31 named behaviour seams (account lookup, interaction policy, refresh-token rotation, resource resolution, pairwise identifiers, RAR handling and more) replaced at call time via
addons.override()
Opt-in capabilities
Implemented, and off until the flag is set. Set them in the console under Settings or on
ApplicationConfig; a change saved in the console applies at once.
Quick Start
With Docker Compose
Prerequisites: Docker with Compose v2.
That starts MongoDB, provisions the schema and the admin panel (an idempotent one-shot step), and
serves the server at http://localhost:3000. Open http://localhost:3000/admin — the first visit
shows a one-time setup screen that creates the initial super administrator. Discovery is at
http://localhost:3000/.well-known/openid-configuration. Set ISSUER in docker-compose.yml to its
final public URL before this first docker compose up: the seeded admin client's redirect URI is
derived from it once, and a later change is not repaired by re-running setup.
Images are published to ghcr.io/redfox-soft/oauth-server-ts on every release, tagged with the
version and latest. To upgrade, pull and run docker compose up again; the setup step re-runs and
is safe to repeat.
From source
Prerequisites: Bun v1.3+ and a running MongoDB or PostgreSQL instance.
Schema provisioning creates the initial signing key, so no key configuration is required. If the server ever starts against an empty key store it also generates and persists one automatically.
Admin panel provisioning
bun run db:setup also provisions the admin panel: the reserved "Administrators" project and the
first-party admin-panel OAuth client. The seed is idempotent — re-run bun run db:setup after
upgrading an existing install so the admin client and project exist.
On first visit to /admin, a one-time setup screen creates the first administrator and makes them a
member of Super administrators; the setup route is closed once that team has an active member. The admin panel requires a database-backed deployment, MongoDB or PostgreSQL
(the in-memory adapter does not persist seeded data across restarts).
Configuration
Keyring
Secrets the server must read back (application secrets, sign-in provider credentials, the SMTP password,
private signing keys, authenticator secrets) are stored encrypted with the instance keyring. Supply it as
KEYRING or a keyring file; without one, first-run setup generates it, writing it to keyring/ only when
that directory exists, and shows it once. Keep it in a secrets manager and back it up apart from the
database: a backup cannot be read without it. bun run keyring add and bun run keyring write <kid> rotate
it in two steps.
Signing keys
Signing keys are stored in the database, not in an environment variable — every issuer's in the
bucketKeys area, the instance's included. The initial RS256 signing key is created when you
provision the schema (bun run db:setup); the server also generates and persists one automatically
if it starts against an empty store. Rotation needs no restart: generate a key (it is published
first), promote it to sign once every instance serves it, and retire the old one, which keeps
verifying for a day — in the console, the admin API, or over MCP (jwks_generate, jwks_promote,
jwks_retire). One key signs per algorithm, so RS256 and PS256 can sign side by side.
Rate limiting
Requests are counted per calling origin and refused with 429 once an origin exceeds its allowance
inside a window. The refusal happens before the endpoint does any work, and carries Retry-After
with the seconds remaining; the body is the standard OAuth error shape, the admin plane's own shape,
or an HTML page, depending on which surface was addressed. No RateLimit-* headers are emitted —
they would tell a caller probing for the threshold exactly where it is.
Allowances are tiered by route class rather than being one blanket number, because a limit tight enough to protect the token endpoint would refuse the console's asset burst:
All of it is settable from the admin console under Settings → Rate limiting, or directly on
ApplicationConfig. A change saved in the console applies at once, like every other server setting.
The server refuses to start if any of these is not a positive integer, rather than serving with limiting silently absent or silently total.
Who a request is counted against is decided once, by clientAddress.source: a proxy header is believed only
on a connection from a proxy the choice trusts, because a caller can write any header. automatic (the default)
fits a server reached directly and one behind nginx, an Ingress or a load balancer on a private network, and reads
X-Forwarded-For from the right; fly reads Fly's own Fly-Client-IP (the shipped fly.toml needs it);
cloudflare and listed add Cloudflare's ranges or your own proxy addresses; direct believes no header. The
console card under the setting shows which address the server sees for you. See
Behind a reverse proxy.
Two properties to know before relying on this. Counting is per instance and held in memory, so with N machines serving concurrently the effective allowance is N times the configured value, and an instance restart clears every counter. This is a resource protection — it bounds what one source can cost the server — and not a security boundary. The limits that must hold absolutely are the per-identity throttles: verification codes cap at five attempts, resends and password-reset requests are cooldown- and daily-capped, and the password sign-in door has the brute-force throttle below — all independently of anything here.
Tuning. Raise strict.max if a legitimate server-to-server integration behind one address, or
many users behind one corporate NAT, start seeing refusals. Raise public.max if the admin console
stutters while loading. Lower maxTrackedOrigins under memory pressure. Set rateLimit.enabled to
false to switch the whole thing off during an incident; it applies on save.
Sign-in brute-force throttle
Failed password attempts are counted per project and address. Once an address reaches the cap, the sign-in door is shut for that address and refuses every further attempt — including one carrying the correct password — until the window ends. Each further exhaustion shuts it for longer, doubling to the ceiling: 15 → 30 → 60 minutes by default, which holds a sustained attack to roughly 120 guesses a day. The counter is forgotten after 24 hours without a failure.
The refusal is the ordinary "invalid username or password" page. It never says that a throttle exists, how many attempts remain, or when the door reopens, because saying any of it would tell an attacker that the address they are guessing is real. For the same reason attempts are counted for addresses that have no account at all — a counter's existence means somebody typed that address, nothing more. And because the refusal happens before the account lookup, a refused attempt costs no password hashing: a flood against one address stops being a CPU cost as well as stopping being a guessing opportunity.
Unlike the per-origin limiter above, these counters are persisted, so the limit holds across restarts and across every machine serving concurrently. That is what makes this a security boundary rather than a resource protection — and why there is deliberately no switch to turn it off.
Two counters are cleared immediately rather than waiting out the window: a password that verifies, and a completed password reset. The second is the escape hatch — consuming the emailed secret proves control of the address, which is exactly what an attacker guessing passwords does not have — so a locked-out user is never stuck waiting. Merely requesting a reset clears nothing.
A project that requires a one-time code stays at the first window however often it is tripped: a guessed password there does not sign anyone in, so the deeper lockouts buy little. This matters most for the reserved Administrators project, whose operators sign in through this same door and have no self-service reset to escape with — their worst case is a 15-minute wait, and the way to make that door properly hard is to require the second factor on it (Admins → Require an authenticator app).
Docker
Published image:
Run bun run db:setup against the same database once before the first start (the Compose file
above does this for you).
Every published image is signed over its digest, and carries an SBOM and a build provenance attestation. The signing is keyless, so verification names the workflow that is allowed to have produced it rather than a key:
Reading the SBOM, the build record and the signed SLSA provenance is three more commands.
To build locally instead:
Endpoints
Most endpoints are governed by a feature flag, and a flag that is off means the endpoint is not
served at all — not merely that discovery stops advertising it. The refusal is deliberately
indistinguishable from a path the server does not have, so if a request below returns 404, check the
flag before suspecting a defect. Flags are set in the admin console under Settings, or directly on
ApplicationConfig, and a change saved in the console applies at once.
Always available
Enabled by default
Flag-governed, but on unless you turn them off.
Opt-in
Absent from a default install until the flag is set.
Reading your own registration follows registration.enabled rather than
registrationManagement.enabled: the registration response hands the client that URI, so refusing the
read while registration is on would break the capability that issued it. Only the mutating methods
need the management flag.
Route families
Reached by redirect or through the console rather than called directly, so these are described as families rather than enumerated.
The interaction and self-service families are unconditional by design — a user locked out of their
account cannot be asked to wait for a capability toggle. The console is unconditional too, even though
/mcp is gated: switching MCP off must not take the console with it.
Administration
The server administers itself. GET /admin is a console for projects, teams, OAuth clients,
administrators, end-users, upstream identity providers, settings, signing keys and an
append-only audit trail. Administrators sign in through an OpenID Connect flow against this server's
own issuer, so there is no second password store.
[The administration console: a project's OAuth clients]
Administration from an AI agent (MCP)
The same management API is available to an AI agent over the Model Context Protocol, as an OAuth 2.1 protected resource of this server. It is off by default — the capability hands an agent the authority of the administrator who authorized it, so a deployment should switch it on deliberately:
- In the console, Settings → Administration → Enable the administrative MCP control plane (it applies on save).
- Point your MCP client at
https://your-server/mcpwith client idadmin-mcpand resourcehttps://your-server/mcp.
An agent then acts as the administrator who signed in, with exactly that account's permissions. Every change it makes runs through the same routes, the same checks and the same audit trail as the console's, and each audit entry names both the operator and the agent.
A few things it deliberately cannot do: delete a project or a team, purge the fault store, and change which client identities may administer the instance. The deletions destroy a container with nothing left afterwards to inspect, and the last is a privilege-escalation path, so they stay console-only. Everything else destructive takes two steps — the agent describes what would change and you confirm that specific operation.
Which client identity works here. The reserved
client_idabove always works and needs no setup beyond the toggle. An agent host that cannot be told aclient_idcan use this surface too, provided it identifies itself with an OAuth Client ID Metadata Document — an HTTPS URL naming a document that describes it — and a super administrator has named that identity under Agent access in the console. A client that registered itself through Dynamic Client Registration cannot administer the instance under any configuration: its identity is minted on demand by whoever asked for it, so there is nothing an operator could meaningfully allowlist.
Protecting your own MCP server
The same protocol surface works for an MCP server that is not this one. Declare it as a protected
resource of a project — its canonical URI, the scopes it recognises, how its tokens are verified —
and this server mints tokens whose audience is exactly that resource, with no code change here and no
restart. An agent host discovers it, signs your end-users in, and comes back with a token your MCP
server verifies against the jwks_uri its issuer's metadata names, without asking anything.
Protect your MCP server with OAuth walks it end to end — including what a correct refusal looks like, so you can tell "protected" from "not reached yet".
Implemented Standards
The Flag column names the capability's governing flag; — means always on. See
Features for defaults.
Security
Report a vulnerability to [email protected] or through private vulnerability reporting; SECURITY.md has the scope, the response times and the safe harbour.
The same file says what stands behind the code: a published threat model, CodeQL, dependency and container scanning on every push and every week (what runs and where to read it), signed images with an SBOM and build provenance, and an OpenSSF Scorecard. It also says what does not exist yet — no external audit, no paid bounty, no OpenID Foundation certification — because a security page that only lists the good news is not one you can trust.
Contributing
Questions and ideas go to GitHub Discussions: Q&A for how-do-I, Ideas for proposals.
Contributions are welcome! Please open an issue or pull request. For significant changes, open an issue first to discuss your proposal.
License
OAuth-server.ts is licensed under the Functional Source License, Version 1.1, ALv2 Future
License (FSL-1.1-ALv2) — the same license Sentry uses. In short: you may use, modify,
self-host and redistribute it for any purpose except offering it to others as a competing hosted
service, and each version converts to the Apache License 2.0 two years after its release.
This project began in April 2025 as a fork of node-oidc-provider by Filip Skokan, distributed under the MIT License. Its notice is preserved in NOTICE.
來源:README.md,提交 4af4e42
工具
0版本歷史
5- v1.5.0最新Nov 5, 2026
- v1.4.0Nov 3, 2026
- v1.3.0Oct 22, 2026
- v1.2.0Oct 21, 2026
- v1.1.0Oct 19, 2026
評分
已滿足適用檢查項的 75%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通必需25 分
- 已宣告認證方式必需10 分
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分
請在儲存庫或註冊中心條目中宣告授權條款。