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

feat(auth): allow non-interactive grants for incoming payments #588

Merged
merged 16 commits into from
Sep 27, 2022

Conversation

sabineschaller
Copy link
Member

@sabineschaller sabineschaller commented Sep 2, 2022

Changes proposed in this pull request

  • if the access type is incoming-payment, the grant will be issued right away, without interaction

Context

Checklist

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

@github-actions github-actions bot added pkg: auth Changes in the GNAP auth package. type: source Changes business logic type: tests Testing related labels Sep 2, 2022
@sabineschaller
Copy link
Member Author

Q: Should we have the same behavior for quote grant requests? @wilsonianb

@wilsonianb
Copy link
Contributor

Q: Should we have the same behavior for quote grant requests? @wilsonianb

I'm not sure.
Creating a quote can indirectly reveal the sender's available balance at the account provider.
OTOH a grant allowing quote creation is likely to also include outgoing payment creation, which would require interaction.

packages/auth/src/accessToken/service.test.ts Outdated Show resolved Hide resolved
@@ -66,7 +71,8 @@ export function createGrantRoutes({
}
}

function validateGrantRequest(
// exported for testing
export function validateGrantRequest(
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 have checks that aren't covered by the OpenAPI validator middleware?

createValidatorMiddleware<ContextType<typeof route>>(openApi, {

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so but I'd like @njlie to confirm.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we're missing out on the benefits of the OpenAPI validator middleware type guard by not specifying the ctx.body type in createGrantInitiation and elsewhere

export type ValidateFunction<T> = (data: any) => data is T
export interface OpenAPI {
paths: Paths
createRequestValidator<T>(options: RequestOptions): ValidateFunction<T>

See:
export type CreateBody = {
description?: string
expiresAt?: string
incomingAmount?: AmountJSON
externalRef?: string
}
async function createIncomingPayment(
deps: ServiceDependencies,
ctx: CreateContext<CreateBody>

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd hold off on any work there now that Adrian has been working on his auth server implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think so but I'd like @njlie to confirm.

Yeah I'm pretty sure this can be excised now, I wrote this before we had brought OpenAPI into the auth service. This basically is a type guard against the POST / request which is definitely covered.

Copy link
Member Author

Choose a reason for hiding this comment

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

I removed the validation function and re-opened #351

packages/auth/src/grant/routes.ts Outdated Show resolved Hide resolved
packages/auth/src/grant/service.test.ts Outdated Show resolved Hide resolved
@sabineschaller sabineschaller marked this pull request as ready for review September 5, 2022 09:47
@sabineschaller
Copy link
Member Author

Creating a quote can indirectly reveal the sender's available balance at the account provider.
OTOH a grant allowing quote creation is likely to also include outgoing payment creation, which would require interaction.

Then let's leave it as is for now.

Copy link
Contributor

@wilsonianb wilsonianb left a comment

Choose a reason for hiding this comment

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

packages/auth/src/accessToken/service.test.ts Outdated Show resolved Hide resolved
packages/auth/src/accessToken/service.test.ts Outdated Show resolved Hide resolved
@@ -10,6 +10,10 @@ function envInt(name: string, value: number): number {
return envValue == null ? value : parseInt(envValue)
}

function envBool(name: string, value: boolean): boolean {
const envValue = process.env[name]
return envValue == null ? value : envValue === 'true'
Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer your version over:

// function envBool(name: string, value: boolean): boolean {
// const envValue = process.env[name]
// return envValue == null ? value : Boolean(envValue)
// }

Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't know we had that.

@@ -66,7 +71,8 @@ export function createGrantRoutes({
}
}

function validateGrantRequest(
// exported for testing
export function validateGrantRequest(
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we're missing out on the benefits of the OpenAPI validator middleware type guard by not specifying the ctx.body type in createGrantInitiation and elsewhere

export type ValidateFunction<T> = (data: any) => data is T
export interface OpenAPI {
paths: Paths
createRequestValidator<T>(options: RequestOptions): ValidateFunction<T>

See:
export type CreateBody = {
description?: string
expiresAt?: string
incomingAmount?: AmountJSON
externalRef?: string
}
async function createIncomingPayment(
deps: ServiceDependencies,
ctx: CreateContext<CreateBody>

!deps.config.incomingPaymentInteraction &&
body.access_token.access
.map((acc) => {
return isIncomingPaymentAccessRequest(acc as IncomingPaymentRequest)
Copy link
Contributor

@wilsonianb wilsonianb Sep 8, 2022

Choose a reason for hiding this comment

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

Suggested change
return isIncomingPaymentAccessRequest(acc as IncomingPaymentRequest)
return isIncomingPaymentAccessRequest(acc as AccessRequest)

I think the isIncomingPaymentAccessRequest argument type should be changed to AccessRequest.

Also, if we specified ctx.body type we wouldn't need to cast here at all (#588 (comment))

Copy link
Contributor

Choose a reason for hiding this comment

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

bump

Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't want to touch this because I thought I would add the change to have the ctx.body typed but that's probably going to be a subsequent PR so yes, I'll change that.

Copy link
Member Author

Choose a reason for hiding this comment

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

But it complains with

Types of property 'type' are incompatible.
      Type 'AccessType.OutgoingPayment' is not assignable to type 'AccessType.IncomingPayment'.ts(2345)

Copy link
Contributor

@wilsonianb wilsonianb Sep 22, 2022

Choose a reason for hiding this comment

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

BaseAccessRequest might work, but I just noticed the other type guards also have the target type as the argument type.
Maybe they can all be updated as part of #351 instead of this pr.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think that is a good idea.

packages/auth/src/grant/routes.ts Outdated Show resolved Hide resolved
packages/auth/src/grant/routes.ts Outdated Show resolved Hide resolved
packages/auth/src/grant/service.ts Outdated Show resolved Hide resolved
table.string('continueToken').unique()
table.string('continueId').unique()
table.string('continueToken').notNullable().unique()
table.string('continueId').notNullable().unique()
Copy link
Contributor

@njlie njlie Sep 13, 2022

Choose a reason for hiding this comment

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

It shouldn't be necessary to generate continuation information if interaction (or further continuation) isn't necessary for a grant. The POST / should just return the access token without any continuation information. Continuation information could just be generated if the grant is modified in a way that requires interaction to be issued again (if we end up supporting that again).

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we discussed that while you were away. We always want to be able to delete/revoke the grant and for that you need the continue token and id. Hence, it must always be present. We also already changed the spec to make it always required.

access: access.map((a: Access) => accessToBody(a)),
expiresIn: accessToken.expiresIn
expiresIn: accessToken.expiresIn,
continue: {
Copy link
Contributor

Choose a reason for hiding this comment

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

We can exclude the continue object if there's no interaction required and the AS can just supply an access_token instead. Also vice/versa for if interaction is required. It's basically an either/or situation between continue and access_token with how we're using GNAP.

Copy link
Member Author

Choose a reason for hiding this comment

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

See above.

!deps.config.incomingPaymentInteraction &&
body.access_token.access
.map((acc) => {
return isIncomingPaymentAccessRequest(acc as IncomingPaymentRequest)
Copy link
Contributor

Choose a reason for hiding this comment

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

bump

packages/auth/src/grant/routes.ts Outdated Show resolved Hide resolved
packages/auth/src/grant/service.test.ts Show resolved Hide resolved
packages/auth/src/grant/service.test.ts Outdated Show resolved Hide resolved
packages/auth/src/grant/routes.ts Outdated Show resolved Hide resolved
access_token: {
value: grant.continueToken
},
uri: domain + `continue/${grant.continueId}`
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so because the grant is granted right away. At least that was my interpretation of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg: auth Changes in the GNAP auth package. type: source Changes business logic type: tests Testing related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support grant issuance without interaction
4 participants