or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication-flows.mddevice-code-flow.mdindex.mdlogging-error-handling.mdtoken-caching.md

token-caching.mddocs/

0

# Token Caching

1

2

Thread-safe token storage and management with serialization capabilities for persistent caching across application sessions. The TokenCache class provides efficient token lookup, storage, removal, and automatic cleanup functionality.

3

4

## Capabilities

5

6

### TokenCache Constructor

7

8

Creates a new token cache instance with optional deserialization from previously saved state.

9

10

```python { .api }

11

def __init__(self, state=None):

12

"""

13

Initialize token cache.

14

15

Parameters:

16

- state (str, optional): Serialized cache state from previous session

17

"""

18

```

19

20

**Usage Example:**

21

22

```python

23

import adal

24

25

# Create empty cache

26

cache = adal.TokenCache()

27

28

# Create cache from previously saved state

29

with open('token_cache.json', 'r') as f:

30

cache_state = f.read()

31

cache = adal.TokenCache(state=cache_state)

32

33

# Use cache with authentication context

34

context = adal.AuthenticationContext(

35

authority='https://login.microsoftonline.com/tenant-id',

36

cache=cache

37

)

38

```

39

40

### Token Lookup

41

42

Searches the cache for tokens matching specified criteria. Returns a list of matching token entries.

43

44

```python { .api }

45

def find(self, query):

46

"""

47

Find tokens in cache matching query criteria.

48

49

Parameters:

50

- query (dict): Search criteria with optional keys:

51

- '_clientId' (str): OAuth client ID (note the underscore prefix)

52

- 'userId' (str): User identifier

53

- 'isMRRT' (bool): Whether to match multi-resource refresh tokens

54

55

Returns:

56

list: List of matching authentication result dictionaries

57

58

Note: Any parameter set to None acts as a wildcard (matches all).

59

All specified criteria must match (AND logic).

60

"""

61

```

62

63

**Usage Example:**

64

65

```python

66

# Find all tokens for a specific user

67

user_tokens = cache.find({'userId': 'user@tenant.com'})

68

69

# Find tokens for specific client and user

70

app_tokens = cache.find({

71

'_clientId': 'your-client-id',

72

'userId': 'user@tenant.com'

73

})

74

75

# Find multi-resource refresh tokens for a user

76

mrrt_tokens = cache.find({

77

'userId': 'user@tenant.com',

78

'isMRRT': True

79

})

80

81

# Find all cached tokens

82

all_tokens = cache.find({})

83

84

for token in user_tokens:

85

print(f"Resource: {token.get('resource')}")

86

print(f"Expires: {token.get('expiresOn')}")

87

```

88

89

### Token Storage

90

91

Adds new token entries to the cache. Automatically handles deduplication and updates existing entries.

92

93

```python { .api }

94

def add(self, entries):

95

"""

96

Add token entries to cache.

97

98

Parameters:

99

- entries (list or dict): Single token entry or list of token entries to add

100

Each entry should be an authentication result dictionary

101

"""

102

```

103

104

**Usage Example:**

105

106

```python

107

# Tokens are typically added automatically by AuthenticationContext

108

# But you can add tokens manually if needed

109

110

token_entry = {

111

'accessToken': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...',

112

'refreshToken': 'AQABAAAAAAD--DLA3VO7QrddgJg7WevrAg...',

113

'tokenType': 'Bearer',

114

'expiresIn': 3600,

115

'expiresOn': '2023-10-01T12:00:00Z',

116

'resource': 'https://management.azure.com/',

117

'userId': 'user@tenant.com',

118

'clientId': 'your-client-id'

119

}

120

121

cache.add(token_entry)

122

print(f"Cache has changes: {cache.has_state_changed}")

123

```

124

125

### Token Removal

126

127

Removes token entries from the cache based on matching criteria.

128

129

```python { .api }

130

def remove(self, entries):

131

"""

132

Remove token entries from cache.

133

134

Parameters:

135

- entries (list or dict): Single token entry or list of token entries to remove

136

Entries are matched based on authority, resource, client ID, and user ID

137

"""

138

```

139

140

**Usage Example:**

141

142

```python

143

# Remove specific tokens (typically done automatically on expiration)

144

expired_tokens = cache.find({'userId': 'user@tenant.com'})

145

cache.remove(expired_tokens)

146

147

# Remove single token entry

148

cache.remove(token_entry)

149

```

150

151

### Cache Serialization

152

153

Serializes the entire cache to a JSON string for persistent storage across application sessions.

154

155

```python { .api }

156

def serialize(self):

157

"""

158

Serialize cache to JSON string.

159

160

Returns:

161

str: JSON representation of cache contents

162

"""

163

```

164

165

**Usage Example:**

166

167

```python

168

# Save cache to file

169

cache_state = cache.serialize()

170

with open('token_cache.json', 'w') as f:

171

f.write(cache_state)

172

173

# Save cache to database or other storage

174

import base64

175

encoded_cache = base64.b64encode(cache_state.encode()).decode()

176

# Store encoded_cache in database

177

```

178

179

### Cache Deserialization

180

181

Loads cache contents from a previously serialized JSON string.

182

183

```python { .api }

184

def deserialize(self, state):

185

"""

186

Load cache contents from serialized state.

187

188

Parameters:

189

- state (str): JSON string from previous serialize() call

190

"""

191

```

192

193

**Usage Example:**

194

195

```python

196

# Load cache from file

197

with open('token_cache.json', 'r') as f:

198

cache_state = f.read()

199

200

cache = adal.TokenCache()

201

cache.deserialize(cache_state)

202

203

# Load from database

204

# encoded_cache = get_from_database()

205

# cache_state = base64.b64decode(encoded_cache).decode()

206

# cache.deserialize(cache_state)

207

```

208

209

### Cache Inspection

210

211

Returns all cache entries as key-value pairs for inspection and debugging.

212

213

```python { .api }

214

def read_items(self):

215

"""

216

Get all cache entries as key-value pairs.

217

218

Returns:

219

list: List of (TokenCacheKey, authentication_result) tuples

220

"""

221

```

222

223

**Usage Example:**

224

225

```python

226

# Inspect cache contents

227

items = cache.read_items()

228

print(f"Cache contains {len(items)} entries:")

229

230

for cache_key, token_data in items:

231

print(f"User: {cache_key.user_id}")

232

print(f"Resource: {cache_key.resource}")

233

print(f"Client: {cache_key.client_id}")

234

print(f"Expires: {token_data.get('expiresOn')}")

235

print("---")

236

```

237

238

## Properties

239

240

### State Change Tracking

241

242

```python { .api }

243

has_state_changed: bool # Read-only property indicating if cache has been modified

244

```

245

246

**Usage Example:**

247

248

```python

249

# Check if cache needs to be saved

250

if cache.has_state_changed:

251

cache_state = cache.serialize()

252

save_cache_to_storage(cache_state)

253

print("Cache saved")

254

else:

255

print("No changes to save")

256

```

257

258

## Complete Token Caching Example

259

260

```python

261

import adal

262

import os

263

import json

264

265

class PersistentTokenCache:

266

def __init__(self, cache_file='adal_cache.json'):

267

self.cache_file = cache_file

268

self.cache = self._load_cache()

269

270

def _load_cache(self):

271

"""Load cache from file if it exists"""

272

if os.path.exists(self.cache_file):

273

try:

274

with open(self.cache_file, 'r') as f:

275

cache_state = f.read()

276

return adal.TokenCache(state=cache_state)

277

except Exception as e:

278

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

279

280

return adal.TokenCache()

281

282

def save_cache(self):

283

"""Save cache to file if it has changed"""

284

if self.cache.has_state_changed:

285

try:

286

cache_state = self.cache.serialize()

287

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

288

f.write(cache_state)

289

print(f"Cache saved to {self.cache_file}")

290

except Exception as e:

291

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

292

293

def get_context(self, authority):

294

"""Get authentication context with persistent cache"""

295

return adal.AuthenticationContext(authority, cache=self.cache)

296

297

def clear_cache(self):

298

"""Clear all cached tokens"""

299

all_tokens = self.cache.find({})

300

if all_tokens:

301

self.cache.remove(all_tokens)

302

self.save_cache()

303

print("Cache cleared")

304

305

# Usage example

306

def main():

307

# Create persistent cache manager

308

cache_manager = PersistentTokenCache('my_app_cache.json')

309

310

# Get authentication context with cache

311

authority = 'https://login.microsoftonline.com/tenant-id'

312

context = cache_manager.get_context(authority)

313

314

try:

315

# Try to get token (will use cache if available)

316

token = context.acquire_token_with_client_credentials(

317

resource='https://management.azure.com/',

318

client_id='your-client-id',

319

client_secret='your-client-secret'

320

)

321

322

print("Authentication successful!")

323

print(f"Token expires: {token.get('expiresOn')}")

324

325

# Save cache for next time

326

cache_manager.save_cache()

327

328

except adal.AdalError as e:

329

print(f"Authentication failed: {e}")

330

331

# Inspect cache contents

332

cached_tokens = cache_manager.cache.find({})

333

print(f"Cache contains {len(cached_tokens)} tokens")

334

335

if __name__ == '__main__':

336

main()

337

```

338

339

## Thread Safety

340

341

TokenCache is thread-safe and can be used safely across multiple threads. All operations use internal locking to prevent race conditions.

342

343

```python

344

import threading

345

import adal

346

347

# Shared cache across threads

348

shared_cache = adal.TokenCache()

349

350

def worker_thread(thread_id):

351

context = adal.AuthenticationContext(

352

'https://login.microsoftonline.com/tenant-id',

353

cache=shared_cache

354

)

355

356

# Safe to call from multiple threads

357

token = context.acquire_token_with_client_credentials(

358

resource='https://management.azure.com/',

359

client_id='your-client-id',

360

client_secret='your-client-secret'

361

)

362

print(f"Thread {thread_id} got token")

363

364

# Create multiple threads using same cache

365

threads = []

366

for i in range(5):

367

t = threading.Thread(target=worker_thread, args=(i,))

368

threads.append(t)

369

t.start()

370

371

for t in threads:

372

t.join()

373

```