identityserver-api-protection
Protecting APIs with Duende IdentityServer: JWT bearer authentication, reference token introspection, scope-based authorization, DPoP/mTLS proof-of-possession validation, local API authentication, and multi-audience scenarios.
What this skill does
# Protecting APIs with IdentityServer
## When to Use This Skill
- Configuring JWT bearer authentication in an ASP.NET Core API to validate tokens from IdentityServer
- Setting up reference token introspection with `AddOAuth2Introspection`
- Handling both JWT and reference tokens in the same API using `ForwardReferenceToken`
- Implementing scope-based authorization policies
- Validating Proof-of-Possession tokens (DPoP and mTLS `cnf` claim)
- Protecting APIs hosted in the same application as IdentityServer (local API authentication)
- Securing multi-audience API deployments
Docs: https://docs.duendesoftware.com/identityserver/tokens/api-protection
## Core Concepts
APIs are the resources that IdentityServer protects. Clients obtain access tokens from IdentityServer, then present those tokens to APIs. The API must validate the token and enforce authorization based on the token's claims (scopes, audience, subject, etc.).
### Token Formats at the API
| Format | Validation Method | Revocable | Network Dependency |
| -------------- | ------------------------------------------ | ---------------------- | ------------------------------------ |
| JWT (`at+jwt`) | Signature verification using issuer's JWKS | No (expires naturally) | None at validation time |
| Reference | Introspection endpoint call | Yes (immediate) | Requires IdentityServer availability |
## JWT Bearer Authentication
### Basic Setup
Install the standard Microsoft JWT bearer package:
```bash
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
```
Configure the authentication handler:
```csharp
// Program.cs
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://identity.example.com";
options.Audience = "api1";
options.TokenValidationParameters.ValidTypes = ["at+jwt"];
});
```
### Critical: JWT Type Validation
Always set `ValidTypes` to `["at+jwt"]` to protect against JWT confusion attacks. Without this, an attacker could present an identity token (which is also a JWT signed by the same issuer) to an API:
```csharp
// ❌ WRONG: No type validation — vulnerable to JWT confusion
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true
};
// ✅ CORRECT: Validate the at+jwt type header
options.TokenValidationParameters.ValidTypes = ["at+jwt"];
```
IdentityServer sets the `typ` header to `at+jwt` on all access token JWTs (per RFC 9068). This is controlled by `IdentityServerOptions.AccessTokenJwtType`.
### Audience Validation
The `Audience` property on `JwtBearerOptions` validates the `aud` claim in the access token. The audience value comes from the `ApiResource` name in IdentityServer:
```csharp
// IdentityServer configuration
var apiResource = new ApiResource("api1")
{
Scopes = { "api1.read", "api1.write" }
};
// API configuration
options.Audience = "api1";
```
If `Audience` is not set, audience validation is skipped (not recommended for production).
### Multi-Audience APIs
When an API belongs to multiple logical resources, configure multiple valid audiences:
```csharp
options.TokenValidationParameters.ValidAudiences = ["api1", "api2"];
```
## Reference Token Introspection
For APIs that receive reference tokens (opaque strings rather than JWTs), use the OAuth 2.0 introspection package:
```bash
dotnet add package Duende.IdentityServer.AccessTokenValidation
```
Or use the introspection handler directly:
```bash
dotnet add package Duende.AspNetCore.Authentication.OAuth2Introspection
```
```csharp
// Program.cs
builder.Services.AddAuthentication("token")
.AddOAuth2Introspection("token", options =>
{
options.Authority = "https://identity.example.com";
options.ClientId = "api1";
options.ClientSecret = "api1_secret";
});
```
The `ClientId` and `ClientSecret` correspond to the `ApiResource` name and secret configured in IdentityServer:
```csharp
// IdentityServer configuration
var apiResource = new ApiResource("api1")
{
ApiSecrets = { new Secret("api1_secret".Sha256()) },
Scopes = { "api1.read" }
};
```
### Common Pitfall: Missing ApiSecrets
```csharp
// ❌ WRONG: No secret configured — introspection will fail with 401
var apiResource = new ApiResource("api1")
{
Scopes = { "api1.read" }
};
// ✅ CORRECT: ApiSecrets required for introspection
var apiResource = new ApiResource("api1")
{
ApiSecrets = { new Secret("secret".Sha256()) },
Scopes = { "api1.read" }
};
```
## Handling Both JWT and Reference Tokens
Use `ForwardReferenceToken` from the `Duende.AspNetCore.Authentication.JwtBearer` package to support both token formats in a single API. This selector inspects the token: if it contains a dot (`.`) it is treated as a JWT; otherwise it is forwarded to the introspection handler.
```bash
dotnet add package Duende.AspNetCore.Authentication.JwtBearer
```
```csharp
// Program.cs
builder.Services.AddAuthentication("token")
.AddJwtBearer("token", options =>
{
options.Authority = "https://identity.example.com";
options.Audience = "api1";
options.TokenValidationParameters.ValidTypes = ["at+jwt"];
// Forward reference tokens to the introspection handler
options.ForwardDefaultSelector =
Selector.ForwardReferenceToken("introspection");
})
.AddOAuth2Introspection("introspection", options =>
{
options.Authority = "https://identity.example.com";
options.ClientId = "api1";
options.ClientSecret = "api1_secret";
});
```
### How ForwardReferenceToken Works
The selector checks whether the incoming Bearer token string contains a dot (`.`):
- **Contains a dot** → treated as a JWT, validated by `AddJwtBearer`
- **No dot** → treated as a reference token, forwarded to `AddOAuth2Introspection`
This is a simple heuristic: JWTs always contain dots (header.payload.signature), while reference tokens are opaque identifiers.
## Scope-Based Authorization
### Scope Claim Format
IdentityServer can emit scopes in two formats, controlled by `EmitScopesAsSpaceDelimitedStringInJwt`:
| Setting | Claim Format | Example |
| ----------------- | ---------------------- | -------------------------------------- |
| `false` (default) | JSON array | `"scope": ["api1.read", "api1.write"]` |
| `true` | Space-delimited string | `"scope": "api1.read api1.write"` |
### Normalizing Scope Claims
When scopes are emitted as a space-delimited string, the `scope` claim appears as a single string value. To normalize it back to individual claims for easier policy checks, implement a custom `IClaimsTransformation`:
```csharp
// Program.cs
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://identity.example.com";
options.Audience = "api1";
options.TokenValidationParameters.ValidTypes = ["at+jwt"];
});
// Register a custom claims transformation to split space-delimited scopes
builder.Services.AddTransient<IClaimsTransformation, ScopeClaimsTransformation>();
```
```csharp
// ScopeClaimsTransformation.cs
public class ScopeClaimsTransformation : IClaimsTransformation
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var identity = (ClaimsIdentity)principal.Identity!;
var scopeClaim = identity.FindFirst("scope");
if (scopeClaim != null && scopeClaim.Value.Contains(' '))
{
identity.RemoveClaim(scopeClaim);
foreach (var scope in scopeClaim.Value.Split(' '))
{
identity.AddClaim(new Claim("scope", scope));
}
}
return Task.FromResult(principal);
}
}
```
This transformation converts a space-delimited `scope` cRelated in Backend & APIs
jfrog
IncludedInteract with the JFrog Platform via the JFrog CLI and REST/GraphQL APIs. Use this skill when the user wants to manage Artifactory repositories, upload or download artifacts, manage builds, configure permissions, manage users and groups, work with access tokens, configure JFrog CLI servers, search artifacts, manage properties, set up replication, manage JFrog Projects, run security audits or scans, look up CVE details, query exposures scan results from JFrog Advanced Security, manage release bundles and lifecycle operations, aggregate or export platform data, or perform any JFrog Platform administration task. Also use when the user mentions jf, jfrog, artifactory, xray, distribution, evidence, apptrust, onemodel, graphql, workers, mission control, curation, advanced security, exposures, or any JFrog product name.
cupynumeric-migration-readiness
IncludedPre-migration readiness assessor for porting NumPy to cuPyNumeric. Use BEFORE substantial porting work begins when the user asks whether code will scale on GPU, whether they should migrate to cuPyNumeric, which NumPy patterns transfer cleanly, what must be refactored before porting, or mentions pre-port assessment, scaling analysis, or refactor planning. Inspect the user's source code, look up NumPy usage, cross-reference the cuPyNumeric API support manifest, and distinguish distributed-scaling-friendly patterns from blockers such as unsupported APIs, scalar synchronization, host round-trips, Python/object-heavy control flow, shape/data-dependent branching, and in-place mutation hazards. Produce a verdict of READY, LIGHT REFACTOR, SIGNIFICANT REFACTOR, or NOT RECOMMENDED, with concrete refactor pointers.
alibabacloud-data-agent-skill
IncludedInvoke Alibaba Cloud Apsara Data Agent for Analytics via CLI to perform natural language-driven data analysis on enterprise databases. Data Agent for Analytics is an intelligent data analysis agent developed by Alibaba Cloud Database team for enterprise users. It automatically completes requirement analysis, data understanding, analysis insights, and report generation based on natural language descriptions. This tool supports: discovering data resources (instances/databases/tables) managed in DMS, initiating query or deep analysis sessions, real-time progress tracking, and retrieving analysis conclusions and generated reports. Use this Skill when users need to query databases, analyze data trends, generate data reports, ask questions in natural language, or mention "Data Agent", "data analysis", "database query", "SQL analysis", "data insights".
token-optimizer
IncludedReduce OpenClaw token usage and API costs through smart model routing, heartbeat optimization, budget tracking, and native 2026.2.15 features (session pruning, bootstrap size limits, cache TTL alignment). Use when token costs are high, API rate limits are being hit, or hosting multiple agents at scale. The 4 executable scripts (context_optimizer, model_router, heartbeat_optimizer, token_tracker) are local-only — no network requests, no subprocess calls, no system modifications. Reference files (PROVIDERS.md, config-patches.json) document optional multi-provider strategies that require external API keys and network access if you choose to use them. See SECURITY.md for full breakdown.
resend-cli
IncludedUse this skill when the task is specifically about operating Resend from an AI agent, terminal session, or CI job via the official resend CLI: installing/authenticating the CLI, sending/listing/updating/cancelling emails, batch sends, domains and DNS, webhooks and local listeners, inbound receiving, contacts, topics, segments, broadcasts, templates, API keys, profiles, or debugging Resend CLI/API failures. Trigger on mentions of Resend CLI, `resend`, `resend doctor`, `resend emails send`, `resend domains`, `resend webhooks listen`, `resend emails receiving`, or agent-friendly terminal automation.
alibabacloud-odps-maxframe-coding
IncludedUse this skill for MaxFrame SDK development and documentation navigation on Alibaba Cloud MaxCompute (ODPS). Helps answer MaxFrame API, concept, official example, and supported pandas API questions; create data processing programs; read/write MaxCompute tables; debug jobs (remote or local); and build custom DPE runtime images. Trigger when users mention MaxFrame, MaxCompute with MaxFrame, ODPS table processing, DPE runtime, MaxFrame docs/examples, DataFrame/Tensor operations, or GPU runtime setup. Works for both English and Chinese queries about Alibaba Cloud data processing with MaxFrame.