or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

algorithm-management.mdindex.mdjwk-operations.mdjwks-client.mdjws-operations.mdjwt-operations.md

jwks-client.mddocs/

0

# JWKS Client

1

2

HTTP client for fetching and caching JSON Web Key Sets from remote endpoints. Provides automatic key refresh, flexible caching strategies, and seamless integration with JWT verification workflows for dynamic key management.

3

4

## Capabilities

5

6

### JWKS Client Initialization

7

8

Creates an HTTP client configured for fetching JWKS from remote endpoints with customizable caching and network options.

9

10

```python { .api }

11

class PyJWKClient:

12

def __init__(self, uri: str, cache_keys: bool = False,

13

max_cached_keys: int = 16, cache_jwk_set: bool = True,

14

lifespan: int = 300, headers: dict = None,

15

timeout: int = 30, ssl_context = None):

16

"""

17

Initialize JWKS client for fetching remote key sets.

18

19

Args:

20

uri (str): JWKS endpoint URL

21

cache_keys (bool): Enable individual key caching

22

max_cached_keys (int): Maximum keys to cache (when cache_keys=True)

23

cache_jwk_set (bool): Enable JWK Set caching

24

lifespan (int): Cache lifespan in seconds (default 300/5min)

25

headers (dict): Additional HTTP headers

26

timeout (int): Request timeout in seconds

27

ssl_context: SSL context for HTTPS requests

28

29

Raises:

30

PyJWKClientError: Invalid configuration

31

"""

32

```

33

34

Usage examples:

35

36

```python

37

import jwt

38

from jwt import PyJWKClient

39

40

# Basic JWKS client

41

jwks_client = PyJWKClient("https://example.com/.well-known/jwks.json")

42

43

# With custom caching

44

jwks_client = PyJWKClient(

45

"https://example.com/.well-known/jwks.json",

46

cache_keys=True, # Cache individual signing keys

47

max_cached_keys=50, # Increase cache size

48

lifespan=600 # 10 minute cache

49

)

50

51

# With custom headers and timeout

52

jwks_client = PyJWKClient(

53

"https://example.com/.well-known/jwks.json",

54

headers={"User-Agent": "MyApp/1.0"},

55

timeout=10 # 10 second timeout

56

)

57

58

# Disable JWK Set caching for always-fresh keys

59

jwks_client = PyJWKClient(

60

"https://example.com/.well-known/jwks.json",

61

cache_jwk_set=False

62

)

63

```

64

65

### Key Retrieval

66

67

Methods for fetching and accessing signing keys from remote JWKS endpoints.

68

69

```python { .api }

70

class PyJWKClient:

71

def get_signing_key(self, kid: str) -> PyJWK:

72

"""

73

Get signing key by key ID, with automatic refresh on cache miss.

74

75

Args:

76

kid (str): Key ID to retrieve

77

78

Returns:

79

PyJWK: Signing key matching the key ID

80

81

Raises:

82

PyJWKClientError: Key not found or network error

83

PyJWKClientConnectionError: Connection failed

84

"""

85

86

def get_signing_key_from_jwt(self, token: str) -> PyJWK:

87

"""

88

Extract key ID from JWT header and retrieve corresponding key.

89

90

Args:

91

token (str): JWT token containing 'kid' in header

92

93

Returns:

94

PyJWK: Signing key for the token

95

96

Raises:

97

PyJWKClientError: Missing kid or key not found

98

PyJWKClientConnectionError: Network error

99

"""

100

101

def get_signing_keys(self, refresh: bool = False) -> list:

102

"""

103

Get all signing keys from the JWKS endpoint.

104

105

Args:

106

refresh (bool): Force refresh from remote endpoint

107

108

Returns:

109

list: List of PyJWK signing keys (use='sig' or None)

110

111

Raises:

112

PyJWKClientError: No signing keys found

113

PyJWKClientConnectionError: Network error

114

"""

115

```

116

117

