Skip to content

Conversation

@mkurapov
Copy link
Contributor

@mkurapov mkurapov commented Oct 9, 2025

Changes proposed in this pull request

  • Add apiSecret to tenant table in auth
  • Seed apiSecret from env variable for operator
  • Propagate apiSecret from backend to auth when creating new tenants
  • On auth startup, update apiSecret if it has changed
  • Add middleware to Admin API to fetch correct tenant from signature
  • Update tableManager tests to properly handle tenant tests

Context

Fixes #3692 | RAF-1191

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)

@netlify
Copy link

netlify bot commented Oct 9, 2025

Deploy Preview for brilliant-pasca-3e80ec canceled.

Name Link
🔨 Latest commit a15bde6
🔍 Latest deploy log https://app.netlify.com/projects/brilliant-pasca-3e80ec/deploys/68e9325a41fb0a00083a19fe

@github-actions github-actions bot added type: tests Testing related pkg: backend Changes in the backend package. type: source Changes business logic pkg: auth Changes in the GNAP auth package. labels Oct 9, 2025
@github-actions
Copy link

github-actions bot commented Oct 9, 2025

🚀 Performance Test Results

Test Configuration:

  • VUs: 4
  • Duration: 1m0s

Test Metrics:

  • Requests/s: 43.25
  • Iterations/s: 14.45
  • Failed Requests: 0.00% (0 of 2602)
📜 Logs

> performance@1.0.0 run-tests:testenv /home/runner/work/rafiki/rafiki/test/performance
> ./scripts/run-tests.sh -e test "-k" "-q" "--vus" "4" "--duration" "1m"

Cloud Nine GraphQL API is up: http://localhost:3101/graphql
Cloud Nine Wallet Address is up: http://localhost:3100/
Happy Life Bank Address is up: http://localhost:4100/
cloud-nine-wallet-test-backend already set
cloud-nine-wallet-test-auth already set
happy-life-bank-test-backend already set
happy-life-bank-test-auth already set
     data_received..................: 939 kB 16 kB/s
     data_sent......................: 2.0 MB 33 kB/s
     http_req_blocked...............: avg=7.84µs   min=2.04µs  med=5.38µs   max=2.34ms   p(90)=6.52µs   p(95)=7.01µs  
     http_req_connecting............: avg=1.77µs   min=0s      med=0s       max=2.29ms   p(90)=0s       p(95)=0s      
     http_req_duration..............: avg=91.81ms  min=6.56ms  med=76.4ms   max=581.86ms p(90)=155.34ms p(95)=176.31ms
       { expected_response:true }...: avg=91.81ms  min=6.56ms  med=76.4ms   max=581.86ms p(90)=155.34ms p(95)=176.31ms
     http_req_failed................: 0.00%  ✓ 0         ✗ 2602
     http_req_receiving.............: avg=92.64µs  min=26.63µs med=81.46µs  max=2ms      p(90)=117.41µs p(95)=148.53µs
     http_req_sending...............: avg=38.96µs  min=10.32µs med=28.53µs  max=2.86ms   p(90)=42.05µs  p(95)=57.39µs 
     http_req_tls_handshaking.......: avg=0s       min=0s      med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=91.68ms  min=6.45ms  med=76.28ms  max=581.77ms p(90)=155.23ms p(95)=176.14ms
     http_reqs......................: 2602   43.253748/s
     iteration_duration.............: avg=276.57ms min=171ms   med=264.63ms max=1.08s    p(90)=329.03ms p(95)=357.98ms
     iterations.....................: 869    14.445622/s
     vus............................: 4      min=4       max=4 
     vus_max........................: 4      min=4       max=4 

@mkurapov mkurapov marked this pull request as ready for review October 9, 2025 18:01
@mkurapov mkurapov requested review from BlairCurrey and njlie October 9, 2025 18:02

