Skip to content

Consent flow loops between /connect/authorize and /Consent #1712

Description

@StuartFerguson

When an OIDC client has RequireConsent = true, the Security Service enters an infinite redirect loop:

/connect/authorize
→ /Consent?returnUrl=...
→ /connect/authorize
→ /Consent

The browser eventually displays:

ERR_TOO_MANY_REDIRECTS

Root cause

OidcRequestHandler redirects explicit-consent requests to /Consent.

However, /Consent is a new HTTP request. The OpenIddict request is not automatically available, so:

HttpContext.GetOpenIddictServerRequest()

returns null.

ConsentRequestHandler treats the missing request as a direct or invalid visit and redirects back to the original returnUrl. This sends the browser back to /connect/authorize, which redirects to /Consent again.

Affected code

  • SecurityService.BusinessLogic/Oidc/OidcRequestHandler.cs
  • SecurityService.BusinessLogic/RequestHandlers/ConsentRequestHandler.cs
  • SecurityService/Pages/Consent/Index.cshtml.cs

The redirect to the consent page occurs in OidcRequestHandler.cs.

The redirect back to the original URL occurs when the OpenIddict request is unavailable in ConsentRequestHandler.cs.

Steps to reproduce

  1. Register an OIDC client with:

    RequireConsent = true
    ConsentType = explicit
    
  2. Start the Security Service.

  3. Start an OIDC client application.

  4. Begin the login flow.

  5. Authenticate successfully.

  6. Allow the client to require consent.

Expected behavior

The consent page should display:

  • Client name
  • Requested identity scopes
  • Requested API scopes
  • Accept and deny buttons

After accepting or denying consent, the user should be redirected back to the client callback URL.

Actual behavior

The browser continuously redirects between:

/connect/authorize

and:

/Consent?returnUrl=...

Eventually the browser reports:

ERR_TOO_MANY_REDIRECTS

Workaround

Set the client to use implicit consent:

RequireConsent = false
ConsentType = implicit

This bypasses the consent page but does not fix the underlying consent implementation.

Suggested fix

Preserve or reload the OpenIddict authorization transaction when /Consent is requested.

The consent page should be able to:

  1. Recover the original authorization request.
  2. Load the client and requested scopes.
  3. Render the consent page.
  4. Process accept or deny actions.
  5. Redirect back to the original authorization URL with the appropriate consent result.

The implementation should not depend on GetOpenIddictServerRequest() being populated on the separate /Consent request unless the authorization transaction is explicitly restored first.

Required tests

Add integration tests covering:

  • Explicit consent page renders successfully.
  • Accepting consent redirects to the client callback.
  • Denying consent redirects with an access-denied response.
  • Missing or invalid consent transactions return a controlled error rather than redirecting indefinitely.
  • Implicit-consent clients continue to authorize without displaying the consent page.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions