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: use redis as backend for koa-session #2693

Merged
merged 10 commits into from May 13, 2024
Merged

Conversation

BlairCurrey
Copy link
Contributor

@BlairCurrey BlairCurrey commented May 1, 2024

Changes proposed in this pull request

  • use redis for koa-session backend

Context

fixes #2673

corresponding helm-chart issue created as well: interledger/helm-charts#38

Checklist

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

@github-actions github-actions bot added type: tests Testing related type: source Changes business logic pkg: auth Changes in the GNAP auth package. labels May 1, 2024
Copy link

netlify bot commented May 1, 2024

Deploy Preview for brilliant-pasca-3e80ec ready!

Name Link
🔨 Latest commit e2c92c1
🔍 Latest deploy log https://app.netlify.com/sites/brilliant-pasca-3e80ec/deploys/663bbbd46bed690008159496
😎 Deploy Preview https://deploy-preview-2693--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 pkg: documentation Changes in the documentation package. type: documentation Improvements or additions to documentation labels May 1, 2024
@BlairCurrey BlairCurrey requested a review from golobitch May 1, 2024 14:28
Copy link
Contributor Author

@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.

I believe I will need to update this repo's helm values.yaml after they are added to our helm chart repo. Assuming it mirrors our backend, I believe we just need to add the same redis config that we have for rafiki-backend.

@golobitch
Copy link
Collaborator

I already prepared PR for helm charts. So that we will be able to merge it after this one is merged.

@@ -342,12 +344,26 @@ export class App {

koa.use(cors())
koa.keys = [this.config.cookieKey]

const redis = await this.container.use('redis')
koa.use(
session(
{
key: 'sessionId',
Copy link
Contributor

Choose a reason for hiding this comment

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

How does this correspond to what is stored inside redis? The key I see in redis is a UUID -> where does "sessionId" come into play?

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 is the original configuration and from what I can tell the key name does not correspond to anything in redis, it's just for the http cookie returned to the client:

image

The value there would be the UUID key you see in redis.

return data ? JSON.parse(data) : null
},
async set(key, session) {
await redis.set(key, JSON.stringify(session))
Copy link
Contributor

Choose a reason for hiding this comment

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

How does expiry/TTL of the session get saved in redis?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Testing the flow showed that it was being expired but it looks like it was just the http cookie. Redis key was not actually expiring. Changed it to make the record expire in redis. 05fcb02

Copy link
Collaborator

Choose a reason for hiding this comment

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

This does not set expiry on redis key. With set command, that would be done either via EX options of set command or through some expiry command. If we know the TTL of the session I would rewrite this code to be like this:

const op = redis.multi()
op.hmset(key, session)
op.expire(key, expirationInMilliseconds)

await op.exec()

@@ -85,8 +85,10 @@ services:
TRUST_PROXY: ${TRUST_PROXY}
AUTH_DATABASE_URL: postgresql://cloud_nine_wallet_auth:cloud_nine_wallet_auth@shared-database/cloud_nine_wallet_auth
AUTH_SERVER_DOMAIN: ${CLOUD_NINE_AUTH_SERVER_DOMAIN:-http://localhost:3006}
REDIS_URL: redis://shared-redis:6379/0
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to distinguish this from the cloud-nine-backend one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good question. Not sure how much it matters TBH but I suppose its consistent with what we're currently doing, so I changed it to:

  • c9 backend: 0
  • c9 auth: 1
  • hlb backend: 2
  • hlb auth: 3

const data = await redis.get(key)
return data ? JSON.parse(data) : null
},
async set(key, session) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I see there is a third parameter maxAge possible in this function. Do we want to include that here?

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 assumed I would use that then purposefully did not after seeing it could be undefined or "session" depending on how we actually configure it. The type here doesnt account for how we actually configure it, so I didn't want to handle scenarios like session or undefined (which should never happen) when we know we want to expire it. I suppose an alternative would be handling session and undefined by defaulting back to expireInMs, but I figured it was just simpler to control both the session max age and redis ttl from 1 variable. If we did handle it by defaulting back to expireInMs then it would basically be: set it to maxAge when maxAge is a number (which will match our configuration) or set it to match our configuration.

Does that make sense? Open to feedback here if anyone sees a better way.

// Add a delay to cookie age to ensure redis record expires after cookie
const expireInMs = maxAgeMs + 10 * 1000
await redis.psetex(key, expireInMs, JSON.stringify(session))
},
Copy link
Contributor

@mkurapov mkurapov May 3, 2024

Choose a reason for hiding this comment

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

Suggested change
},
await redis.set(key, JSON.stringify(session), 'PX', expireInMs)

I think psetex is deprecated based on the docs. (sorry for the odd diff)

Copy link
Collaborator

Choose a reason for hiding this comment

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

psetex is not deprecated per my knowledge, but it is not wise to use it, since it will be deprecated and removed.
From the docs:

Note: Since the SET command options can replace SETNX, SETEX, PSETEX, GETSET, it is possible that in future versions of Redis these commands will be deprecated and finally removed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

BTW, no need to do stringify on session object. Instead, you can just use hmset(key, session)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to hmset as suggested except I used hset. apparently hmset, like psetex is marked as deprecated in redis docs https://redis.io/docs/latest/commands/hmset/

dda0079

Comment on lines +362 to +363
// Add a delay to cookie age to ensure redis record expires after cookie
const expireInMs = maxAgeMs + 10 * 1000
Copy link
Contributor

Choose a reason for hiding this comment

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

If I understand correctly, this is to possibly allow the "final" request with the to-be-expired cookie parameter to complete before deleting it in redis?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah basically. could potentially be shorter I guess - just needs to be long enough to cover the time between finding the non-expired koa-session in the server and retrieving the redis record. no harm in having a little extra buffer though I figure.

@BlairCurrey BlairCurrey requested a review from mkurapov May 6, 2024 15:57
Copy link
Contributor

@njlie njlie left a comment

Choose a reason for hiding this comment

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

LGTM

@BlairCurrey BlairCurrey merged commit 1a0d384 into main May 13, 2024
44 checks passed
@BlairCurrey BlairCurrey deleted the bc/2673/koa-session-redis branch May 13, 2024 16:15
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: documentation Changes in the documentation package. type: documentation Improvements or additions to documentation type: source Changes business logic type: tests Testing related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Move auth's koa session state out of service
4 participants