OAuth APIs (Resource Servers)
An OAuth API in Modgud is the registration of a resource server — an API that wants to validate access tokens issued by Modgud and use them to authorise requests.
OAuth API vs OAuth Client
- OAuth Client = the app that performs the user login and gets tokens
- OAuth API = the API that validates tokens and authorises requests against them
An app can be both (e.g. a BFF pattern: user-login as a client, its own API as an API).

When do I need an OAuth API registration?
For most cases — a SaaS app that validates Modgud tokens — yes, you register an OAuth API for it. The registration is what lets Modgud emit a tailored resource_access[<audience>] block for this resource server in JWT access tokens, UserInfo and authorized introspection responses. Specifically, it's required when:
- You want per-Audience permission narrowing in
resource_accessblocks. The RS declares itsPermissionIdssubset of the App's catalog, and the IdP narrows each user's emission to that subset. - The API needs explicit scope lists for discovery
Relationship to Applications
An OAuth API normally belongs to one Application. A microservice architecture under one app — e.g. acme-api, acme-search, acme-files all linked to the App acme — works because permissions stay app-centric: each microservice gets its own PermissionIds subset of the same App catalog, and the IdP narrows the separate resource_access["acme-api"], resource_access["acme-search"] and resource_access["acme-files"] blocks accordingly.
An API can temporarily remain unassigned for legacy or standalone setups. Without an Application link, Modgud has no permission catalog to resolve and does not emit a resource_access block for that audience.
Creating an API
Administration → OAuth & Federation → OAuth-APIs → Create.
Required fields
- Audience (aud) — technical identifier (e.g.
acme-api). Used inaudclaims when the token is issued. - Display Name — UI label
- Application — which App does this RS belong to? Recommended and required for per-Audience permission emission.
- Description — optional
PermissionIds
The subset of the linked App's catalog this RS gates on. Used by the IdP to narrow resource_access[<this API's Audience>].permissions — sibling resource servers under the same App get their own Audience keys and do not project each other's permissions.
The selection starts empty. Pick only the catalog entries this resource server actually exposes.
Scopes
A list of scope names this API understands. Any token whose scope claim contains one of these is considered "for this API". Used for OIDC discovery and resource indication.
One-click implicit scope
In the API detail modal there is a Create implicit scope button when the API has no scope with the same name yet (it hits POST /api/admin/oauth/apis/{id}/create-implicit-scope). Clicking it creates a real OAuthScope row with:
Name= API nameResources=[<api-name>](so the audience matches the API)Enabled = true,ShowInDiscoveryDocument = false(private by default, see below)- Linked to the same App as the API
Why you usually want this: without a scope whose Resources lists the API name, a token requested for this API carries no matching aud claim, and the IdP emits no resource_access block for the API. The implicit scope is what couples the two — once a client requests scope=<api-name>, the issued token gets aud=<api-name> and the RS's resource_access block is populated. It is the fast path for the common 1:1 case: an API and a scope that always go together.
After creation the button disappears (re-check via API list reload). The implicit scope is otherwise a normal scope row — editable, deletable, and requestable by clients via scope=<api-name>.
When to keep things separate
Two situations warrant a manually-created additional scope on top of the implicit one:
- Granularity —
<api>.read/.write/.adminagainst the same audience. Differentiates capabilities viascp, notaud. - Multi-RS scope — one scope name pointing to multiple APIs (
scope=admin→aud: [policy-api, audit-api]). Edge case but valid.
User claims
Optional list of claim types this API expects in tokens. Used by some IdP-side filtering mechanisms; for most setups, leave empty.
How a resource server authenticates against Modgud
An OAuth API has no credential surface of its own. When the resource server needs to call Modgud's own APIs directly (e.g. an admin or distribution endpoint), it does so via OAuth using a confidential OAuth Client linked to a Service Account: the client requests an access token via Client-Credentials and uses it as a bearer like any other token. There is no per-API shared secret to rotate.
Token introspection is a special case
Validating an opaque reference access token via /connect/introspect is different, because the IdP only reveals a token — its active status and its resource_access block — to a caller that is one of the token's audiences or its presenter. A generic Service-Account client is neither, and gets active: false.
So an introspecting resource server registers a confidential OAuth Client whose Client ID equals its own audience (this API's name — the RFC 8707 resource= value already carried in the token's aud), and authenticates the introspection call with that client's own credentials (sent as form-body parameters, so a URL-shaped audience id works). The .NET resource-server library does this through AddModgudResourceServer with TokenMode = ModgudTokenMode.OnlyReferenceToken.
Editing
Most fields can be edited live; Audience (aud) is immutable after creation. Changing the linked Application is allowed but be careful — the RS's scope-resolution and the per-Audience resource_access shape immediately switch to the new app context.
Cloning an API
Audience (aud) is immutable, so to make a near-identical resource server, clone it. List → right-click → Clone. The Create create dialog opens pre-filled — display name, description, scopes, user claims, the linked Application and its catalog subset are copied; only Audience (aud) is blank.
Deleting
List → right-click → Delete. Soft-deleted; the OAuth API is no longer usable but the aggregate stream is retained for audit.
Common patterns
One app, one resource server
Default for most SaaS apps: create one OAuth API named after the app's slug, link it to the App, and pick the catalog subset it gates on.
One app, multiple resource servers (microservices)
Each microservice gets its own OAuth API entry with its own narrower PermissionIds subset of the App's catalog. All link to the same App. Per-Audience narrowing means each block contains only its API's permission subset. A multi-audience token may carry multiple blocks side-by-side, but each resource-server scheme projects only its configured Audience.
Multi-tenant API
If the same API logic serves multiple realms, each realm gets its own OAuth API entry. Modgud's tenancy already enforces realm separation at the database level, so a query-level lookup can't reach another realm's tokens — each realm's OpenIddict store lives in its own database.
Tips
Audit trail
RS-Auth-protected endpoint calls log the calling RS's name. Useful when several microservices share one App and you want to know which specific RS made a given request.
Two distinct identities
A user bearer token identifies the user; the RS-as-OAuth-client identity (a Client-Credentials access token minted via a Service Account) identifies the RS itself. They sit on independent authentication axes — both can be relevant on the same request.