Usage examples:

118

119

```python

120

import jwt

121

from jwt import PyJWKClient

122

from jwt.exceptions import PyJWKClientError

123

124

jwks_client = PyJWKClient("https://example.com/.well-known/jwks.json")

125

126

# Get key by ID

127

try:

128

signing_key = jwks_client.get_signing_key("my-key-id")

129

print(f"Found key: {signing_key.algorithm_name}")

130

except PyJWKClientError as e:

131

print(f"Key retrieval failed: {e}")

132

133

# Get key from JWT token

134

token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Im15LWtleS1pZCJ9..."

135

try:

136

signing_key = jwks_client.get_signing_key_from_jwt(token)

137

payload = jwt.decode(token, signing_key.key, algorithms=[signing_key.algorithm_name])

138

print(f"Token verified: {payload}")

139

except PyJWKClientError as e:

140

print(f"Token verification failed: {e}")

141

142

# Get all signing keys

143

signing_keys = jwks_client.get_signing_keys()

144

for key in signing_keys:

145

print(f"Key {key.key_id}: {key.algorithm_name}")

146

147

# Force refresh from remote

148

fresh_keys = jwks_client.get_signing_keys(refresh=True)

149

```

150

151

### JWK Set Management

152

153

Methods for managing and accessing complete JWK Sets from remote endpoints.

154

155

```python { .api }

156

class PyJWKClient:

157

def get_jwk_set(self, refresh: bool = False) -> PyJWKSet:

158

"""

159

Get complete JWK Set from endpoint with caching.

160

161

Args:

162

refresh (bool): Force refresh from remote endpoint

163

164

Returns:

165

PyJWKSet: Complete key set

166

167

Raises:

168

PyJWKClientError: Invalid JWKS format

169

PyJWKClientConnectionError: Network error

170

"""

171

172

def fetch_data(self):

173

"""

174

Fetch raw JWKS data from remote endpoint.

175

176

Returns:

177

dict: Raw JWKS data

178

179

Raises:

180

PyJWKClientConnectionError: Network or timeout error

181

"""

182

183

@staticmethod

184

def match_kid(signing_keys: list, kid: str) -> PyJWK | None:

185

"""

186

Find signing key by key ID from a list of keys.

187

188

Args:

189

signing_keys (list): List of PyJWK signing keys

190

kid (str): Key ID to match

191

192

Returns:

193

PyJWK | None: Matching key or None if not found

194

"""

195

```

196

197

Usage examples:

198

199

```python

200

import jwt

201

from jwt import PyJWKClient

202

203

jwks_client = PyJWKClient("https://example.com/.well-known/jwks.json")

204

205

# Get cached JWK Set

206

jwk_set = jwks_client.get_jwk_set()

207

print(f"Loaded {len(jwk_set.keys)} keys")

208

209

# Force refresh for latest keys

210

fresh_jwk_set = jwks_client.get_jwk_set(refresh=True)

211

212

# Access individual keys

213

rsa_key = jwk_set['rsa-key-1']

214

ec_key = jwk_set['ec-key-1']

215

216

# Low-level: fetch raw data

217

raw_jwks = jwks_client.fetch_data()

218

print(f"Raw JWKS: {raw_jwks}")

219

```

220

221

### Complete JWT Verification Workflow

222

223

Common patterns for integrating JWKS client with JWT verification:

224

225

