or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdindex.mdinstallation-storage.mdoauth-flow.mdstate-management.md

installation-storage.mddocs/

0

# Installation Storage

1

2

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

3

4

## Capabilities

5

6

### InstallationStore Interface

7

8

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

9

10

```typescript { .api }

11

/**

12

* Interface for storing and retrieving installation data

13

*/

14

interface InstallationStore {

15

/**

16

* Store installation data after successful OAuth flow

17

* @param installation - Complete installation data from OAuth response

18

* @param logger - Optional logger for debugging

19

*/

20

storeInstallation<AuthVersion extends "v1" | "v2">(

21

installation: Installation<AuthVersion, boolean>,

22

logger?: Logger

23

): Promise<void>;

24

25

/**

26

* Fetch installation data for API authorization

27

* @param query - Query parameters to identify installation

28

* @param logger - Optional logger for debugging

29

* @returns Installation data including tokens and metadata

30

*/

31

fetchInstallation(

32

query: InstallationQuery<boolean>,

33

logger?: Logger

34

): Promise<Installation<"v1" | "v2", boolean>>;

35

36

/**

37

* Delete installation data (optional method)

38

* @param query - Query parameters to identify installation to delete

39

* @param logger - Optional logger for debugging

40

*/

41

deleteInstallation?(

42

query: InstallationQuery<boolean>,

43

logger?: Logger

44

): Promise<void>;

45

}

46

```

47

48

### Installation Data Types

49

50

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

51

52

```typescript { .api }

53

/**

54

* Complete installation data structure

55

*/

56

interface Installation<

57

AuthVersion extends "v1" | "v2" = "v1" | "v2",

58

IsEnterpriseInstall extends boolean = boolean

59

> {

60

/** Team/workspace information (undefined for enterprise-wide installs) */

61

team: IsEnterpriseInstall extends true

62

? undefined

63

: {

64

id: string;

65

name?: string;

66

};

67

68

/** Enterprise organization information (when applicable) */

69

enterprise: IsEnterpriseInstall extends true ? EnterpriseInfo : EnterpriseInfo | undefined;

70

71

/** User installation data */

72

user: {

73

token: AuthVersion extends "v1" ? string : string | undefined;

74

refreshToken?: AuthVersion extends "v1" ? never : string | undefined;

75

expiresAt?: AuthVersion extends "v1" ? never : number | undefined;

76

scopes: AuthVersion extends "v1" ? string[] : string[] | undefined;

77

id: string;

78

};

79

80

/** Bot installation data */

81

bot?: {

82

token: string;

83

refreshToken?: string;

84

expiresAt?: number;

85

scopes: string[];

86

id: string;

87

userId: string;

88

};

89

90

/** Incoming webhook configuration */

91

incomingWebhook?: {

92

url: string;

93

channel?: string;

94

channelId?: string;

95

configurationUrl?: string;

96

};

97

98

/** App ID (OAuth v2 only) */

99

appId?: AuthVersion extends "v2" ? string : undefined;

100

101

/** Token type when bot user exists */

102

tokenType?: "bot";

103

104

/** Enterprise organization URL (OAuth v2 only) */

105

enterpriseUrl?: AuthVersion extends "v2" ? string : undefined;

106

107

/** Whether this is an enterprise-wide installation */

108

isEnterpriseInstall?: IsEnterpriseInstall;

109

110

/** OAuth version used for this installation */

111

authVersion?: AuthVersion;

112

113

/** Metadata passed through OAuth flow */

114

metadata?: string;

115

}

116

117

/**

118

* Enterprise organization information

119

*/

120

interface EnterpriseInfo {

121

id: string;

122

name?: string;

123

}

124

125

/**

126

* Type alias for enterprise organization installation

127

*/

128

type OrgInstallation = Installation<"v2", true>;

129

```

130

131

### InstallationQuery Types

132

133

Query parameters for fetching installation data.

134

135

```typescript { .api }

136

/**

137

* Query parameters for fetching installation data

138

*/

139

interface InstallationQuery<isEnterpriseInstall extends boolean = boolean> {

140

/** Team ID for workspace-specific queries (required for workspace installs) */

141

teamId: isEnterpriseInstall extends false ? string : undefined;

142

/** Enterprise ID for organization queries (required for enterprise installs) */

143

enterpriseId: isEnterpriseInstall extends true ? string : string | undefined;

144

/** User ID for user-specific queries */

145

userId?: string;

146

/** Conversation ID for context-specific queries */

147

conversationId?: string;

148

/** Whether this is an enterprise-wide installation query (required) */

149

isEnterpriseInstall: isEnterpriseInstall;

150

}

151

152

/**

153

* Type alias for enterprise organization installation queries

154

*/

155

type OrgInstallationQuery = InstallationQuery<true>;

156

```

157

158

### Built-in Installation Stores

159

160

Ready-to-use implementations of the InstallationStore interface.

161

162

#### MemoryInstallationStore

163

164

```typescript { .api }

165

/**

166

* In-memory installation store for development and testing

167

* WARNING: Data is lost when process restarts

168

*/

169

class MemoryInstallationStore implements InstallationStore {

170

constructor();

171

172

async storeInstallation<AuthVersion extends "v1" | "v2">(

173

installation: Installation<AuthVersion, boolean>,

174

logger?: Logger

175

): Promise<void>;

176

177

async fetchInstallation(

178

query: InstallationQuery<boolean>,

179

logger?: Logger

180

): Promise<Installation<"v1" | "v2", boolean>>;

181

182

async deleteInstallation(

183

query: InstallationQuery<boolean>,

184

logger?: Logger

185

): Promise<void>;

186

}

187

```

188

189

#### FileInstallationStore

190

191

```typescript { .api }

192

/**

193

* File-based installation store for simple persistent storage

194

*/

195

class FileInstallationStore implements InstallationStore {

196

/**

197

* @param options - Configuration options for file storage

198

*/

199

constructor(options?: FileInstallationOptions);

200

201

async storeInstallation<AuthVersion extends "v1" | "v2">(

202

installation: Installation<AuthVersion, boolean>,

203

logger?: Logger

204

): Promise<void>;

205

206

async fetchInstallation(

207

query: InstallationQuery<boolean>,

208

logger?: Logger

209

): Promise<Installation<"v1" | "v2", boolean>>;

210

211

async deleteInstallation(

212

query: InstallationQuery<boolean>,

213

logger?: Logger

214

): Promise<void>;

215

}

216

217

/**

218

* Configuration options for FileInstallationStore

219

*/

220

interface FileInstallationOptions {

221

/** Base directory to store installation files */

222

baseDir?: string;

223

/** Whether to store historical installation data */

224

historicalDataEnabled?: boolean;

225

/** Client ID for organizing installations by app */

226

clientId?: string;

227

}

228

```

229

230

**Usage Examples:**

231

232

```typescript

233

import {

234

InstallProvider,

235

MemoryInstallationStore,

236

FileInstallationStore,

237

Installation,

238

InstallationQuery

239

} from "@slack/oauth";

240

241

// Using memory store (development)

242

const memoryStore = new MemoryInstallationStore();

243

const installer = new InstallProvider({

244

clientId: process.env.SLACK_CLIENT_ID!,

245

clientSecret: process.env.SLACK_CLIENT_SECRET!,

246

stateSecret: "secret",

247

installationStore: memoryStore,

248

});

249

250

// Using file store (simple persistence)

251

const fileStore = new FileInstallationStore("./slack-installations");

252

const installer2 = new InstallProvider({

253

clientId: process.env.SLACK_CLIENT_ID!,

254

clientSecret: process.env.SLACK_CLIENT_SECRET!,

255

stateSecret: "secret",

256

installationStore: fileStore,

257

});

258

259

// Custom installation store implementation

260

class DatabaseInstallationStore implements InstallationStore {

261

async storeInstallation(installation: Installation) {

262

// Store in database

263

await db.installations.create({

264

teamId: installation.team?.id,

265

enterpriseId: installation.enterprise?.id,

266

botToken: installation.bot?.token,

267

userToken: installation.user?.token,

268

// ... other fields

269

});

270

}

271

272

async fetchInstallation(query: InstallationQuery) {

273

// Fetch from database

274

const record = await db.installations.findOne({

275

teamId: query.teamId,

276

enterpriseId: query.enterpriseId,

277

});

278

279

if (!record) {

280

throw new Error("Installation not found");

281

}

282

283

return {

284

team: record.teamId ? { id: record.teamId } : undefined,

285

enterprise: record.enterpriseId ? { id: record.enterpriseId } : undefined,

286

bot: record.botToken ? {

287

token: record.botToken,

288

scopes: record.botScopes || [],

289

id: record.botId,

290

userId: record.botUserId,

291

} : undefined,

292

user: record.userToken ? {

293

token: record.userToken,

294

scopes: record.userScopes || [],

295

id: record.userId,

296

} : undefined,

297

};

298

}

299

300

async deleteInstallation(query: InstallationQuery) {

301

await db.installations.deleteOne({

302

teamId: query.teamId,

303

enterpriseId: query.enterpriseId,

304

});

305

}

306

}

307

308

// Direct installation store usage

309

const query: InstallationQuery = {

310

teamId: "T1234567890",

311

isEnterpriseInstall: false,

312

};

313

314

const installation = await installer.installationStore.fetchInstallation(query);

315

console.log("Bot token:", installation.bot?.token);

316

```