or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

common-auth-flows.mdconfidential-client.mdindex.mdmanaged-identity.mdpublic-client.mdsecurity-advanced.mdtoken-cache.md

token-cache.mddocs/

0

# Token Caching

1

2

MSAL Python provides comprehensive token caching capabilities to minimize authentication requests and improve application performance. The cache system supports both in-memory caching for single-session scenarios and persistent caching for applications that need to maintain tokens across sessions.

3

4

## Capabilities

5

6

### TokenCache (In-Memory)

7

8

Base token cache class that maintains tokens in memory using a unified schema compatible across all MSAL libraries. Automatically handles token refresh and account management.

9

10

```python { .api }

11

class TokenCache:

12

def __init__(self):

13

"""Create an in-memory token cache."""

14

15

def find(

16

self,

17

credential_type: str,

18

target=None,

19

query=None,

20

*,

21

now=None

22

) -> list:

23

"""

24

Find matching cache entries.

25

26

.. deprecated::

27

Use list(search(...)) instead to explicitly get a list.

28

29

Parameters:

30

- credential_type: Type of credential (ACCESS_TOKEN, REFRESH_TOKEN, ACCOUNT, ID_TOKEN)

31

- target: Scope string for filtering

32

- query: Additional query parameters dict

33

- now: Current time for expiration checking

34

35

Returns:

36

List of matching cache entries

37

"""

38

39

def search(

40

self,

41

credential_type: str,

42

target=None,

43

query=None,

44

now=None

45

):

46

"""

47

Generator version of find() for memory efficiency.

48

49

Returns:

50

Generator yielding matching cache entries

51

"""

52

53

def add(self, event: dict, now=None):

54

"""

55

Add token to cache from authentication event.

56

57

Parameters:

58

- event: Authentication result dictionary

59

- now: Current time override

60

"""

61

62

def modify(

63

self,

64

credential_type: str,

65

old_entry: dict,

66

new_key_value_pairs=None

67

):

68

"""

69

Modify existing cache entry.

70

71

Parameters:

72

- credential_type: Type of credential to modify

73

- old_entry: Existing cache entry

74

- new_key_value_pairs: Dict of new values, or None to remove entry

75

"""

76

```

77

78

### Credential Types

79

80

```python { .api }

81

class TokenCache:

82

class CredentialType:

83

ACCESS_TOKEN = "AccessToken"

84

REFRESH_TOKEN = "RefreshToken"

85

ACCOUNT = "Account"

86

ID_TOKEN = "IdToken"

87

APP_METADATA = "AppMetadata"

88

89

class AuthorityType:

90

ADFS = "ADFS"

91

MSSTS = "MSSTS" # AAD v2 for both AAD & MSA

92

```

93

94

### Token Management Methods

95

96

Specific methods for managing different token types:

97

98

```python { .api }

99

def remove_rt(self, rt_item: dict):

100

"""Remove refresh token from cache."""

101

102

def update_rt(self, rt_item: dict, new_rt: str):

103

"""Update refresh token in cache."""

104

105

def remove_at(self, at_item: dict):

106

"""Remove access token from cache."""

107

108

def remove_idt(self, idt_item: dict):

109

"""Remove ID token from cache."""

110

111

def remove_account(self, account_item: dict):

112

"""Remove account from cache."""

113

```

114

115

Usage example:

116

117

```python

118

import msal

119

120

# Create custom cache

121

cache = msal.TokenCache()

122

123

# Use cache with application

124

app = msal.PublicClientApplication(

125

client_id="your-client-id",

126

token_cache=cache

127

)

128

129

# After authentication, tokens are automatically cached

130

result = app.acquire_token_interactive(scopes=["User.Read"])

131

132

# Find cached access tokens

133

access_tokens = cache.find(

134

credential_type=cache.CredentialType.ACCESS_TOKEN,

135

target="User.Read"

136

)

137

138

print(f"Found {len(access_tokens)} cached access tokens")

139

140

# Find accounts

141

accounts = cache.find(credential_type=cache.CredentialType.ACCOUNT)

142

for account in accounts:

143

print(f"Account: {account.get('username')}")

144

```

