Skip to content

feat(backend): tenant signature validation for admin api #3164

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

Merged
merged 16 commits into from
Dec 17, 2024

Conversation

njlie
Copy link
Contributor

@njlie njlie commented Dec 6, 2024

Changes proposed in this pull request

  • Adds a utility to determine if a http signature was signed by a tenant, the operator, or neither and augment the context of the request to reflect that.

Context

Fixes #2928.

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)
  • OpenAPI specs updated (if necessary)

@github-actions github-actions bot added type: tests Testing related pkg: backend Changes in the backend package. type: source Changes business logic labels Dec 6, 2024
@njlie njlie marked this pull request as ready for review December 6, 2024 18:38
@njlie njlie force-pushed the nl/2928/multi-tenancy-signatures branch from f4d3fb6 to 22d48fe Compare December 6, 2024 23:02
@github-actions github-actions bot added pkg: frontend Changes in the frontend package. pkg: auth Changes in the GNAP auth package. pkg: documentation Changes in the documentation package. labels Dec 6, 2024
Base automatically changed from nl/3123/backend-tenant-service to 2893/multi-tenancy-v1 December 9, 2024 17:32
@njlie njlie force-pushed the nl/2928/multi-tenancy-signatures branch from 22d48fe to 3805b10 Compare December 9, 2024 18:43
@github-actions github-actions bot removed pkg: frontend Changes in the frontend package. pkg: documentation Changes in the documentation package. pkg: auth Changes in the GNAP auth package. labels Dec 9, 2024
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.

To clarify, is the idea that we can run Rafiki without an operator tenant?

}

const tenantService = await ctx.container.use('tenantService')
const tenantId = headers['tenantid']
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 tenantId = headers['tenantid']
const tenantId = headers['tenant-id']

nit, but maybe kebab case is better for http headers?


const tenantService = await ctx.container.use('tenantService')
const tenantId = headers['tenantid']
const tenant = tenantId ? await tenantService.get(tenantId) : undefined
Copy link
Contributor

Choose a reason for hiding this comment

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

should we return false if we can't find the tenant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The idea here is to account for non-tenanted requests (e.g. createTenant, listTenants, other pagination requests). But this is moot since our call where the tenant id is to be expected in any request.

Comment on lines 217 to 220
if (
verifyApiSignatureDigest(
signature as string,
ctx.request,
config,
config.adminApiSecret as string
)
) {
ctx.tenant = tenant
ctx.isOperator = true
return true
}
Copy link
Contributor

Choose a reason for hiding this comment

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

(related to my PR)
If we think that apiSecret is required in the DB schema, and we are seeding the operator tenant, we could actually avoid this extra check, and just determine whether its the operator by doing tenant.apiSecret = config.adminApiSecret after verifyingApiSignatureDigest

@mkurapov mkurapov linked an issue Dec 9, 2024 that may be closed by this pull request
2 tasks
@@ -384,12 +398,14 @@ export class App {
)

if (this.config.adminApiSecret) {
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 remove this check if this secret is required, right?

@njlie njlie requested a review from mkurapov December 10, 2024 23:43
if (!verifyApiSignature(ctx, this.config)) {
koa.use(
async (ctx: TenantedHttpSigContext, next: Koa.Next): Promise<void> => {
if (!verifyTenantOrOperatorApiSignature(ctx, this.config)) {
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 (!verifyTenantOrOperatorApiSignature(ctx, this.config)) {
if (!(await verifyTenantOrOperatorApiSignature(ctx, this.config))) {

to mirror the PR just merged into main


ctx.request.body = requestBody

const result = await verifyTenantOrOperatorApiSignature(ctx, config)
Copy link
Contributor

Choose a reason for hiding this comment

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

Might be good to check that the tenantService.get was in fact called

signature as string,
ctx.request,
config,
config.adminApiSecret as string
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
config.adminApiSecret as string
config.adminApiSecret

Since it should already be typed

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: but we can pass in either whole of config or adminApiSecret + adminApiSignatureVersion explicitly

@njlie
Copy link
Contributor Author

njlie commented Dec 12, 2024

Integration tests should start working again once #3177 is in

@njlie njlie requested a review from mkurapov December 13, 2024 19:40

if (!(await canApiSignatureBeProcessed(signature, ctx, config))) return false

// First, try validating with the tenant api secret
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
// First, try validating with the tenant api secret

Comment on lines 176 to 177
Verifies http signatures by first attempting to replicate it with a secret
associated with a tenant id in the headers, then with the configured admin secret.
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be good to update this comment

Comment on lines 148 to 162
type TenantedHttpSigHeaders = HttpSigHeaders & Record<'tenantId', string>

type TenantedHttpSigRequest = Omit<HttpSigContext['request'], 'headers'> & {
headers: TenantedHttpSigHeaders
}

export type TenantedHttpSigContext = HttpSigContext & {
headers: TenantedHttpSigHeaders
request: TenantedHttpSigRequest
tenant?: Tenant
isOperator: boolean
}
Copy link
Contributor

Choose a reason for hiding this comment

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

These types seem to be more suited for the Open Payments server. The main thing we need to do here is to add the the tenant object to the GraphQL context (ApolloContext & lines 409 -> 418), such that we can get the tenant information in the resolvers

@njlie njlie force-pushed the nl/2928/multi-tenancy-signatures branch from 602aa6e to 23b121d Compare December 16, 2024 19:04
@@ -171,6 +173,53 @@ async function canApiSignatureBeProcessed(
return true
}

export interface TenantApiSignatureResult {
tenant?: Tenant
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
tenant?: Tenant
tenant: Tenant

(same for TenantedApolloContext)

@njlie njlie merged commit a8b7ca4 into 2893/multi-tenancy-v1 Dec 17, 2024
36 checks passed
@njlie njlie deleted the nl/2928/multi-tenancy-signatures branch December 17, 2024 15:23
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.

[Multi-Tenant] Use http-signatures to determine tenant identity during requests
3 participants