ory/fosite

Add support for extending Response Modes

Closed this issue · 3 comments

Is your feature request related to a problem? Please describe.

Hi, for our OpenID implicit flow, we have clients with user agents that do not support redirects and fragments, but a custom agreed response for the id_token and redirect information.

RFC 6749 Section 4.2.2 leaves open the option to provide other kind of responses:

Developers should note that some user-agents do not support the
inclusion of a fragment component in the HTTP "Location" response
header field. Such clients will require using other methods for
redirecting the client than a 3xx redirection response -- for
example, returning an HTML page that includes a 'continue' button
with an action linked to the redirection URI.

OpenID oauth v2 multiple response types governs how response_mode param must behave and provides a real life example of how they can be extended:

See OAuth 2.0 Form Post Response Mode OAuth.Post for an example of a specification that defines an additional Response Mode. Note that it is expected that additional Response Modes may be defined by other specifications in the future...

Currently fosite supports the standard oauth2 and openid response modes query, fragment, form_post but it does not have a way to provide additional ones.

Describe the solution you'd like
In short, I would like that fosite provides an extension point to set additional response modes.

The following are the proposed implementation changes to achieve it:

  1. fosite.go: Add an attribute to fosite.Fosite to set a ResponseModeHandler which handles custom response modes
type Fosite struct {
    ...
    ResponseModeHandler ResponseModeHandler
}

response_mode_handler.go

type ResponseModeHandler interface {
    // Set of response modes supported by this handler. Being an array
    // allows for composition without adding complexity to `Fosite`
    ResponseModes() []string
    // Writes Authorize Response errors
    WriteAuthorizeError(...)
    // Writes successful Authorize Response
    WriteAuthorizeResponse(...)
}
  1. authorize_error.go:63: Check if response_mode is in fosite.Fosite.ResponseModeHandler.ResponseModes() and if so write error using its WriteAuthorizeError. It will behave similar to ResponseModeFormPost.

  2. authorize_write.go:41: Add a extra case for fosite.Fosite.ResponseModeHandler.ResponseModes(). It behaves similar to ResponseModeFormPost case

  3. authorize_request_handler.go: Adjust ParseResponseMode to include handler ResponseModes() in the validation

Describe alternatives you've considered

  • custom fork
  • change clients: currently not possible, clients are not in our control, and they are thousands

Additional context

The change is 100% backwards compatible, if no handler is register fosite provides its standard functionality, if registered fosite will include it on authorization validations, write error and write success.

Already supported response_mode methods won't be modified to use the new interface, they can remain explicit and hardcoded since they are standard.

What do you think?
Would you be open for this contribution?

Please take a look to the draft PR #592. Your comments are welcome!

Btw. I created this issue with other username, but it is me :)

mitar commented

I am curious, why is form_post not working for your clients? What is the mode you are hoping to implement? Would it make sense to contribute this extra mode to fosite?

Hi @mitar, basically we do not use general purpose but tailored user-agents. Those are closed-source user-agents with workflows/endpoints and redirections hard-coded, they neither understand HTTP redirects nor are capable to extract values from #fragments, they expect a json response similar to the Access Token Successful/Error Reponse (rfc6749). Those thousand clients are already in the market using this custom authorize response, and updating them is not an option due to technical and also project coordination overhead.

example response

{
    "id_token": "..."
}

Because it is a response tailored to our needs I do not think it will be useful in fosite, but if needed or there are others interested in this solution I could also include it. For now I think it will be more useful to include an example similar to what I have implemented for our case, in the docs/how-to folder, explaining how this extension can be used.