145

146

### SerializableTokenCache (Persistent)

147

148

Extended token cache that supports serialization to/from persistent storage. Tracks state changes to optimize serialization operations.

149

150

```python { .api }

151

class SerializableTokenCache(TokenCache):

152

def __init__(self):

153

"""Create a serializable token cache."""

154

155

def serialize(self) -> str:

156

"""

157

Serialize current cache state to JSON string.

158

159

Returns:

160

JSON string representation of cache state

161

"""

162

163

def deserialize(self, state: str):

164

"""

165

Deserialize cache from JSON string.

166

167

Parameters:

168

- state: JSON string from previous serialize() call

169

"""

170

171

def add(self, event: dict, **kwargs):

172

"""

173

Add token and mark cache as changed.

174

175

Parameters:

176

- event: Authentication result dictionary

177

"""

178

179

def modify(

180

self,

181

credential_type: str,

182

old_entry: dict,

183

new_key_value_pairs=None

184

):

185

"""

186

Modify cache entry and mark cache as changed.

187

188

Parameters:

189

- credential_type: Type of credential to modify

190

- old_entry: Existing cache entry

191

- new_key_value_pairs: Dict of new values, or None to remove entry

192

"""

193

194

@property

195

def has_state_changed(self) -> bool:

196

"""Check if cache state has changed since last serialization."""

197

198

def _mark_as_changed(self):

199

"""Mark cache as changed (internal use)."""

200

```

201

202

### File-Based Cache Implementation

203

204

Example implementation of persistent file-based cache:

205

206

```python

207

import msal

208

import os

209

import json

210

import atexit

211

212

class FilePersistenceTokenCache(msal.SerializableTokenCache):

213

def __init__(self, file_path):

214

super().__init__()

215

self.file_path = file_path

216

217

# Load existing cache

218

if os.path.exists(file_path):

219

try:

220

with open(file_path, 'r') as f:

221

cache_data = f.read()

222

if cache_data:

223

self.deserialize(cache_data)

224

except Exception as e:

225

print(f"Warning: Failed to load cache: {e}")

226

227

# Save cache on exit

228

atexit.register(self._save_cache)

229

230

def _save_cache(self):

231

if self.has_state_changed:

232

try:

233

with open(self.file_path, 'w') as f:

234

f.write(self.serialize())

235

except Exception as e:

236

print(f"Warning: Failed to save cache: {e}")

237

238

# Usage

239

cache = FilePersistenceTokenCache("token_cache.json")

240

241

app = msal.PublicClientApplication(

242

client_id="your-client-id",

243

token_cache=cache

244

)

245

246

# First run - tokens will be cached to file

247

result = app.acquire_token_interactive(scopes=["User.Read"])

248

249

# Subsequent runs - tokens loaded from file

250

accounts = app.get_accounts()

251

if accounts:

252

result = app.acquire_token_silent(scopes=["User.Read"], account=accounts[0])

253

```

254

255

### Cross-Application Token Sharing

256

257

Multiple applications can share the same cache for single sign-on experiences:

258

259

```python

260

import msal

261

262

# Shared cache file

263

shared_cache = FilePersistenceTokenCache("shared_cache.json")

264

265

# Application 1

266

app1 = msal.PublicClientApplication(

267

client_id="app1-client-id",

268

token_cache=shared_cache

269

)

270

271

# Application 2

272

app2 = msal.PublicClientApplication(

273

client_id="app2-client-id",

274

token_cache=shared_cache

275

)

276

277

# User authenticates in app1

278

result1 = app1.acquire_token_interactive(scopes=["User.Read"])

279

280

# App2 can use cached tokens for same user

281

accounts = app2.get_accounts()

282

if accounts:

283

result2 = app2.acquire_token_silent(

284

scopes=["User.Read"],

285

account=accounts[0]

286

)

287

```

