-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(open-payments): add HTTP message signatures #722
Conversation
Authorization: 'GNAP accessToken', | ||
Signature: 'TODO', | ||
'Signature-Input': 'TODO' | ||
Authorization: 'GNAP accessToken' |
Check failure
Code scanning / CodeQL
Hard-coded credentials
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add some sort of exception for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean for codeql?
We could open an issue to exclude test files in https://github.com/interledger/rafiki/blob/main/.github/workflows/codeql-analysis.yml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
)};keyid="${keyId}";alg="ed25519"` | ||
) | ||
.get('/incoming-payment') | ||
// TODO: verify signature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we move the meat of auth
's signature validation into open-payments
(after #672 is merged)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by open-payments
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that make sense to add it in here though? I feel like sig validation is done on the "receiver" side , but the client is more of the "caller" in our scenario
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking the "receiver"/RS will also be using open-payments
at least for the types (why I was reluctant to call open-payments
a "client" library), so why not also provide httpsig verification?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wilsonianb wouldn't the RS also use it for sig creation when requesting resources at other RS's?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, the RS will also use createClient
createAuthenticatedClient
to send (signed) requests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, the receiver will just import the lib and use some of those sig verification methods as part of the middleware (how it's done now) -> makes sense
}, | ||
null, | ||
{ | ||
runWhen: (config) => !!config.headers['Authorization'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we don't need Authorization
header on the AS calls, this would effectively handle both AS and RS with no problem, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are some AS calls that require the Authorization
header
https://datatracker.ietf.org/doc/html/draft-ietf-gnap-core-protocol#section-7.3.1.1
I was actually thinking that the grant initiation request might be the only one with it, but I'm not sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that mean that the auth server open payment specs are missing the signature-input
and signature
header parameters in the open api spec files? If they need to provide an Authorization
token?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, looks like that's the case.
I just opened:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wilsonianb for my own understanding, wouldn't grant initiation be the only AS request without the Authorization: GNAP {accessToken}
header?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I think that's correct
axiosInstance.defaults.headers.common['Content-Type'] = 'application/json' | ||
|
||
axiosInstance.interceptors.request.use( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirming: does this apply to all GET, POST, DELETE requests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe so, but it can be filtered.
privateKey: KeyLike | ||
keyId: string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about 👆, if there are clients that only want to use open-payments
to query payment pointers and/or their keys and/or connections, it seems like it should be possible to create a client without requiring privateKey
/keyId
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should open-payments
createClient
be split into:
createAuthenticatedClient
createUnauthenticatedClient
?
The latter would return the subset of requests that are possible without httpsig.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wilsonianb the explicit split makes sense to me
method: config.method.toUpperCase(), | ||
url: config.url, | ||
// https://github.com/axios/axios/issues/5089#issuecomment-1297761617 | ||
headers: JSON.parse(JSON.stringify(config.headers)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the concerns with using just config.headers
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At some point, I was getting the error described in that axios github issue.
But now I can't reproduce. (Tests pass with `config.headers)
I'll tentatively remove the workaround.
privateKey: KeyLike | ||
keyId: string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wilsonianb the explicit split makes sense to me
)};keyid="${keyId}";alg="ed25519"` | ||
) | ||
.get('/incoming-payment') | ||
// TODO: verify signature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that make sense to add it in here though? I feel like sig validation is done on the "receiver" side , but the client is more of the "caller" in our scenario
Remove Axios headers workaround.
@@ -112,6 +112,7 @@ export const Config = { | |||
'AUTH_SERVER_SPEC', | |||
'https://raw.githubusercontent.com/interledger/open-payments/77462cd0872be8d0fa487a4b233defe2897a7ee4/auth-server-open-api-spec.yaml' | |||
), | |||
keyId: envString('KEY_ID', 'rafiki'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the default be something that includes internal
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just wondering if rafiki
is a good value for the default. But now that I have thought a bit more about it, I think it is.
} | ||
} | ||
|
||
export interface CreateOpenPaymentClientArgs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we call this CreateAuthenticatedClientArgs
? I think it makes it more readable.
incomingPayment: IncomingPaymentRoutes | ||
} | ||
|
||
export const createClient = async ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And then I'd call this createAuthenticatedClient
.
)};keyid="${keyId}";alg="ed25519"` | ||
) | ||
.get('/incoming-payment') | ||
// TODO: verify signature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wilsonianb wouldn't the RS also use it for sig creation when requesting resources at other RS's?
Authorization: 'GNAP accessToken', | ||
Signature: 'TODO', | ||
'Signature-Input': 'TODO' | ||
Authorization: 'GNAP accessToken' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add some sort of exception for this?
components.push('authorization') | ||
} | ||
if (request.body) { | ||
// TODO: 'content-digest' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to reference #655 in the comment, if we do that at all?
keyId | ||
}: SignOptions): Promise<SignatureHeaders> => { | ||
const components = ['@method', '@target-uri'] | ||
if (request.headers['Authorization']) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we care about casing on Authorization
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears to matter.
The sets headers properly if accessToken provided
test fails if I make this lower case.
)};keyid="${keyId}";alg="ed25519"` | ||
) | ||
.get('/incoming-payment') | ||
// TODO: verify signature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, the receiver will just import the lib and use some of those sig verification methods as part of the middleware (how it's done now) -> makes sense
@@ -112,6 +112,7 @@ export const Config = { | |||
'AUTH_SERVER_SPEC', | |||
'https://raw.githubusercontent.com/interledger/open-payments/77462cd0872be8d0fa487a4b233defe2897a7ee4/auth-server-open-api-spec.yaml' | |||
), | |||
keyId: envString('KEY_ID', 'rafiki'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just wondering if rafiki
is a good value for the default. But now that I have thought a bit more about it, I think it is.
Changes proposed in this pull request
createClient
createAuthenticatedClient
createUnauthenticatedClient
limited to payment pointer and connection requests, which do not require 👆Context
Checklist
fixes #number