Skip to content

API ideas #6

Description

@oneiros

I love how much consideration and hard work already went into this gem, but I cannot help to think it is bit too cumbersome to use 😕

Maybe I am missing a use case, but I mainly think of two:

  1. I use a web framework (e.g. sinatra or rails) and want to verify incoming requests and/or sign outgoing responses.
  2. I use an http library (e.g. net/http or httpx) to make requests and I want to sign those requests and/or verify the responses I get.

One thing those cases have in common is that the framework or library used has its own abstraction for requests and responses (e.g. sinatra has classes that inherit from Rack::Request and Rack::Response, rails has ActionDispatch::Request etc., net/http has Net::HTTPRequest and so on).

If I want to sign a request I currently need to:

  1. Copy URI, method, headers (and possibly params, though that does not yet work currently) from the original request object.
  2. User Linzer.new_request to create a Rack::Request object with the information gathered in 1.
  3. Create a Linzer::Message from the request object created in 2.
  4. Perform signing with the Linzer::Message object.
  5. Copy the two resulting headers back into my original request object.

(The process for responses is quite similar.)

This is a 4-5 step process for something that from a developer's POV is a single operation: "Please sign this request."
Verification is quite similar.

All the frameworks and libraries mentioned above work quite similarly when it comes to requests and responses. Their respective objects do differ but at the end of the day there are only so many ways you can represent URIs, headers etc.

So I think it would be possible for linzer to ship with a tiny abstraction for these objects and pluggable adapters to work with all those different request and response objects. It could ship with support for maybe rack, rails and net/http and let users define their own adapters.

With that in place, the signing and verifying could then really be a single step each. Maybe with an API like this:

Linzer.sign!(
  request_or_response,
  key: my_key,
  components: %w[@method @target-uri content-digest],
  label: "sig1",
  params: {
    created: Time.now.to_i
  }
)

request_or_response is one of the supported request or response objects. key is mandatory, but all the other parameters can have sensible defaults.

Verification needs a way to retrieve a key for the given keyid. I thought that maybe it could use a block for that:

Linzer.verify(request_or_response) do |keyid|
  # Retrieve and return the matching key
end

I also have some ideas of how key handling could be a bit simpler, but I wanted to get this out of the way first.

Please let me know what you think. I am happy to prototype this idea a bit further and submit a PR if you think it could work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions