CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-slack--oauth

Official library for interacting with Slack's OAuth endpoints

Pending
Overview
Eval results
Files

installation-storage.mddocs/

Installation Storage

Flexible storage system for persisting and retrieving Slack app installation data including tokens, team information, user details, and enterprise organization data.

Capabilities

InstallationStore Interface

Interface for storing and retrieving installation data with support for multiple storage backends.

/**
 * Interface for storing and retrieving installation data
 */
interface InstallationStore {
  /**
   * Store installation data after successful OAuth flow
   * @param installation - Complete installation data from OAuth response
   * @param logger - Optional logger for debugging
   */
  storeInstallation<AuthVersion extends "v1" | "v2">(
    installation: Installation<AuthVersion, boolean>,
    logger?: Logger
  ): Promise<void>;

  /**
   * Fetch installation data for API authorization
   * @param query - Query parameters to identify installation
   * @param logger - Optional logger for debugging
   * @returns Installation data including tokens and metadata
   */
  fetchInstallation(
    query: InstallationQuery<boolean>,
    logger?: Logger
  ): Promise<Installation<"v1" | "v2", boolean>>;

  /**
   * Delete installation data (optional method)
   * @param query - Query parameters to identify installation to delete
   * @param logger - Optional logger for debugging
   */
  deleteInstallation?(
    query: InstallationQuery<boolean>,
    logger?: Logger
  ): Promise<void>;
}

Installation Data Types

Complete installation data structure supporting both OAuth versions and enterprise installations.

/**
 * Complete installation data structure
 */
interface Installation<
  AuthVersion extends "v1" | "v2" = "v1" | "v2",
  IsEnterpriseInstall extends boolean = boolean
> {
  /** Team/workspace information (undefined for enterprise-wide installs) */
  team: IsEnterpriseInstall extends true
    ? undefined
    : {
        id: string;
        name?: string;
      };

  /** Enterprise organization information (when applicable) */
  enterprise: IsEnterpriseInstall extends true ? EnterpriseInfo : EnterpriseInfo | undefined;

  /** User installation data */
  user: {
    token: AuthVersion extends "v1" ? string : string | undefined;
    refreshToken?: AuthVersion extends "v1" ? never : string | undefined;
    expiresAt?: AuthVersion extends "v1" ? never : number | undefined;
    scopes: AuthVersion extends "v1" ? string[] : string[] | undefined;
    id: string;
  };

  /** Bot installation data */
  bot?: {
    token: string;
    refreshToken?: string;
    expiresAt?: number;
    scopes: string[];
    id: string;
    userId: string;
  };

  /** Incoming webhook configuration */
  incomingWebhook?: {
    url: string;
    channel?: string;
    channelId?: string;
    configurationUrl?: string;
  };

  /** App ID (OAuth v2 only) */
  appId?: AuthVersion extends "v2" ? string : undefined;

  /** Token type when bot user exists */
  tokenType?: "bot";

  /** Enterprise organization URL (OAuth v2 only) */
  enterpriseUrl?: AuthVersion extends "v2" ? string : undefined;

  /** Whether this is an enterprise-wide installation */
  isEnterpriseInstall?: IsEnterpriseInstall;

  /** OAuth version used for this installation */
  authVersion?: AuthVersion;

  /** Metadata passed through OAuth flow */
  metadata?: string;
}

/**
 * Enterprise organization information
 */
interface EnterpriseInfo {
  id: string;
  name?: string;
}

/**
 * Type alias for enterprise organization installation
 */
type OrgInstallation = Installation<"v2", true>;

InstallationQuery Types

Query parameters for fetching installation data.

/**
 * Query parameters for fetching installation data
 */
interface InstallationQuery<isEnterpriseInstall extends boolean = boolean> {
  /** Team ID for workspace-specific queries (required for workspace installs) */
  teamId: isEnterpriseInstall extends false ? string : undefined;
  /** Enterprise ID for organization queries (required for enterprise installs) */
  enterpriseId: isEnterpriseInstall extends true ? string : string | undefined;
  /** User ID for user-specific queries */
  userId?: string;
  /** Conversation ID for context-specific queries */
  conversationId?: string;
  /** Whether this is an enterprise-wide installation query (required) */
  isEnterpriseInstall: isEnterpriseInstall;
}

/**
 * Type alias for enterprise organization installation queries
 */
