# SpendStream Security

Protecting your financial data is foundational to SpendStream. This page describes the security measures we have in place to keep your Xero data safe.

## Data encryption

All sensitive data stored by SpendStream is encrypted at rest using AES-256-GCM, an industry-standard authenticated encryption algorithm. This includes your Xero OAuth access tokens, refresh tokens, and the full transaction data we sync from Xero.

- Every encryption operation uses a unique, randomly generated initialization vector, ensuring that identical data produces different ciphertext every time.
- Separate encryption keys are used for OAuth tokens and transaction data.
- Encryption keys are imported as non-extractable, meaning the key material cannot be read back from memory by any application code.

## Authentication and access control

SpendStream uses Xero OAuth 2.0 for user authentication. There are no passwords to manage — your identity is verified through Xero’s own secure login process.

- A pre-authentication gate sits in front of the entire application, requiring valid credentials before any page or API endpoint can be reached.
- Route protection is enforced at multiple layers — middleware, page layouts, and individual data functions — so that no page or API can be accessed without a valid session.
- Role-based access controls ensure that sensitive actions, such as inviting team members, can only be performed by tenant owners.
- Every data query is scoped to the authenticated user’s tenant, preventing any cross-tenant data access.

## Infrastructure

SpendStream runs entirely on [Cloudflare Workers](https://www.cloudflare.com/learning/serverless/what-is-workers/), a serverless edge computing platform. There are no traditional servers, virtual machines, or containers to maintain or patch.

- All server-side code is enforced to run only in server environments — it cannot be loaded or executed in a browser, preventing accidental exposure of secrets, encryption keys, or database queries.
- The background worker that syncs your Xero data has no public HTTP endpoint. It operates exclusively through Cloudflare’s internal cron triggers and queues, giving it zero external attack surface.
- Worker preview URLs and public development endpoints are disabled, ensuring no unintended public access.

## Session security

Sessions are managed with cryptographically random IDs and stored in Cloudflare KV with an automatic expiry, ensuring stale sessions are cleaned up without manual intervention.

- Session cookies are set with HttpOnly (inaccessible to JavaScript), Secure (HTTPS only), and SameSite=Lax (resistant to cross-site request forgery) attributes.
- Sessions expire after 24 hours of inactivity, with a sliding window that extends the expiry on each authenticated request.
- On logout or session invalidation, the server session is deleted from storage and the cookie is immediately cleared.

## Data isolation

Tenant isolation is enforced at every layer of the application. Each organization’s data is logically separated, and all database queries include tenant scoping tied to the authenticated session.

- Every database query uses parameterized statements, eliminating any risk of SQL injection.
- Database constraints enforce data integrity — including unique indexes, foreign key relationships, and status enums that prevent invalid state transitions.
- All IDs are generated with semantic prefixes (for example, user IDs, tenant IDs, and transaction IDs each have distinct prefixes), preventing accidental misuse of one type of identifier in place of another.
- Deactivated users, connections, and rules are excluded from all queries, ensuring revoked access takes immediate effect.

## Secrets management

Encryption keys, OAuth client secrets, and API keys are never committed to source code or configuration files.

- Production secrets stored as Cloudflare Worker secrets, which are encrypted at rest by Cloudflare.
- Local development secrets are stored in gitignored files that are never committed to the repository.
- Configuration files contain only non-sensitive values such as public OAuth client IDs and environment labels.

## Rate limiting and reliability

SpendStream implements per-tenant rate limiting for all outbound API calls to Xero, ensuring that no single tenant’s sync activity can degrade performance for another.

- Rate limit slots are automatically released after a timeout, preventing deadlocks from failed or interrupted requests.
- All background processing uses queues with dead letter queues and bounded retry limits, so failed messages are not retried indefinitely.
- Workflow steps have explicit timeouts, preventing runaway operations from consuming resources.
- Sync operations use optimistic concurrency control, preventing duplicate processing when multiple workers attempt the same task.

## OAuth and invitation security

The Xero OAuth flow follows authorization code grant best practices, and team invitations are designed to prevent unauthorized access.

- The OAuth state parameter is single-use, time-limited, and validated and consumed on callback — preventing CSRF attacks during the authorization flow.
- OAuth redirect URIs are constructed from server-side configuration only, preventing open redirect attacks.
- Team invitations expire after seven days and cannot be accepted twice. The Xero account email must match the invited email address.
- Duplicate pending invitations for the same email and tenant are prevented at the database level.

## Report a vulnerability

If you believe you have found a security vulnerability in SpendStream, please report it responsibly by emailing [support@spendstream.app](mailto:support@spendstream.app). We take all reports seriously and will respond as quickly as possible.

We ask that you do not publicly disclose the vulnerability until we have had a chance to investigate and resolve it. Please include enough detail to reproduce the issue, and we will keep you informed of our progress.