Table of Contents

Response modes

New in 0.130.0, Duende 8.0.1's, measured. The code flow answers the way the client asks with response_mode:

response_mode The answer
query, or none a redirect with the parameters in the redirect URI's query string
form_post a page that posts the parameters to the redirect URI as a form (OAuth 2.0 Form Post Response Mode)
fragment a redirect with the parameters in the redirect URI's fragment

Errors go back in the same mode as success. The form_post page is Duende's: it posts itself on load, with a button for a browser that runs no script; its Content-Security-Policy allows only that script, named by its hash ('unsafe-inline' is added at CspLevel.One, which has no hash sources); it is sent no-store, because it carries a code, and with Referrer-Policy: no-referrer.

ASP.NET Core's OpenID Connect handler asks for form_post by default, so its clients get what they asked for rather than a code in the query string. Written out, as a web application signing in against this server would have it — from samples/Server.Hosting:

/// <summary>
/// Signs users in with the authorization code flow and PKCE, asking for the code by form post — the handler's
/// default, written out here — so it never appears in a URL.
/// </summary>
public static IServiceCollection AddServerSignIn(this IServiceCollection services)
{
    services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddOpenIdConnect(options =>
        {
            options.Authority = "https://localhost:5001";
            options.ClientId = "web";
            options.ClientSecret = "web-secret";
            options.ResponseType = OpenIdConnectResponseType.Code;
            options.ResponseMode = OpenIdConnectResponseMode.FormPost;
            options.UsePkce = true;
            options.SaveTokens = true;
            options.Scope.Add("api");
            options.Scope.Add("offline_access");
        });

    return services;
}

Client libraries shows the same handler in a back end for a front end that calls APIs as its user.

Any other value — query.jwt (JARM, which Duende does not have either), or Form_Post, since the match is exact — is refused with unsupported_response_type and "Invalid response_mode", on the error page rather than at the client, there being no knowing how a client that asked for a mode this server lacks would read its answer. Until 0.130.0 response_mode was not read at all: every answer went in the query, whatever was asked.

Discovery lists response_modes_supported as form_post, query, fragment, in Duende's order, under DiscoveryOptions.ShowResponseModes (on by default); until 0.130.0 it said query only, and went with ShowResponseTypes. ValidatedAuthorizeRequest.ResponseMode, deleted in 0.26.0, is back with the value.

The authorization code flow is recorded end to end.