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(backend): update wallet address middleware & error handling #2722

Merged
merged 13 commits into from
May 21, 2024

Conversation

mkurapov
Copy link
Contributor

@mkurapov mkurapov commented May 13, 2024

Changes proposed in this pull request

To finalize the standardization of Open Payments route errors in backend, this PR splits up walletAddressMiddleware into several separate middlewares. One group of them deals with adding the walletAddressUrl onto the ctx (from several resources), while getWalletAddressForSubresource simply polls for the active wallet address by using the ctx.walletAddressUrl itself. Each of the middlewares is then added explicity to the route.

The benefits:

  • We now handle all possible Open Payments API errors properly in the openPaymentsServerErrorMiddleware (i.e. we don't use ctx.throw at all in other places)
  • walletAddressUrl is added to the ctx as a middleware specific to the route route, meaning we can remove all-encompassing logic in walletAddressMiddleware. Likewise, we don't need to actually fetch the wallet address before doing token introspection and http sig verification

Other changes:

  • When wallet address is requested, return the Open Payments one by default.
  • SPSP payment pointer can only be fetched when setting accept header to include 'application/spsp4+json' explicitly

Context

Fixes #1905 for backend

Checklist

  • Related issues linked using fixes #number
  • Tests added/updated
  • Documentation added
  • Make sure that all checks pass
  • Bruno collection updated

@mkurapov mkurapov changed the title 1905/mk/update wallet address middleware chore(backend): update wallet address middleware May 13, 2024
Copy link

netlify bot commented May 13, 2024

Deploy Preview for brilliant-pasca-3e80ec ready!

Name Link
🔨 Latest commit bffcaa5
🔍 Latest deploy log https://app.netlify.com/sites/brilliant-pasca-3e80ec/deploys/664c9ffc9d1af20008f24f03
😎 Deploy Preview https://deploy-preview-2722--brilliant-pasca-3e80ec.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@github-actions github-actions bot added type: tests Testing related pkg: backend Changes in the backend package. type: source Changes business logic labels May 13, 2024
@@ -318,8 +318,7 @@ export function initIocContainer(
return createWalletAddressKeyRoutes({
config: await deps.use('config'),
logger: await deps.use('logger'),
walletAddressKeyService: await deps.use('walletAddressKeyService'),
walletAddressService: await deps.use('walletAddressService')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

unused now

@@ -95,7 +95,7 @@ export function createTokenIntrospectionMiddleware({
const access = tokenInfo.access.find((access: Access) => {
if (
access.type !== requestType ||
(access.identifier && access.identifier !== ctx.walletAddress.url)
(access.identifier && access.identifier !== ctx.walletAddressUrl)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

at this point, in the token introspection middleware, we only have ctx.walletAddressUrl in the context

@@ -30,16 +30,6 @@ export async function getWalletAddress(
deps: ServiceDependencies,
ctx: WalletAddressContext
): Promise<void> {
if (!ctx.walletAddress) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we're already handling this in the wallet address middleware

@@ -426,7 +429,6 @@ export class App {
// Create incoming payment
router.post<DefaultState, SignedCollectionContext<IncomingCreateBody>>(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

now, each route follows as such:

  1. validate against the OpenAPI spec (since we need all of the request parameters, including the walletAddressUrl ones to be always valid)
  2. Get wallet address URL, from either request body, query params, or the resource itself
  3. do token introspection
  4. do http sig verification (if required)
  5. fetch (poll for) walletAddress via the walletAddressUrl from step 2, add it onto the ctx
  6. Call the actual route

walletAddressServerSpec,
{
path: '/',
method: HttpMethod.GET
},
validatorMiddlewareOptions
),
getWalletAddressUrlFromPath,
createWalletAddressMiddleware({ errorCodeOnMissing: 404 }),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

usually, we call 400 (since 404 doesn't really make sense when doing a e.g. GET/POST outgoing payment, since its not the resource that's missing, its the wallet address request body or query param that points to an invalid wallet address).

Comment on lines 671 to 672
getWalletAddressUrlFromPath,
createSpspMiddleware(this.config.spspEnabled),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

do not call getWalletAddressForSubresource here and in the jwks.json route since we have different behaviours in these routes for handling deps.config.walletAddressUrl === ctx.walletAddressUrl

@mkurapov mkurapov changed the title chore(backend): update wallet address middleware chore(backend): update wallet address middleware & error handling May 13, 2024
@mkurapov mkurapov marked this pull request as ready for review May 13, 2024 19:40
@BlairCurrey
Copy link
Contributor

  • walletAddressUrl is added to the ctx as a middleware specific to the route route, meaning we can remove all-encompassing logic in walletAddressMiddleware. Likewise, we don't need to actually fetch the wallet address before doing token introspection and http sig verification

This is much better. Low coupling/high cohesion. 👍

): Promise<void> {
if (!ctx.walletAddress) {
// The instance's wallet address is only relevant for the /jwks.json route, so "hide" it when directly requested
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this mean we just shouldn't have a /GET route for the wallet address? If we still need this route then when would we need it? Comment seems to suggest we only need the /jwks.json route IMO but maybe im not following.

Copy link
Contributor

Choose a reason for hiding this comment

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

When I call the Examples > Open Payments > Get receiver wallet address bruno request (which creates the wallet address) then call Open Payments APIs > Get a Wallet Address I receive the the wallet address.

If I copy/paste the url into the browser http://localhost:4000/accounts/asmith then I see Could not get wallet address.

How should it be working? It seems like the bruno request should not return the wallet address according to this comment.

Copy link
Contributor Author

@mkurapov mkurapov May 15, 2024

Choose a reason for hiding this comment

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

@BlairCurrey

deps.config.walletAddressUrl is the configured wallet address for the Rafiki instance itself, i.e. its the wallet address used to get incoming payments/incoming payment grants when creating "remote" incoming payments at other Rafikis. e.g. https://ilp.rafiki.money/.well-known/pay/.

Maybe I can word this comment better, but we actually don't want to resolve https://ilp.rafiki.money/.well-known/pay/ ever (this shouldn't be a wallet address which resources can be created on, its not for an actual user account). As a result, we only care about the https://ilp.rafiki.money/.well-known/pay/jwks.json path, since it gets looked up to check the keys of the rafiki instance when validating the grant on another rafiki.

Copy link
Contributor Author

@mkurapov mkurapov May 15, 2024

Choose a reason for hiding this comment

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

All other "normal" wallet addresses, should get resolved as usual with the correct asset, publicName, auth/resourceServer urls (since they represent actual user accounts)

Actually, it is odd you don't see http://localhost:4000/accounts/asmith. Can you try a different wallet address?

Copy link
Contributor

@BlairCurrey BlairCurrey May 15, 2024

Choose a reason for hiding this comment

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

Ah that makes sense, thank you for the context.

Same result with http://localhost:3000/accounts/gfranklin in the browser, but not with bruno. This is not the case on main - this request works as expected in the browser. The only difference between bruno and the browser I see is the host header that gets added in the pre-request script. Not sure if that's the actual difference.

Here is the error:

{"level":30,"time":1715787978022,"pid":1,"hostname":"cloud-nine-wallet-backend","method":"GET","path":"/accounts/gfranklin","message":"Could not get wallet address","msg":"Received error when handling SPSP request"}

Looks like its failing at the if (!walletAddress?.isActive) { check of the spspsMiddleware.

Copy link
Contributor

Choose a reason for hiding this comment

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

going back just 1 step further it looks like if (ctx.accepts('application/spsp4+json')) { resolves to false with the bruno request, and true for the browser.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What happens when we call http://localhost:3000/accounts/gfranklin in the browser, or without the host header in bruno, is we actually end up updating the wallet address via the wallet_address.not_found webhook event. (ie if you request http://localhost:3000/accounts/gfranklin on main, the wallet address will end up changing from https://cloud-nine-wallet-backend/accounts/gfranklin to http://localhost:3000/accounts/gfranklin.

This PR is different for SPSP request since we use walletAddressService.getByUrl instead of walletAddressService.getOrPollByUrl. However, I think this PR has same behaviour when we request a wallet address without going through SPSP as well just by requesting the wallet address with Accept: application/json. I think we should update the MASE code to prevent this, and seems like a separate issue in general.

I'm not sure why I decided to use walletAddressService.getByUrl in SPSP instead of polling (maybe I assume if you are using SPSP then we should already have a wallet address)?

This makes me wonder if we should default to returning the Open Payments wallet address actually, and only return the SPSP details if explicitly requested?

Copy link
Contributor Author

@mkurapov mkurapov May 21, 2024

Choose a reason for hiding this comment

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

This makes me wonder if we should default to returning the Open Payments wallet address actually, and only return the SPSP details if explicitly requested?

Chatted w Sabine regarding this, decision made to return Open Payments wallet address by default when fetching it. Also made a change to return SPSP wallet address only if 'application/spsp4+json' is present in accept header explicitly, and decided that using getByUrl was sufficient in the SPSP lookup.

@mkurapov mkurapov force-pushed the 1905/mk/update-wallet-address-middleware branch from 15cc4a6 to 7f6a65a Compare May 21, 2024 13:08
Comment on lines -652 to +663
createWalletAddressMiddleware(),
getWalletAddressUrlFromPath,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Basically, if we are fetching the SPSP payment pointer, we don't need to call the validator middleware since its not Open Payments related

Comment on lines -41 to +42
if (ctx.accepts('application/spsp4+json')) {
if (ctx.request.header.accept?.includes(SPSP_CONTENT_TYPE_V4)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this ctx.accepts was the reason why by default when fetching the wallet address, the SPSP details were returned, since usually, the accept header included catch all */* route, which to ctx.accepts would be truthy.

Now, we need to explicitly add application/spsp4+json in the header to get the SPSP details

@mkurapov
Copy link
Contributor Author

Going to close and re-open to see if I can get past the stuck actions (I think affected by their degradation here)

@mkurapov mkurapov closed this May 21, 2024
@mkurapov mkurapov reopened this May 21, 2024
@mkurapov mkurapov merged commit 3d6c1d4 into main May 21, 2024
79 checks passed
@mkurapov mkurapov deleted the 1905/mk/update-wallet-address-middleware branch May 21, 2024 14:11
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. type: source Changes business logic type: tests Testing related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Standardize Open Payment errors
2 participants