type OrgInstallationQuery = InstallationQuery<true>;

Built-in Installation Stores

Ready-to-use implementations of the InstallationStore interface.

MemoryInstallationStore

/**
 * In-memory installation store for development and testing
 * WARNING: Data is lost when process restarts
 */
class MemoryInstallationStore implements InstallationStore {
  constructor();
  
  async storeInstallation<AuthVersion extends "v1" | "v2">(
    installation: Installation<AuthVersion, boolean>,
    logger?: Logger
  ): Promise<void>;
  
  async fetchInstallation(
    query: InstallationQuery<boolean>,
    logger?: Logger
  ): Promise<Installation<"v1" | "v2", boolean>>;
  
  async deleteInstallation(
    query: InstallationQuery<boolean>,
    logger?: Logger
  ): Promise<void>;
}

FileInstallationStore

/**
 * File-based installation store for simple persistent storage
 */
class FileInstallationStore implements InstallationStore {
  /**
   * @param options - Configuration options for file storage
   */
  constructor(options?: FileInstallationOptions);
  
  async storeInstallation<AuthVersion extends "v1" | "v2">(
    installation: Installation<AuthVersion, boolean>,
    logger?: Logger
  ): Promise<void>;
  
  async fetchInstallation(
    query: InstallationQuery<boolean>,
    logger?: Logger
  ): Promise<Installation<"v1" | "v2", boolean>>;
  
  async deleteInstallation(
    query: InstallationQuery<boolean>,
    logger?: Logger
  ): Promise<void>;
}

/**
 * Configuration options for FileInstallationStore
 */
interface FileInstallationOptions {
  /** Base directory to store installation files */
  baseDir?: string;
  /** Whether to store historical installation data */
  historicalDataEnabled?: boolean;
  /** Client ID for organizing installations by app */
  clientId?: string;
}

Usage Examples:

import { 
  InstallProvider, 
  MemoryInstallationStore, 
  FileInstallationStore,
  Installation,
  InstallationQuery 
} from "@slack/oauth";

// Using memory store (development)
const memoryStore = new MemoryInstallationStore();
const installer = new InstallProvider({
  clientId: process.env.SLACK_CLIENT_ID!,
  clientSecret: process.env.SLACK_CLIENT_SECRET!,
  stateSecret: "secret",
  installationStore: memoryStore,
});

// Using file store (simple persistence)  
const fileStore = new FileInstallationStore("./slack-installations");
const installer2 = new InstallProvider({
  clientId: process.env.SLACK_CLIENT_ID!,
  clientSecret: process.env.SLACK_CLIENT_SECRET!,
  stateSecret: "secret", 
  installationStore: fileStore,
});

// Custom installation store implementation
class DatabaseInstallationStore implements InstallationStore {
  async storeInstallation(installation: Installation) {
    // Store in database
    await db.installations.create({
      teamId: installation.team?.id,
      enterpriseId: installation.enterprise?.id,
      botToken: installation.bot?.token,
      userToken: installation.user?.token,
      // ... other fields
    });
  }

  async fetchInstallation(query: InstallationQuery) {
    // Fetch from database
    const record = await db.installations.findOne({
      teamId: query.teamId,
      enterpriseId: query.enterpriseId,
    });
    
    if (!record) {
      throw new Error("Installation not found");
    }

    return {
      team: record.teamId ? { id: record.teamId } : undefined,
      enterprise: record.enterpriseId ? { id: record.enterpriseId } : undefined,
      bot: record.botToken ? {
        token: record.botToken,
        scopes: record.botScopes || [],
        id: record.botId,
        userId: record.botUserId,
      } : undefined,
      user: record.userToken ? {
        token: record.userToken,
        scopes: record.userScopes || [],
        id: record.userId,
      } : undefined,
    };
  }

  async deleteInstallation(query: InstallationQuery) {
    await db.installations.deleteOne({
      teamId: query.teamId,
      enterpriseId: query.enterpriseId,
    });
  }
}

// Direct installation store usage
const query: InstallationQuery = {
  teamId: "T1234567890",
  isEnterpriseInstall: false,
};

const installation = await installer.installationStore.fetchInstallation(query);
console.log("Bot token:", installation.bot?.token);

Install with Tessl CLI

npx tessl i tessl/npm-slack--oauth

docs

error-handling.md

index.md

installation-storage.md

oauth-flow.md

state-management.md

tile.json