288

289

### Cache Querying and Management

290

291

Advanced cache operations for monitoring and managing cached tokens:

292

293

```python

294

import msal

295

from datetime import datetime

296

297

app = msal.PublicClientApplication(

298

client_id="your-client-id",

299

token_cache=cache

300

)

301

302

# Get all accounts

303

accounts = app.get_accounts()

304

print(f"Cached accounts: {len(accounts)}")

305

306

for account in accounts:

307

print(f" User: {account.get('username')}")

308

print(f" Environment: {account.get('environment')}")

309

print(f" Home Account ID: {account.get('home_account_id')}")

310

311

# Find all access tokens

312

access_tokens = cache.find(

313

credential_type=cache.CredentialType.ACCESS_TOKEN

314

)

315

316

print(f"\nCached access tokens: {len(access_tokens)}")

317

for token in access_tokens:

318

scopes = token.get('target', 'Unknown scopes')

319

expires_on = token.get('expires_on')

320

if expires_on:

321

expires_dt = datetime.fromtimestamp(int(expires_on))

322

print(f" Scopes: {scopes}")

323

print(f" Expires: {expires_dt}")

324

print(f" Expired: {expires_dt < datetime.now()}")

325

326

# Find refresh tokens

327

refresh_tokens = cache.find(

328

credential_type=cache.CredentialType.REFRESH_TOKEN

329

)

330

print(f"\nCached refresh tokens: {len(refresh_tokens)}")

331

332

# Remove specific account

333

if accounts:

334

app.remove_account(accounts[0])

335

print("Removed first account from cache")

336

```

337

338

### Cache Security Considerations

339

340

Important security practices for token caching:

341

342

```python

343

import msal

344

import os

345

import stat

346

347

class SecureFileCache(msal.SerializableTokenCache):

348

def __init__(self, file_path):

349

super().__init__()

350

self.file_path = file_path

351

352

# Load with secure permissions

353

if os.path.exists(file_path):

354

# Verify file permissions (owner read/write only)

355

file_stat = os.stat(file_path)

356

if file_stat.st_mode & 0o077: # Check if group/other have permissions

357

print("Warning: Cache file has insecure permissions")

358

359

with open(file_path, 'r') as f:

360

self.deserialize(f.read())

361

362

atexit.register(self._save_cache)

363

364

def _save_cache(self):

365

if self.has_state_changed:

366

# Create file with secure permissions

367

with open(self.file_path, 'w') as f:

368

f.write(self.serialize())

369

370

# Set restrictive permissions (owner only)

371

os.chmod(self.file_path, stat.S_IRUSR | stat.S_IWUSR)

372

373

# Additional security measures:

374

# 1. Store cache files in user-specific directories

375

# 2. Encrypt cache contents for sensitive environments

376

# 3. Set appropriate file system permissions

377

# 4. Consider in-memory cache for highly sensitive applications

378

# 5. Implement cache expiration and cleanup policies

379

```

380

381

## Error Handling

382

383

Cache-related error handling patterns:

384

385

```python

386

try:

387

cache = FilePersistenceTokenCache("cache.json")

388

389

app = msal.PublicClientApplication(

390

client_id="your-client-id",

391

token_cache=cache

392

)

393

394

# Attempt silent authentication

395

accounts = app.get_accounts()

396

if accounts:

397

result = app.acquire_token_silent(

398

scopes=["User.Read"],

399

account=accounts[0]

400

)

401

402

if "access_token" not in result:

403

# Silent auth failed, try interactive

404

print("Silent authentication failed, trying interactive...")

405

result = app.acquire_token_interactive(scopes=["User.Read"])

406

else:

407

# No cached accounts

408

print("No cached accounts found, using interactive authentication...")

409

result = app.acquire_token_interactive(scopes=["User.Read"])

410

411

except Exception as e:

412

print(f"Cache operation failed: {e}")

413

# Fall back to default in-memory cache

414

app = msal.PublicClientApplication(client_id="your-client-id")

415

```