Skip to main content
OAuth token management in PayPulse Cloud is handled by oauth_utils.py and secretsmanager_utils.py in the common Lambda layer. The design keeps token handling centralized so that every Lambda function that needs Gmail access follows the same refresh-and-validate path.

Token storage pattern

Tokens are stored per user in AWS Secrets Manager using the key pattern:
Where {user_id} is the internal PayPulse user ID (a UUID prefixed with user_). Each secret is a JSON object containing the following fields: The prepare_oauth_secret_data function in oauth_utils.py builds this structure before storage:
oauth_utils.py

Token expiry check

Before a token is used, is_token_expired compares the stored expires_at timestamp against the current UTC time:
oauth_utils.py
If expires_at is absent (for example, in older records), the function returns False and lets the token attempt proceed.

Automatic token refresh

Token refresh happens inside create_gmail_service in gmail_api_utils.py. The function checks whether the token has expired or will expire within the next 5 minutes, and refreshes proactively if so:
gmail_api_utils.py
After a successful refresh, update_oauth_tokens writes the new access_token and updated expires_at back to Secrets Manager while preserving the existing scope and Google user info.

Account switch detection

When the iOS app sends new tokens, the backend compares the google_user_id in the incoming token against the one already stored for the user:
oauth_utils.py
If is_account_switch is True, the new tokens still overwrite the old ones — the check is informational. The store-tokens endpoint includes account_switch in its response so the iOS app can display a warning to the user.

Token validation flow

1

Retrieve tokens from Secrets Manager

Lambda calls get_oauth_tokens(user_id, region), which reads the gmail/user/{user_id} secret. If expires_at is in the past, the function adds is_expired: True to the returned dict.
2

Create Gmail service (refresh if needed)

create_gmail_service is called with the stored tokens. It checks whether the token expires within 5 minutes and refreshes proactively using the refresh token.
3

Update Secrets Manager after refresh

If a refresh occurred, update_oauth_tokens writes the new access token and expiry timestamp back to Secrets Manager, preserving the existing Google user info and scope.
4

Use the access token

The Gmail API service object is returned and used to search for or download emails. The service holds valid credentials for the duration of the Lambda invocation.
If a refresh token has expired or been revoked by the user, create_gmail_service raises a RefreshTokenExpiredError and deletes the stale tokens from Secrets Manager. The user must reconnect their Gmail account from the iOS app.