# Scoutflo Integration SDK

This project is a TypeScript SDK for managing third-party integrations and their authentication lifecycles, supporting multiple providers and authentication strategies.

## Project Structure

- `src/index.ts`: SDK entry point, exposes the `createSDK` function.
- `src/connection-manager.ts`: Core logic for managing and resolving connections.
- `src/providers/`: Provider-specific logic (e.g., `sentry.provider.ts`).
- `src/storage/`: Storage interface and implementations:
  - `mongodb.storage.ts`: MongoDB storage provider
  - `redis.storage.ts`: Redis storage provider
  - `storage.interface.ts`: Storage provider interface
- `src/strategies/`: Authentication strategies:
  - `api-key.strategy.ts`: API Key auth
  - `basic.strategy.ts`: Basic Auth
  - `mapped-credentials.strategy.ts`: provider-mapped static credential auth
  - `oauth.strategy.ts`: reusable OAuth app lifecycle core for provider adapters
- `src/providers/atlassian-oauth.adapter.ts`: Atlassian adapter over the shared OAuth core
- `src/providers/atlassian-oauth.helpers.ts`: Atlassian-specific helper functions for state, scope, cache-key, runtime, and sibling-token handling
- `src/types/`: Shared types and enums
- `usage/my-service.ts`: Example usage of the SDK in a service
- `package.json`: Project metadata, scripts, and dependencies
- `tsconfig.json`: TypeScript compiler configuration

## Setup and Installation

1. **Clone or create the project directory:**
    ```bash
    git clone <repo-url>
    cd scoutflo-integration-sdk
    ```

2. **Install dependencies:**
    ```bash
    npm install
    ```
    This will install TypeScript, Zod, and the example database drivers (MongoDB, Redis, Mongoose).

## Usage

1. **Ensure your MongoDB and/or Redis instances are running** if you are using the provided storage implementations.

2. **Build the project:**
    ```bash
    npm run build
    ```
    This will compile the TypeScript files into JavaScript in the `dist` directory.

3. **Use the SDK in your service:**
    See `usage/my-service.ts` for an example. A minimal example:
    ```typescript
    import { Model } from "mongoose";
    import { createSDK } from "../src";
    import { MongoDBStorage } from "../src/storage/mongodb.storage";
    import { Integration } from "../src/types";

    const model = new Model<Integration>();
    const storage = new MongoDBStorage(model);
    const sdk = createSDK(storage);

    // Create or resolve connections using sdk.createConnection and sdk.resolveConnection
    ```

### Atlassian Jira/Confluence lifecycle

The SDK now includes adapter-backed lifecycle support for Atlassian Jira, JSM, and Confluence.

Current architecture:

1. `OAuthStrategy` owns shared OAuth mechanics such as install URL shell assembly, code exchange, refresh exchange, cache lookup, lock handling, and token invalidation.
2. `AtlassianProvider` remains the public Atlassian facade and still owns the resolved runtime contract.
3. `AtlassianOAuthAdapter` owns Atlassian-specific callback and refresh orchestration.
4. `atlassian-oauth.helpers.ts` owns smaller Atlassian-specific helpers for scope lookup, state shaping, cache-key compatibility, runtime shaping, and sibling refresh-token synchronization.

What is supported:

1. install URL generation for Atlassian OAuth 3LO through the shared OAuth core
2. callback handling that returns a single selected-product integration payload
3. sibling Atlassian integration scope-union support when extending an existing site install
4. install context recovery through encoded OAuth `state` for sibling integration installs
5. Redis-backed short-lived access-token caching with TTL
6. refresh-token based access-token renewal with sibling Atlassian refresh-token synchronization
7. normalized Jira/JSM/Confluence resolved runtime output for caller services

Minimal shape:

```typescript
import Redis from "ioredis";
import { createSDK } from "../src";
import { MongoDBStorage } from "../src/storage/mongodb.storage";
import { RedisStorage } from "../src/storage/redis.storage";

const persistentStorage = new MongoDBStorage(model);
const cacheStorage = new RedisStorage(new Redis(process.env.REDIS_URL));

const sdk = createSDK(persistentStorage, {
  cacheStorage,
  atlassian: {
    clientId: process.env.ATLASSIAN_CLIENT_ID!,
    clientSecret: process.env.ATLASSIAN_CLIENT_SECRET!,
    redirectUri: process.env.ATLASSIAN_REDIRECT_URI!,
    identityResolver: async (userId) => {
      return {
        account_id: "account-id",
        created_by: userId,
      };
    },
  },
});

const install = await sdk.buildInstallUrl({ user_id: "user-123", account_id: "acct-123", provider: "jira" as const });
const callback = await sdk.handleInstallCallback({ code: "oauth-code", state: install.state, provider: "jira" as const });

await sdk.createConnection(callback.integration);

const jiraRuntime = await sdk.resolveConnection("jira-integration-id");
```

Atlassian install behavior in the current V1 flow:

- scope union lookup runs only for Atlassian sibling providers (`jira`, `confluence`, `jsm`)
- sibling scope lookup is account-scoped via `account_id`
- refresh-token rotation is synchronized across Atlassian sibling integrations for the same Scoutflo `account_id`
- non-Atlassian integrations do not trigger Atlassian scope lookup
- Redis token cache key format is `integration:{provider}:{integration_id}:app_user:{account_id}:access_token`
- Redis refresh lock key format is `integration:{provider}:{integration_id}:app_user:{account_id}:refresh_lock`
- `resolveConnection()` still returns the Atlassian-specific runtime shape from `AtlassianProvider`

IMPORTANT V1 NOTE:

- callback now fails fast when Atlassian returns multiple accessible resources.
- SDK throws `atlassian_site_selection_required` and does not silently choose `accessible-resources[0]`.
- product warning: Atlassian multi-site selection UX/API is still pending and must be implemented before broader rollout.
- backlog item (mandatory): add explicit site-selection contract and persistence rules for multi-site callbacks.

OAuth callback-state security deferral (explicit):

- accepted risk: callback `state` is used for context recovery, but it is not yet a signed, replay-protected integrity boundary.
- **do not treat as secure CSRF-grade state yet.**
- why deferred: V1 prioritized lifecycle migration and blast-radius control across SDK, Gateway, and Voyager.
- owner: Platform Integrations (SDK + Gateway joint ownership)
- target version/date: SDK `2.0.0` by **April 30, 2026**
- mandatory V2 work: signed + replay-resistant state integrity, deterministic reject semantics, and negative tests for tampered/replayed/mismatched callback state.

Important notes:

1. `handleInstallCallback(...)` returns the selected Atlassian integration payload but does not persist it automatically.
2. `cacheStorage` is required when Atlassian lifecycle support is enabled.
3. access tokens are cached in Redis per integration, while refresh tokens remain in persistent integration storage.
4. when a site already has sibling Atlassian integrations, the SDK can extend requested scopes and synchronize rotated refresh tokens across those sibling Atlassian integrations.

## Versioning Rule (Documented Policy)

Current decision:
- keep current semver line for this controlled rollout (no immediate major bump required right now).

Before wider/shared adoption:
- either bump major to `2.0.0`, or
- make the interface fully backward-compatible and document that compatibility explicitly.

Do not widen adoption without one of the two conditions above being completed.

## Rollout Checks Before Preprod/Prod

Run and capture evidence for all three paths before promoting:

1. Slack install/callback path:
   - `npm run test:slack-lifecycle`
2. Slack token refresh path:
   - `npm run trace:slack-requested-flow`
3. Atlassian install/callback happy path (current assumptions):
   - `npm run test:atlassian-lifecycle`

## Customization

- **Storage:** Implement the `IStorageProvider` interface in `src/storage/storage.interface.ts` to connect to your actual data store (e.g., a specific MongoDB database, a different Redis configuration, or another technology).
- **Providers:** Add new provider logic in `src/providers/` and update the `ConnectionManager` to support them.
- **Authentication Strategies:** Add or modify strategies in `src/strategies/` for different authentication methods.
- **Types:** Extend or modify types in `src/types/` as needed for your integrations.

## Scripts

- `npm run build`: Compile TypeScript to JavaScript in `dist/`
- `npm publish`: Publish the package (private)

## Dependencies

- `typescript`, `zod`, `ioredis`, `mongodb`, `mongoose`, `redis`

---

For more details, see the code in the respective directories and the example in `usage/my-service.ts`.
