Generate MVC binding models from core request models#188
Merged
Conversation
Introduce four semantic markers in DeclarativeValidation that name the wire format of a parameter without referencing any binding technology: space-separated string, integer seconds, embedded JSON document, and space-separated BCP 47 language tags. Annotate the authorization request model with them. Relax the request_uri annotation to plain AbsoluteUri: the value is either an HTTPS URL (OIDC Core 6.2) or the urn:ietf:params:oauth:request_uri: value issued by the PAR endpoint (RFC 9126 2.2), so requiring the https scheme contradicted the supported PAR flow.
Add an incremental source generator that produces the MVC binding model from the core request model: bound properties with wire names taken from the core serialization metadata, model binders resolved from the core wire-format markers, validation attributes translated to their executable MVC counterparts, and the projection method back onto the core type. Each binder self-declares the marker it realises via the Binds attribute, so the marker-to-binder knowledge lives next to the binders and the generator carries no mapping table. A marker with no binder fails the build instead of silently dropping the parameter. A hand-written partial record stub marked with GeneratedFrom opts a model into generation. Migrate the authorization request model as the first one: the handwritten model is replaced by a stub, and the generated counterpart is identical except that the absolute-URI validation is now actually enforced - the handwritten model referenced the inert core marker instead of the executable attribute.
Restores resolved against two user-level package sources without a source mapping, which central package management flags as NU1507 on every project. The library consumes only public packages, so the repository pins itself to nuget.org and maps every package there. Release-workflow jobs that stage local packages restore in sibling checkout directories, outside the scope of this file.
…ined Remove the AllowedValues lists from the token request grant type, the authorization request response type, and the client registration grant and response types. All four sets are defined by host DI registrations and are already validated at runtime against the same union the discovery document advertises - by the composite grant handler, the flow validator, and the registration pipeline respectively. A static list duplicated that gate and misstated it: it rejected host-added extensions at the model-binding layer with a transport-level 400 instead of the protocol-level error, and the duplicated MVC copy had already drifted from the core one (eight grants versus nine). Document the rule on every runtime-defined field: remarks now explain why the signing and encryption algorithm fields, the token endpoint authentication method, the registration grant and response types, and the authorization request client identifier carry no declarative constraints.
The server never supported response_type=none: no response processor is registered for it and no code references the constant, so it only suggested a capability the discovery document does not advertise.
…ators Migrate the introspection, revocation, userinfo, device authorization, backchannel authentication and end-session models to source generation: the hand-written models become generation stubs and the corresponding core models gain wire-format markers. The end-session model keeps its hand-written cross-property validation in the stub. Replace the generated Map method with an implicit conversion operator to the core model, so controllers and tests assign the bound model directly and a forgotten projection call is no longer possible. Generation closes two silent parameter drops on the CIBA endpoint: the core model accepts the claims and signed request parameters, but the hand-written MVC model carried no binding for either. It also enforces the absolute-URI constraint on the post-logout redirect URI, which the hand-written model declared with the inert core marker, and removes the accidental GET binding the CIBA model carried on one property of the POST-only endpoint. Restructure the generator emission into a ModelEmitter holding the per-model state, and add the System.Index polyfill so list patterns compile on netstandard2.0.
… own files The pipeline record types moved to one-type-per-file; this removes the copies left behind in the original file.
The hand-written MVC model becomes a generation stub and the core model gains the space-separated marker on the scope parameter. The token controller assigns the bound model through the implicit conversion.
Introduce three semantic source markers in the core: a value arriving in a named HTTP request header, the parsed Authorization header, and the client certificate presented at the transport layer. The client request model is annotated with them and its hand-written MVC counterpart becomes a generation stub; the header and certificate binders self-declare the markers they realise. In the generator, a source marker overrides the payload exclusion, and an unrecognised declarative marker on a payload-excluded property now fails the build, so a renamed or mistyped marker cannot silently drop a bound parameter. Names of types the generator cannot reference are gathered as documented constants; reachable ones use nameof.
Correct the factually wrong summary of the backchannel authentication success model (the request is accepted while end-user authentication is still pending), fix copy-paste drift on the post-logout redirect URIs summary, repair broken grammar, and document the previously undocumented wire-parameter constants across the model parameter classes.
Delete the hand-written MVC mirror of the client registration request: the JSON body deserializes directly into the core model, whose serialization metadata and property defaults are authoritative, so a transport copy adds nothing but drift risk. The controller supplies the transport-level Authorization header on top of the bound model. Convert the client management authorization projection to an implicit operator for consistency, and document why that model stays hand-written: the client identifier travels in the URL path, a routing concept the core deliberately does not know about.
A bound model consumed by one call converts implicitly at the call site; a model feeding both the handler and the formatter keeps one explicit conversion into a core-prefixed local, so both calls receive the same core instance.
Restore the explicit projection methods on the generated models and the management authorization: the method is discoverable in completion lists and reads naturally in tests, while the implicit operator stays as a one-line delegate to it, so there is still a single projection body and no call to forget.
…oop filters into Where Address the pull request analysis findings: the static helpers used only by the emitter move inside it, and the loops that filtered their sequence in the body now filter explicitly in the enumeration pipeline.
|
Comment on lines
+508
to
+512
| foreach (var declaration in declarations) | ||
| { | ||
| if (declaration.ConstructorArguments is [{ Value: INamedTypeSymbol marker }]) | ||
| map[marker.ToDisplayString()] = type; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Replaces the hand-written MVC binding models with an incremental Roslyn source generator that produces them from the core request models, eliminating the duplicated model layer and the silent-parameter-drop class of bugs it bred.
How it works
Resulting model layout
Behavioural fixes surfaced by the migration
Also included
All 2910 tests pass, including the full E2E suite.