Add support for custom auth plugins#464
Conversation
|
I haven't tried it yet, I up slapped together an example of how this could work. Many thanks. |
|
We might want to support plugins for other features besides authentication. Perhaps we can pass a
This makes sense but it seems useful to be able to specify a plugin by its file path, especially if it's not in
This is probably the best solution but it makes it slightly awkward for plugins to take interactive user input. (On Unix you can still do this by opening
This can be expensive e.g. for large file uploads. How often will plugins need to know the body? Maybe we could figure out a way for the plugin to only request the body if it needs it? If we leave this out now we can always add it later. (I see the HMAC example uses it...)
Can't the plugin look at its own working directory? |
Would an env variable be enough? we're currently passing
Sounds good 👍
Hmm, I haven't thought about this. Let me check the behaviour in windows as well.
Yeah, you're right. Let me remove it.
I agree. One thing I was experimented with is to have the plugin configure itself, so if it receives the following: {
"next_request": {
"method": "POST",
"url": "http://example.org/api/v1/hello",
"headers": [],
"body_base64": null
},
"auth": [],
"state": null,
"current_dir": "/home/user"
"config": null
}it can output something like: { "set_config": { "requires_body": true } }Which leads to the plugin being spawned again with the following: {
"next_request": {
"method": "POST",
"url": "http://example.org/api/v1/hello",
"headers": [],
"body_base64": "eyJoZWxsbyI6IndvcmxkIn0="
},
"auth": [],
"state": null,
"current_dir": "/home/user"
"config": { "requires_body": true }
} |
Making it part of the payload seems most natural, you can directly express that as a serde enum or TypeScript tagged union. Configuring could even be a separate mandatory step, so that we send a payload like We could also do JSONL, keep the plugin process running and keep sending it new payloads. (And take care not to deadlock if a naïve plugin e.g. stops listening too early because it doesn't take redirects into account?) It smells like an ad hoc IPC system, we may be reinventing the wheel... |
The idea of the env-var was to have a "normal" application just define an alias (symlink) and then trigger the plugin behavior by detecting the env-var. That would not work if you would first need to parse stdin.
I think using stdio for things like this and a one shot approach is a fairly common pattern in Unix-ish OSes. Also for Windows. Creating a service and keeping things running makes things rather complicated. |
I forgot that the env var name contained It's unfortunate that env vars get inherited by the plugin's child processes but that's probably unlikely to cause problems in practice. (We could pass it as an argument instead.) Instead of
If we do this, do we also allow other commands in that same response (with the possibility that the plugin modifies headers twice)? A poorly written plugin might confuse an empty body for a missing body, so maybe we should error if the plugin calls |
Sounds like a good idea! |
Note that this would be in line with how git finds external helpers. From
|
|
Does it make sense to still use the Update: |
this will be documented in man pages instead
|
Couple of questions to think about:
|
This PR extends
--auth-typeto acceptplugin-NAMEe.gplugin-hmac-sha256which instructsxhto spawn an executable namedxh-plugin-NAME.Initially, the plugin the receives
{"type": "configure"}and it can either respond with{"requires_body": true}or{}.After that, the plugin is re-spawned for each request and receives the following via stdin:
{ "type": "before_request", "request": { "method": "POST", "url": "http://example.org/api/v1/hello", "headers": [ { "name": "content-type", "value": "application/json" } ], "body_base64": "eyJoZWxsbyI6IndvcmxkIn0=" }, "auth": [], "state": null, }The plugin will then modify request by outputting JSON object via stdout:
{ "remove_headers": ["x-signature"], "add_headers": [ { "name": "x-signature", "value": "12345678" } ], "set_state": null }Some notes:
--authoption e.g--auth-type=plugin-oauth2 --auth=client_id:pluto --auth=client_secret:12345 --auth=scope:42XH_AUTH_PLUGIN=1is passed to pluginstatecan be used to hold arbitrary data.plugin-prefix is not required if full path is specified e.g--auth-type=/usr/bin/oauth2Resolves #424