feat(ext-mcp): let applications put middleware on the MCP route, and document the auth split
McpExtension built its middleware chain entirely internally, so a consumer had no way to add rate limiting, audit logging or tracing to /mcp — routine on every other Flash route. McpConfig.middleware(...) appends to the chain after the transport guards and after whatever McpSecurity resolved to, so it composes with OAuth2 protection instead of replacing it, and never satisfies REQUIRED. Docs: flash-ext-auth-core and flash-ext-auth-oidc both get a docs/ directory — oidc had none at all, and its module README documented types that no longer exist. Includes a migration table from flash-ext-oidc.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# Sessions
|
||||
|
||||
A `Session` is what a credential source keeps server-side between requests, looked up by a cookie.
|
||||
Core owns the container; what goes in it is the source's business.
|
||||
|
||||
```java
|
||||
public final class Session {
|
||||
String id();
|
||||
Map<String, Object> claims();
|
||||
Instant expiresAt();
|
||||
Map<String, Object> attributes();
|
||||
boolean isExpired();
|
||||
Object attribute(String key);
|
||||
String attributeAsString(String key);
|
||||
}
|
||||
```
|
||||
|
||||
## Why it expires early
|
||||
|
||||
`isExpired()` returns true **30 seconds before** `expiresAt`. Without that window a session can
|
||||
pass the check at the top of a request and be dead by the time the handler uses it — a class of
|
||||
failure that reproduces once a day and never in a test. Renewal is therefore always slightly
|
||||
premature, on purpose.
|
||||
|
||||
## Attributes
|
||||
|
||||
`attributes()` is opaque to this module. `flash-ext-auth-oidc` keeps its access, id and refresh
|
||||
tokens there under its own keys, which is what lets renewal stay entirely inside that extension
|
||||
while the session itself carries no OAuth2 vocabulary.
|
||||
|
||||
Store what your source needs to renew or revoke, and nothing a handler should be reading — handlers
|
||||
read `claims()`.
|
||||
|
||||
## The store
|
||||
|
||||
```java
|
||||
public interface SessionStore {
|
||||
void save(Session session);
|
||||
Optional<Session> find(String sessionId);
|
||||
void delete(String sessionId);
|
||||
}
|
||||
```
|
||||
|
||||
`InMemorySessionStore` is the default: a `ConcurrentHashMap`, fine for a single instance, and it
|
||||
loses every session on restart. Supply your own for Redis or JDBC when sessions have to survive a
|
||||
deploy or be shared across nodes.
|
||||
|
||||
Sessions are immutable. Renewing one builds a new instance with the same `id()` and `save`s it
|
||||
over the old — there is no mutate-in-place path, so a store can cache or serialise freely.
|
||||
Reference in New Issue
Block a user