if (idpConsentUrl || idpSecret) {
await deps.authServiceClient.tenant.update(id, {
apiSecret,
Copy link
Contributor

Choose a reason for hiding this comment

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

This is sort of tangential - dont think there is anything wrong here. But I dont think we're actually able to update tenants from the UI. Are we supposed to be able to?

As-is, it seems like the API secret is only visible to operators. For any tenant (self or non-operators). But its disabled if you're an operator (enabled if you're not, but you cant see it).

Im not really sure what the intended behavior is supposed to be but I dont see any scenario where you can update from the UI.

Copy link
Contributor

@njlie njlie Oct 9, 2025

Choose a reason for hiding this comment

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

I noticed this too. For the information that the auth service already stores, like the idp secret or consent screen URL, any updates are handled initially by the backend service, then propagated through to the auth service. So I think the apiSecret has this same dynamic.

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 its coming back to me... just saw this when trying to update via bruno:

"Operator cannot update apiSecret over admin api"

So working as intended I think.

But this begs the question, what happens if we set the env var secret to different things? I believe they would just be different (or perhaps backed would overwrite what auth does itself if it starts up later). Obviously people should not do that but not sure if there is really a graceful way to warn.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So it looks like the only possibility of updating the apiSecret is if the tenant calls the updateTenant mutation directly via the API, and not the UI. Do you think that is OK behaviour?

If so, then we can keep this as is, and just propagate the apiSecret like the other fields. I also changed the conditional to make sure we get the change across to auth.

Copy link
Contributor

@BlairCurrey BlairCurrey Oct 10, 2025

Choose a reason for hiding this comment

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

So it looks like the only possibility of updating the apiSecret is if the tenant calls the updateTenant mutation directly via the API, and not the UI.

I was able to update in bruno (add in ui, get id, change senderTenantId and backendApiSignatureSecret in bruno config, change id in bruno's "Rafiki Admin API" > "Update tenant" request variables).

After I did, I tried to use the admin ui with the new credentials. I saw the and saw some unauthorized errors. Seems like there is an issue there.

I also saw the same thing when I hastily added in an input for the secret for non-operator tenants.

Do you think that is OK behaviour?

I would expect that they can change it from the UI, if they are allowed to change it at all. Which I think they should.

Copy link
Contributor

@BlairCurrey BlairCurrey Oct 10, 2025

Choose a reason for hiding this comment

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

never mind on part of that, but there is something we need to fix if we add a form input for the api secret for non operators. I added one (and the schema validator), submitted, and got unauthorized error. it did update the secret but the browser session didnt seem to udpate. worked fine when i manually cleared and re-entered. There is code that looks like it should be setting on the session but something is wrong there.

Although its interesting that we have that code to update the session... because as-is the code only shows a disabled input for the api secret if you are the operator. You cant update if you are non-operator, nor see it. So the code to update the session on api secret change would just never run? I think we intended to let the non operator update at one point at least.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just tested as well and yes, I also need to clear the credentials and re-set them manually in Admin UI before it works.

We can put that as an enhancement but I don't see it being something of high priority to fix.

BlairCurrey
BlairCurrey previously approved these changes Oct 9, 2025
Copy link
Contributor

@BlairCurrey BlairCurrey left a comment

Choose a reason for hiding this comment

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

Looks good w/ the caveat in my comment:

But this begs the question, what happens if we set the env var secret to different things? I believe they would just be different (or perhaps backed would overwrite what auth does itself if it starts up later). Obviously people should not do that but not sure if there is really a graceful way to warn.

Not sure if there is much we can do about that.

test('Gets a tenant', async (): Promise<void> => {
const tenant = await Tenant.query().insert({
id: v4(),
apiSecret: v4(),
Copy link
Contributor

Choose a reason for hiding this comment

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

This test is missing an assertion that this value was retrieved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, do we need this get route in the first place?

Copy link
Contributor

Choose a reason for hiding this comment

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

Don't think so. Anything the auth service knows about the tenant is also known by the backend, which already exposes a way to get a 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.

I will do some cleanup in another PR

import * as crypto from 'crypto'
import Koa from 'koa'

function getSignatureParts(signature: string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not relavant to whether or not this gets merged in but I feel as though we are reaching the point where signature verification should be a shared library between the Rafiki services

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, especially now with the cards & POS services

let appContainer: TestContainer
let redis: Redis

describe('admin api signatures', (): void => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Some of these tests don't appear to be replaced elsewhere

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Does it still test stuff like if the signature is too old to be processed or if it has already been processed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, good catch. Adding old signature tests

@mkurapov
Copy link
Contributor Author

mkurapov commented Oct 10, 2025

@BlairCurrey

But this begs the question, what happens if we set the env var secret to different things? I believe they would just be different (or perhaps backed would overwrite what auth does itself if it starts up later). Obviously people should not do that but not sure if there is really a graceful way to warn.

This wouldn't really be the case but you could have different API secrets in auth & backend, and as long as you use the correct API key to call each of the APIs its OK

@BlairCurrey
Copy link
Contributor

BlairCurrey commented Oct 10, 2025

@mkurapov

But this begs the question, what happens if we set the env var secret to different things? I believe they would just be different (or perhaps backed would overwrite what auth does itself if it starts up later). Obviously people should not do that but not sure if there is really a graceful way to warn.

This wouldn't really be the case but you could have different API secrets in auth & backend, and as long as you use the correct API key to call each of the APIs its OK

Yes I think you are right. I was conflating the operator/tenant flows in my head but they are different.

Operator cannot update apiSecret via admin api. They must use environment variables. They may be different and clients can simply use these separate secrets. Fine.

Tenants can update apiSecret via the backend mutation. This propagates to the auth client. They are always the same. Fine.

I was thinking of the scenario where they are set both from the environment variables and the admin api which propagates. But this case isn't possible since the environment variables are for the operator and the operator cannot be updated via the admin api.

@mkurapov mkurapov merged commit 0c08e41 into main Oct 10, 2025
32 of 44 checks passed
@mkurapov mkurapov deleted the max/raf-1191 branch October 10, 2025 18:31
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. 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.

Start storing apiSecret in the auth service & authenticate Admin API requests

4 participants