Skip to content

[SCRUM-622] refactor(auth): use Powertools secret retrieval for tokens from secrets manager - #212

Open
sh1un wants to merge 2 commits into
devfrom
feature/SCRUM-622-refactor-secrets-manager-code-replaced-by-powertools
Open

[SCRUM-622] refactor(auth): use Powertools secret retrieval for tokens from secrets manager#212
sh1un wants to merge 2 commits into
devfrom
feature/SCRUM-622-refactor-secrets-manager-code-replaced-by-powertools

Conversation

@sh1un

@sh1un sh1un commented Jun 27, 2026

Copy link
Copy Markdown
Member

Description

This PR refactors all AWS Secrets Manager interactions across webhook_service, auth_service, and rsvp_service to use AWS Lambda Powertools Parameters (v3.30.0) instead of raw boto3 calls.

Motivation

Previously, each Lambda manually managed its own boto3.client("secretsmanager") instance, handled JSON parsing, and in some cases implemented ad-hoc in-memory caching via global variables. This resulted in repeated boilerplate and inconsistency across services.

Powertools Parameters provides:

  • Built-in in-process caching (default 5-min TTL) — reduces Secrets Manager API calls per warm invocation
  • Automatic JSON transform via transform="json" — eliminates manual json.loads(response["SecretString"])
  • Consistent error type (GetParameterError) — makes exception handling predictable

Changes per service

webhook_service/trigger_webhook/

  • utils.py: Removed SecretsManager class; replaced with module-level get_access_token() using get_secret(..., transform="json", force_fetch=True). force_fetch=True is intentional — access tokens are actively written by auth_service and must always be fetched fresh to avoid serving a stale token after rotation.
  • email_service.py: Updated to import and call the new get_access_token() function directly; removed EmailService.__init__ which previously existed solely to instantiate SecretsManager.

auth_service/refresh_service_accounts_token/

  • lambda_function.py: Replaced secrets_client.get_secret_value() on the password read path with get_secret(..., transform="json"). The update_secret write path remains on boto3 — Powertools Parameters is read-only. Added GetParameterError to the except clause to properly catch Powertools-raised exceptions alongside ClientError.

rsvp_service/get_rsvp_status/ and rsvp_service/update_rsvp/

  • jwt_util.py (both): Removed manual boto3.client("secretsmanager") instantiation and the global _jwt_secret_cache variable. Replaced with get_secret(jwt_secret_arn) — Powertools handles caching transparently with the same effective behavior.

Type of Change

  • 🐛 Bug Fix (fixes an issue)
  • ✨ New Feature (introduces a new feature)
  • 💄 UI/UX (improves interface or user experience)
  • 🔨 Refactor (code refactoring)
  • 📝 Documentation (updates documentation)
  • 🔧 Configuration (changes configuration)
  • 🚀 Performance (improves performance)
  • ✅ Test (related to tests)
  • 🔒 Security (addresses security concerns)

Related Issues

SCRUM-622

Testing

  1. Deploy to the preview environment via the CI/CD pipeline.
  2. Trigger the webhook_service with a valid SurveyCake webhook payload and verify the email is sent successfully (confirms get_access_token fetches a valid token).
  3. Manually invoke the refresh_service_accounts_token Lambda and verify the response shows "status": "success" for both surveycake and postman accounts (confirms password retrieval and token update still work).
  4. Call GET /rsvp/{run_id_participant_id}/status and PATCH /rsvp/{run_id_participant_id} with a valid JWT and verify 200 responses (confirms JWT secret retrieval via Powertools).
  5. Postman collection tests run automatically in CI after deployment.

Screenshots/Videos

N/A — backend refactor with no API contract changes.

Checklist

  • I have tested these changes.
  • I have updated the relevant documentation.
  • My code adheres to the project's code standards.
  • I have addressed all console warnings/errors.
  • I have removed all console.log or print() statements.
  • I have added necessary test cases.
  • All existing tests pass.

Additional Notes

  • The update_secret call in auth_service intentionally remains on raw boto3 — Powertools Parameters does not support write operations.
  • force_fetch=True in webhook_service bypasses the Powertools cache for access tokens only. This is necessary because these tokens are short-lived credentials written by a separate Lambda (auth_service) and must be read fresh on every invocation.
  • All secret path patterns are unchanged — this is a pure implementation swap with no AWS resource modifications.

@sh1un sh1un self-assigned this Jun 27, 2026
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

[Auth Service] Preview Deployment Status

Service Environment Commit Status Details
Auth Service Preview 40ab07 Successfully deployed

Test Results

Test Suite Status Report
API Tests Download Report

🔍 Details

  • Run ID: 28321059764
  • Triggered by: @sh1un
  • Environment: Preview

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

[RSVP Service] Preview Deployment Status

Service Environment Commit Status Details
RSVP Service Preview 40ab07 Successfully deployed

Test Results

Test Suite Status Report
API Tests Download Report

🔍 Details

  • Run ID: 28321059768
  • Triggered by: @sh1un
  • Environment: Preview

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

[Webhook Service] Preview Deployment Status

Service Environment Commit Status Details
Webhook Service Preview 40ab07 Successfully deployed

Test Results

Test Suite Status Report
API Tests Download Report

🔍 Details

  • Run ID: 28321059777
  • Triggered by: @sh1un
  • Environment: Preview

@sh1un sh1un changed the title refactor(auth): use Powertools secret retrieval for tokens from secre… [SCRUM-622] refactor(auth): use Powertools secret retrieval for tokens from secrets manager Jun 27, 2026
Comment on lines 42 to 49
def _get_jwt_secret():
global _jwt_secret_cache

if _jwt_secret_cache is not None:
return _jwt_secret_cache

jwt_secret_arn = os.getenv("JWT_SECRET_ARN")
if not jwt_secret_arn:
raise RuntimeError("JWT_SECRET_ARN environment variable is not set")

response = _secretsmanager_client.get_secret_value(SecretId=jwt_secret_arn)
secret_string = response.get("SecretString")
if not secret_string:
secret = get_secret(jwt_secret_arn)
if not secret:
raise RuntimeError("JWT secret is empty")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Worth adding a logger for get_secret to get more info when there is an error:

sth like this:

    try:
        secret = get_secret(jwt_secret_arn)
    except Exception as e:
        logger.error("Failed to retrieve JWT secret: %s", e)
        raise

Comment on lines 42 to +51
def _get_jwt_secret():
global _jwt_secret_cache

if _jwt_secret_cache is not None:
return _jwt_secret_cache

jwt_secret_arn = os.getenv("JWT_SECRET_ARN")
if not jwt_secret_arn:
raise RuntimeError("JWT_SECRET_ARN environment variable is not set")

response = _secretsmanager_client.get_secret_value(SecretId=jwt_secret_arn)
secret_string = response.get("SecretString")
if not secret_string:
secret = get_secret(jwt_secret_arn)
if not secret:
raise RuntimeError("JWT secret is empty")

_jwt_secret_cache = secret_string
return _jwt_secret_cache
return secret

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here:

Worth adding a logger for get_secret to get more info when there is an error:

sth like this:

    try:
        secret = get_secret(jwt_secret_arn)
    except Exception as e:
        logger.error("Failed to retrieve JWT secret: %s", e)
        raise

@sh1un
sh1un requested a review from chungchihhan June 28, 2026 11:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants