Skip to content
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

chore: token invalid throw 401 instead of return public incoming payment #3062

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

DarianM
Copy link

@DarianM DarianM commented Oct 31, 2024

Changes proposed in this pull request

Moving the await next() inside the try catch as I believe this is a good practice.
Letting next() call outside the try-catch creates potential issues:

  • error handling inconsistency: some errors are caught and handled, but other errors from next() can propagate up unhandled I think
  • unexpected flow: the middleware might partially fail but still continue execution. If an error occurs during token validation, the middleware might still call next() with invalid/incomplete context state

Renaming bypassError to canSkipAuthValidation to not introspect an access token and respond with "public" incoming payment that does not require an access token at all

Context

If an accessToken is provided with the request to get an incoming payment, and the token is invalid, it fails with a 401 error instead of returning a public incoming payment
Link issues here - fixes #2889

Checklist

  • Related issues linked using fixes #number
  • Tests added/updated
  • Make sure that all checks pass
  • Bruno collection updated (if necessary)
  • Documentation issue created with user-docs label (if necessary)

@github-actions github-actions bot added type: tests Testing related pkg: backend Changes in the backend package. type: source Changes business logic pkg: documentation Changes in the documentation package. labels Oct 31, 2024
Copy link

netlify bot commented Oct 31, 2024

Deploy Preview for brilliant-pasca-3e80ec canceled.

Name Link
🔨 Latest commit dd1b8e3
🔍 Latest deploy log https://app.netlify.com/sites/brilliant-pasca-3e80ec/deploys/6724e6f07324a40008e38fc4

@@ -178,6 +178,18 @@ You can either trigger the debugger by adding `debugger` statements in the code
#### Debugging with VS Code:

To debug with VS Code, add this configuration to your `.vscode/launch.json`:
```json
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also adding the json from the localenv README file here as it's missing here and I find it useful as it helped me debug the code.
if out of scope PR, can make another PR

@DarianM DarianM changed the title chore:token invalid throw 401 instead of return public incoming payment chore: token invalid throw 401 instead of return public incoming payment Oct 31, 2024
@DarianM DarianM requested a review from mkurapov October 31, 2024 16:49
return
}

const authSplit = ctx.request.headers.authorization?.split(' ')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const authSplit = ctx.request.headers.authorization?.split(' ')
const authSplit = ctx.request.headers.authorization.split(' ')

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

authorization can still be undefined here, when the canSkipAuthValidation is false it won't enter the early return statement and will return 401 because of undefined authorization;
this is the reason i left the ? there

Copy link
Contributor

@mkurapov mkurapov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just need to run pnpm format to fix the check.

We also have a line in packages/backend/src/open_payments/payment/incoming_remote/service.ts which we can remove
("// TODO: remove after #2889 is completed").

@@ -145,19 +151,16 @@ export function createTokenIntrospectionMiddleware({
: undefined
}
}

await next()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep this in the outside of try-catch. Otherwise, the catch block in this middleware will apply to all of the following middlewares called in await next() which might lead to unexpected behaviour, e.g., adding WWW-Authenticate when we don't want to.

Comment on lines 157 to 162
if (!(err instanceof OpenPaymentsServerRouteError)) {
throw err
}

ctx.set('WWW-Authenticate', `GNAP as_uri=${config.authServerGrantUrl}`)

if (!bypassError) {
throw err
}
throw err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!(err instanceof OpenPaymentsServerRouteError)) {
throw err
}
ctx.set('WWW-Authenticate', `GNAP as_uri=${config.authServerGrantUrl}`)
if (!bypassError) {
throw err
}
throw err
if (err instanceof OpenPaymentsServerRouteError) {
ctx.set('WWW-Authenticate', `GNAP as_uri=${config.authServerGrantUrl}`)
}
throw err

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are here, this seemed more straightforward

@@ -577,7 +577,7 @@ export class App {
createTokenIntrospectionMiddleware({
requestType: AccessType.IncomingPayment,
requestAction: RequestAction.Read,
bypassError: true
canSkipAuthValidation: true
}),
authenticatedStatusMiddleware,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In authenticatedStatusMiddleware we should return early if we know that we ended up skipping auth validation in createTokenIntrospectionMiddleware (since it doesn't make sense to do a lot of work for something we already know shouldn't succeed). One way we can do this is to make client optional in HttpSigWithAuthenticatedStatusContext, and only proceed to call throwIfSignatureInvalid if ctx.client is defined (setting ctx.client is the main function of createTokenIntrospectionMiddleware)

Copy link
Author

@DarianM DarianM Nov 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about checking for
if (!ctx.request.headers.authorization) await next() return }
instead of making the client optional (string | undefined) for the rest of the code? I also see that authenticatedStatusMiddleware is only used for open payments

DarianM and others added 3 commits November 1, 2024 15:40
`authorization` can still be undefined here, when the `canSkipAuthValidation` is false it won't enter the early return statement and will return 401 because of undefined authorization

Co-authored-by: Max Kurapov <max@interledger.org>
@DarianM DarianM requested a review from mkurapov November 1, 2024 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg: backend Changes in the backend package. pkg: documentation Changes in the documentation package. type: source Changes business logic type: tests Testing related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Throw error when failing to introspect token instead of returning public incoming payment
2 participants