```python

226

import jwt

227

from jwt import PyJWKClient

228

from jwt.exceptions import PyJWKClientError, InvalidTokenError

229

230

def verify_jwt_with_jwks(token: str, jwks_url: str) -> dict:

231

"""

232

Verify JWT using remote JWKS endpoint.

233

"""

234

jwks_client = PyJWKClient(jwks_url)

235

236

try:

237

# Get signing key from token

238

signing_key = jwks_client.get_signing_key_from_jwt(token)

239

240

# Verify and decode token

241

payload = jwt.decode(

242

token,

243

signing_key.key,

244

algorithms=[signing_key.algorithm_name],

245

audience="my-app",

246

issuer="https://my-auth-server.com"

247

)

248

249

return payload

250

251

except PyJWKClientError as e:

252

raise ValueError(f"Key retrieval failed: {e}")

253

except InvalidTokenError as e:

254

raise ValueError(f"Token validation failed: {e}")

255

256

# Usage

257

try:

258

payload = verify_jwt_with_jwks(

259

token="eyJ...",

260

jwks_url="https://auth.example.com/.well-known/jwks.json"

261

)

262

print(f"User: {payload.get('sub')}")

263

except ValueError as e:

264

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

265

```

266

267

### Caching Strategies

268

269

PyJWKClient provides two levels of caching:

270

271

#### JWK Set Caching (Default: Enabled)

272

273

Caches the entire JWKS response for the specified lifespan:

274

275

```python

276

# Default: 5-minute JWK Set cache

277

jwks_client = PyJWKClient(uri, cache_jwk_set=True, lifespan=300)

278

279

# Custom cache duration

280

jwks_client = PyJWKClient(uri, lifespan=1800) # 30 minutes

281

282

# Disable JWK Set caching

283

jwks_client = PyJWKClient(uri, cache_jwk_set=False)

284

```

285

286

#### Individual Key Caching (Default: Disabled)

287

288

Caches individual signing key lookups using LRU cache:

289

290

```python

291

# Enable signing key caching

292

jwks_client = PyJWKClient(uri, cache_keys=True, max_cached_keys=16)

293

294

# Larger key cache

295

jwks_client = PyJWKClient(uri, cache_keys=True, max_cached_keys=100)

296

297

# Both caching levels enabled

298

jwks_client = PyJWKClient(

299

uri,

300

cache_jwk_set=True, # Cache full JWKS response

301

lifespan=600, # 10 minute JWKS cache

302

cache_keys=True, # Also cache individual key lookups

303

max_cached_keys=50 # LRU cache for 50 keys

304

)

305

```

306

307

### Error Handling

308

309

JWKS client specific exceptions and error patterns:

310

311

```python

312

import jwt

313

from jwt.exceptions import (

314

PyJWKClientError,

315

PyJWKClientConnectionError,

316

PyJWKError

317

)

318

319

jwks_client = PyJWKClient("https://example.com/.well-known/jwks.json")

320

321

try:

322

signing_key = jwks_client.get_signing_key("key-id")

323

except PyJWKClientConnectionError as e:

324

# Network/timeout errors

325

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

326

# Implement retry logic or fallback

327

except PyJWKClientError as e:

328

# Key not found, invalid JWKS format, etc.

329

print(f"JWKS error: {e}")

330

except Exception as e:

331

print(f"Unexpected error: {e}")

332

333

# Graceful degradation example

334

def get_key_with_fallback(client, kid, backup_key=None):

335

try:

336

return client.get_signing_key(kid)

337

except (PyJWKClientError, PyJWKClientConnectionError):

338

if backup_key:

339

return backup_key

340

raise ValueError("Primary and backup key retrieval failed")

341

```

342

343

### Advanced Configuration

344

345

SSL and network customization:

346

347

```python

348

import ssl

349

from jwt import PyJWKClient

350

351

# Custom SSL context

352

ssl_context = ssl.create_default_context()

353

ssl_context.check_hostname = False # For testing only!

354

355

jwks_client = PyJWKClient(

356

"https://internal-auth.company.com/.well-known/jwks.json",

357

headers={

358

"Authorization": "Bearer internal-token",

359

"User-Agent": "MyApp/2.0"

360

},

361

timeout=5,

362

ssl_context=ssl_context

363

)

364

365

# Corporate proxy or special routing

366

jwks_client = PyJWKClient(

367

"https://auth.example.com/.well-known/jwks.json",

368

headers={"X-Forwarded-For": "internal.company.com"}

369

)

370

```