axa-fr-oidc 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- axa_fr_oidc-1.3.0/.gitignore +6 -0
- axa_fr_oidc-1.3.0/LICENSE +21 -0
- axa_fr_oidc-1.3.0/PKG-INFO +658 -0
- axa_fr_oidc-1.3.0/README.md +643 -0
- axa_fr_oidc-1.3.0/pyproject.toml +209 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/__init__.py +82 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/authorization/__init__.py +12 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/authorization/generic_authorization.py +54 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/authorization/jwt_authorization.py +106 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/client.py +465 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/constants.py +35 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/http_service/__init__.py +11 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/http_service/http_service.py +84 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/memory_cache/__init__.py +11 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/memory_cache/memory_cache.py +124 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/oidc/__init__.py +23 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/oidc/oidc_authentication.py +617 -0
- axa_fr_oidc-1.3.0/src/axa_fr_oidc/oidc/openid_connect.py +472 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Axa France IARD / Axa France VIE
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: axa-fr-oidc
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: Python library for OpenID Connect (OIDC) authentication with DPoP (Demonstrating Proof-of-Possession) support, featuring JWT validation, token caching, and both sync/async operations.
|
|
5
|
+
Author: Cop Python
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: async,authentication,dpop,jwt,oauth2,oidc,openid-connect,token-validation
|
|
8
|
+
Requires-Python: <3.13,>=3.10
|
|
9
|
+
Requires-Dist: httpx<1.0.0,>=0.28.1
|
|
10
|
+
Requires-Dist: jwskate<1.0.0,>=0.12.2
|
|
11
|
+
Requires-Dist: loguru<1.0.0,>=0.7.3
|
|
12
|
+
Requires-Dist: pyjwt<3.0.0,>=2.10.1
|
|
13
|
+
Requires-Dist: requests-oauth2client<2.0.0,>=1.8.0
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# axa-fr-oidc
|
|
17
|
+
|
|
18
|
+
<div align="center">
|
|
19
|
+
|
|
20
|
+
| Python | Project |
|
|
21
|
+
|-------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
|
|
22
|
+
|  |  |
|
|
23
|
+
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
A Python library for OpenID Connect (OIDC) authentication with DPoP (Demonstrating Proof-of-Possession) support, featuring JWT validation, token caching, and both sync/async operations.
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- 🔐 **OIDC Authentication** - Full OpenID Connect authentication support
|
|
31
|
+
- 🔑 **DPoP Support** - Demonstrating Proof-of-Possession for enhanced security
|
|
32
|
+
- ✅ **JWT Validation** - Comprehensive token validation with JWKS
|
|
33
|
+
- 💾 **Token Caching** - Built-in memory cache for tokens and JWKS
|
|
34
|
+
- ⚡ **Async/Sync** - Supports both synchronous and asynchronous operations
|
|
35
|
+
- 🎯 **Type Safe** - Fully typed with Python type hints
|
|
36
|
+
- 🔒 **Flexible Auth Methods** - `client_secret_jwt`, `client_secret_post`, and `client_secret_basic` with automatic fallback
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
### Using uv (recommended)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uv add axa-fr-oidc
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Using pip
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install axa-fr-oidc
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
### Simple Usage with OidcClient (Recommended)
|
|
55
|
+
|
|
56
|
+
The `OidcClient` provides a simplified, high-level API for common OIDC operations:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from axa_fr_oidc import OidcClient
|
|
60
|
+
|
|
61
|
+
# Create a client with client credentials
|
|
62
|
+
client = OidcClient(
|
|
63
|
+
issuer="https://issuer.url",
|
|
64
|
+
client_id="your-client-id",
|
|
65
|
+
client_secret="your-client-secret",
|
|
66
|
+
scopes=["openid", "profile"],
|
|
67
|
+
audience="your-api-audience",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Get an access token (automatically cached and refreshed)
|
|
71
|
+
access_token = client.get_access_token()
|
|
72
|
+
|
|
73
|
+
# Validate a token
|
|
74
|
+
result = client.validate_token(access_token)
|
|
75
|
+
if result.success:
|
|
76
|
+
print(f"Token is valid! Subject: {result.payload['sub']}")
|
|
77
|
+
else:
|
|
78
|
+
print(f"Token is invalid: {result.error}")
|
|
79
|
+
|
|
80
|
+
# Clean up resources
|
|
81
|
+
client.close_sync()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Using Context Managers
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from axa_fr_oidc import OidcClient
|
|
88
|
+
|
|
89
|
+
# Sync context manager
|
|
90
|
+
with OidcClient(
|
|
91
|
+
issuer="https://issuer.url",
|
|
92
|
+
client_id="your-client-id",
|
|
93
|
+
client_secret="your-client-secret",
|
|
94
|
+
) as client:
|
|
95
|
+
token = client.get_access_token()
|
|
96
|
+
|
|
97
|
+
# Async context manager
|
|
98
|
+
async with OidcClient(
|
|
99
|
+
issuer="https://issuer.url",
|
|
100
|
+
client_id="your-client-id",
|
|
101
|
+
client_secret="your-client-secret",
|
|
102
|
+
) as client:
|
|
103
|
+
token = await client.get_access_token_async()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Async Operations
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import asyncio
|
|
110
|
+
from axa_fr_oidc import OidcClient
|
|
111
|
+
|
|
112
|
+
async def main():
|
|
113
|
+
async with OidcClient(
|
|
114
|
+
issuer="https://issuer.url",
|
|
115
|
+
client_id="your-client-id",
|
|
116
|
+
client_secret="your-client-secret",
|
|
117
|
+
) as client:
|
|
118
|
+
# Async token retrieval
|
|
119
|
+
token = await client.get_access_token_async()
|
|
120
|
+
|
|
121
|
+
# Async token validation
|
|
122
|
+
result = await client.validate_token_async(token)
|
|
123
|
+
print(result.success, result.payload)
|
|
124
|
+
|
|
125
|
+
asyncio.run(main())
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Private Key Authentication
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from axa_fr_oidc import OidcClient
|
|
132
|
+
|
|
133
|
+
# Load your private key
|
|
134
|
+
with open("private_key.pem", "r") as f:
|
|
135
|
+
private_key_pem = f.read()
|
|
136
|
+
|
|
137
|
+
client = OidcClient(
|
|
138
|
+
issuer="https://issuer.url",
|
|
139
|
+
client_id="your-client-id",
|
|
140
|
+
private_key=private_key_pem,
|
|
141
|
+
algorithm="RS256",
|
|
142
|
+
scopes=["openid", "profile"],
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
token = client.get_access_token()
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### Validating DPoP Tokens
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from axa_fr_oidc import OidcClient
|
|
152
|
+
|
|
153
|
+
client = OidcClient(
|
|
154
|
+
issuer="https://issuer.url",
|
|
155
|
+
client_id="your-client-id",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Validate a DPoP-bound token
|
|
159
|
+
result = client.validate_token(
|
|
160
|
+
token=access_token,
|
|
161
|
+
dpop=dpop_proof,
|
|
162
|
+
path="/api/resource",
|
|
163
|
+
http_method="POST",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
print(result.success, result.error)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Token Exchange
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from axa_fr_oidc import OidcClient
|
|
173
|
+
|
|
174
|
+
client = OidcClient(
|
|
175
|
+
issuer="https://issuer.url",
|
|
176
|
+
client_id="your-client-id",
|
|
177
|
+
client_secret="your-client-secret",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Exchange a token (RFC 8693)
|
|
181
|
+
new_token = client.token_exchange(
|
|
182
|
+
subject_token=user_token,
|
|
183
|
+
requested_token_type="urn:ietf:params:oauth:token-type:access_token",
|
|
184
|
+
)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Custom HTTP Configuration (Proxies, SSL, Timeouts)
|
|
188
|
+
|
|
189
|
+
The client supports custom HTTP configurations including proxy settings, SSL verification, and timeouts:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from axa_fr_oidc import OidcClient
|
|
193
|
+
|
|
194
|
+
# Using a proxy
|
|
195
|
+
client = OidcClient(
|
|
196
|
+
issuer="https://issuer.url",
|
|
197
|
+
client_id="your-client-id",
|
|
198
|
+
client_secret="your-client-secret",
|
|
199
|
+
proxy="http://proxy.example.com:8080",
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Using an HTTPS proxy
|
|
203
|
+
client = OidcClient(
|
|
204
|
+
issuer="https://issuer.url",
|
|
205
|
+
client_id="your-client-id",
|
|
206
|
+
client_secret="your-client-secret",
|
|
207
|
+
proxy="https://secure-proxy.example.com:8443",
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Disable SSL verification (not recommended for production)
|
|
211
|
+
client = OidcClient(
|
|
212
|
+
issuer="https://issuer.url",
|
|
213
|
+
client_id="your-client-id",
|
|
214
|
+
client_secret="your-client-secret",
|
|
215
|
+
verify=False,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Set custom timeout (in seconds)
|
|
219
|
+
client = OidcClient(
|
|
220
|
+
issuer="https://issuer.url",
|
|
221
|
+
client_id="your-client-id",
|
|
222
|
+
client_secret="your-client-secret",
|
|
223
|
+
timeout=30.0,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Combine multiple HTTP configurations
|
|
227
|
+
client = OidcClient(
|
|
228
|
+
issuer="https://issuer.url",
|
|
229
|
+
client_id="your-client-id",
|
|
230
|
+
client_secret="your-client-secret",
|
|
231
|
+
proxy="http://proxy.example.com:8080",
|
|
232
|
+
verify=True,
|
|
233
|
+
timeout=10.0,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
token = client.get_access_token()
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### Client Secret Authentication Methods
|
|
240
|
+
|
|
241
|
+
When using `client_secret`, you can control how the credentials are sent to the
|
|
242
|
+
token endpoint via the `auth_method` parameter.
|
|
243
|
+
|
|
244
|
+
| `auth_method` | Behaviour |
|
|
245
|
+
|---|---|
|
|
246
|
+
| `"client_secret_jwt"` *(default)* | Signs an HS256 JWT assertion (RFC 7523). **Automatically falls back to `client_secret_post` on 401** if the server does not have this method enabled for the client. |
|
|
247
|
+
| `"client_secret_post"` | Sends `client_id` + `client_secret` in the POST body. Broadly supported. |
|
|
248
|
+
| `"client_secret_basic"` | Sends credentials as an HTTP Basic Auth header. Broadly supported. |
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
from axa_fr_oidc import OidcClient
|
|
252
|
+
from axa_fr_oidc.constants import (
|
|
253
|
+
CLIENT_SECRET_AUTH_METHOD_JWT, # "client_secret_jwt" (default)
|
|
254
|
+
CLIENT_SECRET_AUTH_METHOD_POST, # "client_secret_post"
|
|
255
|
+
CLIENT_SECRET_AUTH_METHOD_BASIC, # "client_secret_basic"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Default: tries client_secret_jwt, falls back to client_secret_post on 401
|
|
259
|
+
client = OidcClient(
|
|
260
|
+
issuer="https://issuer.url",
|
|
261
|
+
client_id="your-client-id",
|
|
262
|
+
client_secret="your-client-secret",
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
# Explicitly use client_secret_post (no fallback overhead)
|
|
266
|
+
client = OidcClient(
|
|
267
|
+
issuer="https://issuer.url",
|
|
268
|
+
client_id="your-client-id",
|
|
269
|
+
client_secret="your-client-secret",
|
|
270
|
+
auth_method=CLIENT_SECRET_AUTH_METHOD_POST,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Explicitly use client_secret_basic
|
|
274
|
+
client = OidcClient(
|
|
275
|
+
issuer="https://issuer.url",
|
|
276
|
+
client_id="your-client-id",
|
|
277
|
+
client_secret="your-client-secret",
|
|
278
|
+
auth_method=CLIENT_SECRET_AUTH_METHOD_BASIC,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
token = client.get_access_token()
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
For more details, see the [Client Secret Auth Methods Guide](./docs/client-secret-auth-methods.md).
|
|
285
|
+
|
|
286
|
+
### Extract Properties from a JWT Token
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from axa_fr_oidc import JWTAuthorization
|
|
290
|
+
|
|
291
|
+
authorization_header = "<your-jwt-token>"
|
|
292
|
+
jwt_auth = JWTAuthorization(authorization_header)
|
|
293
|
+
|
|
294
|
+
print(jwt_auth.get_property("sub")) # Print the subject of the token
|
|
295
|
+
print(jwt_auth.get_property("exp")) # Print the expiration time of the token
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Advanced Usage (Low-Level API)
|
|
299
|
+
|
|
300
|
+
For users who need more control over the authentication process, the library provides
|
|
301
|
+
low-level components that can be customized individually.
|
|
302
|
+
|
|
303
|
+
### Using OpenIdConnect and OidcAuthentication Directly
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
from axa_fr_oidc import OidcAuthentication, OpenIdConnect, MemoryCache, XHttpServiceGet
|
|
307
|
+
from httpx import AsyncClient, Client
|
|
308
|
+
|
|
309
|
+
# Create HTTP clients
|
|
310
|
+
http_client = Client()
|
|
311
|
+
http_async_client = AsyncClient()
|
|
312
|
+
|
|
313
|
+
# Create HTTP service
|
|
314
|
+
http_service = XHttpServiceGet(
|
|
315
|
+
http_client=http_client,
|
|
316
|
+
http_async_client=http_async_client
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Create cache
|
|
320
|
+
memory_cache = MemoryCache()
|
|
321
|
+
|
|
322
|
+
# Create authentication handler
|
|
323
|
+
auth = OidcAuthentication(
|
|
324
|
+
issuer="https://issuer.url",
|
|
325
|
+
scopes=["openid", "profile"],
|
|
326
|
+
api_audience="your-api-audience",
|
|
327
|
+
service=http_service,
|
|
328
|
+
memory_cache=memory_cache,
|
|
329
|
+
algorithms=["RS256", "ES256"],
|
|
330
|
+
cache_expiration=7200, # Cache JWKS for 2 hours
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# Create OpenID Connect client
|
|
334
|
+
oidc = OpenIdConnect(
|
|
335
|
+
authentication=auth,
|
|
336
|
+
memory_cache=memory_cache,
|
|
337
|
+
client_id="your-client-id",
|
|
338
|
+
client_secret="your-client-secret"
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Get access token
|
|
342
|
+
access_token = oidc.get_access_token()
|
|
343
|
+
|
|
344
|
+
# Validate token
|
|
345
|
+
result = auth.validate(access_token, None, None, None)
|
|
346
|
+
print(result.success, result.error)
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Async Operations with Low-Level API
|
|
350
|
+
|
|
351
|
+
All low-level components support async/await:
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
from axa_fr_oidc import OidcAuthentication, OpenIdConnect, MemoryCache, XHttpServiceGet
|
|
355
|
+
from httpx import AsyncClient, Client
|
|
356
|
+
|
|
357
|
+
async def main():
|
|
358
|
+
http_service = XHttpServiceGet(
|
|
359
|
+
http_client=Client(),
|
|
360
|
+
http_async_client=AsyncClient()
|
|
361
|
+
)
|
|
362
|
+
memory_cache = MemoryCache()
|
|
363
|
+
|
|
364
|
+
auth = OidcAuthentication(
|
|
365
|
+
issuer="https://issuer.url",
|
|
366
|
+
scopes=["openid", "profile"],
|
|
367
|
+
api_audience="your-api-audience",
|
|
368
|
+
service=http_service,
|
|
369
|
+
memory_cache=memory_cache
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
oidc = OpenIdConnect(
|
|
373
|
+
authentication=auth,
|
|
374
|
+
memory_cache=memory_cache,
|
|
375
|
+
client_id="your-client-id",
|
|
376
|
+
client_secret="your-client-secret"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# Async token retrieval
|
|
380
|
+
access_token = await oidc.get_access_token_async()
|
|
381
|
+
|
|
382
|
+
# Async token validation
|
|
383
|
+
result = await auth.validate_async(access_token, None, None, None)
|
|
384
|
+
print(result.success, result.payload)
|
|
385
|
+
|
|
386
|
+
# Run with asyncio
|
|
387
|
+
import asyncio
|
|
388
|
+
asyncio.run(main())
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Using Private Key Authentication (Low-Level)
|
|
392
|
+
|
|
393
|
+
For client credentials flow with private key (JWT bearer) using the low-level API:
|
|
394
|
+
|
|
395
|
+
```python
|
|
396
|
+
from axa_fr_oidc import OidcAuthentication, OpenIdConnect, MemoryCache, XHttpServiceGet
|
|
397
|
+
from httpx import AsyncClient, Client
|
|
398
|
+
|
|
399
|
+
# Load your private key
|
|
400
|
+
with open("private_key.pem", "r") as f:
|
|
401
|
+
private_key_pem = f.read()
|
|
402
|
+
|
|
403
|
+
http_service = XHttpServiceGet(
|
|
404
|
+
http_client=Client(),
|
|
405
|
+
http_async_client=AsyncClient()
|
|
406
|
+
)
|
|
407
|
+
memory_cache = MemoryCache()
|
|
408
|
+
|
|
409
|
+
auth = OidcAuthentication(
|
|
410
|
+
issuer="https://issuer.url",
|
|
411
|
+
scopes=["openid", "profile"],
|
|
412
|
+
api_audience="your-api-audience",
|
|
413
|
+
service=http_service,
|
|
414
|
+
memory_cache=memory_cache
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
oidc = OpenIdConnect(
|
|
418
|
+
authentication=auth,
|
|
419
|
+
memory_cache=memory_cache,
|
|
420
|
+
client_id="your-client-id",
|
|
421
|
+
private_key=private_key_pem,
|
|
422
|
+
algorithm="RS256" # or other supported algorithms
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
access_token = oidc.get_access_token()
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
> **Note:** For most use cases, consider using the simpler `OidcClient` instead.
|
|
429
|
+
> See the [Quick Start](#quick-start) section for examples.
|
|
430
|
+
|
|
431
|
+
### Custom Configuration
|
|
432
|
+
|
|
433
|
+
You can customize various timeouts and cache settings:
|
|
434
|
+
|
|
435
|
+
#### Using OidcClient
|
|
436
|
+
|
|
437
|
+
```python
|
|
438
|
+
from axa_fr_oidc import OidcClient
|
|
439
|
+
|
|
440
|
+
client = OidcClient(
|
|
441
|
+
issuer="https://issuer.url",
|
|
442
|
+
client_id="your-client-id",
|
|
443
|
+
client_secret="your-client-secret",
|
|
444
|
+
scopes=["openid", "profile"],
|
|
445
|
+
audience="your-api-audience",
|
|
446
|
+
algorithms=["RS256", "ES256"], # Allowed algorithms for validation
|
|
447
|
+
cache_expiration=7200, # Cache JWKS for 2 hours (default: 24 hours)
|
|
448
|
+
)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
#### Using Low-Level API
|
|
452
|
+
|
|
453
|
+
```python
|
|
454
|
+
from axa_fr_oidc import (
|
|
455
|
+
OidcAuthentication,
|
|
456
|
+
MemoryCache,
|
|
457
|
+
XHttpServiceGet,
|
|
458
|
+
)
|
|
459
|
+
from httpx import AsyncClient, Client
|
|
460
|
+
|
|
461
|
+
auth = OidcAuthentication(
|
|
462
|
+
issuer="https://issuer.url",
|
|
463
|
+
scopes=["openid", "profile"],
|
|
464
|
+
api_audience="your-api-audience",
|
|
465
|
+
service=XHttpServiceGet(
|
|
466
|
+
http_client=Client(),
|
|
467
|
+
http_async_client=AsyncClient()
|
|
468
|
+
),
|
|
469
|
+
memory_cache=MemoryCache(),
|
|
470
|
+
algorithms=["RS256", "ES256"], # Supported algorithms
|
|
471
|
+
cache_expiration=7200, # Cache JWKS for 2 hours (default: 24 hours)
|
|
472
|
+
)
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
## API Reference
|
|
476
|
+
|
|
477
|
+
### High-Level Client (Recommended)
|
|
478
|
+
|
|
479
|
+
- **`OidcClient`** - Simplified, all-in-one client for OIDC operations
|
|
480
|
+
- `get_access_token()` / `get_access_token_async()` - Get an access token
|
|
481
|
+
- `validate_token()` / `validate_token_async()` - Validate an access token
|
|
482
|
+
- `token_exchange()` - Exchange tokens (RFC 8693)
|
|
483
|
+
- `get_token_endpoint()` / `get_token_endpoint_async()` - Get the token endpoint URL
|
|
484
|
+
- `clear_cache()` - Clear all cached data
|
|
485
|
+
- `close()` / `close_sync()` - Release resources
|
|
486
|
+
- Supports context managers (`with`/`async with`)
|
|
487
|
+
|
|
488
|
+
### Low-Level Classes
|
|
489
|
+
|
|
490
|
+
- **`OidcAuthentication`** - OIDC token validation and JWKS management
|
|
491
|
+
- **`OpenIdConnect`** - Client for obtaining access tokens
|
|
492
|
+
- **`MemoryCache`** - In-memory cache for tokens and JWKS
|
|
493
|
+
- **`XHttpServiceGet`** - HTTP service wrapper for sync/async requests
|
|
494
|
+
- **`JWTAuthorization`** - Utility for extracting JWT claims
|
|
495
|
+
- **`AuthenticationResult`** - Result object from validation operations
|
|
496
|
+
|
|
497
|
+
### Interfaces
|
|
498
|
+
|
|
499
|
+
All main classes have corresponding interfaces for dependency injection:
|
|
500
|
+
|
|
501
|
+
- **`IOidcAuthentication`** - Interface for OidcAuthentication
|
|
502
|
+
- **`IOpenIdConnect`** - Interface for OpenIdConnect
|
|
503
|
+
- **`IMemoryCache`** - Interface for MemoryCache
|
|
504
|
+
- **`IHttpServiceGet`** - Interface for XHttpServiceGet
|
|
505
|
+
- **`IGenericAuthorization`** - Interface for JWTAuthorization
|
|
506
|
+
|
|
507
|
+
### Constants
|
|
508
|
+
|
|
509
|
+
The library exports useful constants for configuration:
|
|
510
|
+
|
|
511
|
+
```python
|
|
512
|
+
from axa_fr_oidc import (
|
|
513
|
+
DEFAULT_CACHE_EXPIRATION_SECONDS, # 86400 (24 hours)
|
|
514
|
+
DEFAULT_DPOP_MAX_AGE_SECONDS, # 300 (5 minutes)
|
|
515
|
+
DEFAULT_CLOCK_SKEW_SECONDS, # 300 (5 minutes)
|
|
516
|
+
DEFAULT_JTI_LIFETIME_SECONDS, # 300 (5 minutes)
|
|
517
|
+
DEFAULT_JWT_ALGORITHM, # "RS256"
|
|
518
|
+
DEFAULT_JWT_EXPIRATION_SECONDS, # 300 (5 minutes)
|
|
519
|
+
DEFAULT_HTTP_TIMEOUT_SECONDS, # 5 seconds
|
|
520
|
+
SUPPORTED_ALGORITHMS, # ["RS256", "HS256"]
|
|
521
|
+
DPOP_TOKEN_TYPE, # "dpop+jwt"
|
|
522
|
+
GRANT_TYPE_CLIENT_CREDENTIALS, # "client_credentials"
|
|
523
|
+
CLIENT_ASSERTION_TYPE_JWT_BEARER, # "urn:ietf:params:oauth:..."
|
|
524
|
+
CONTENT_TYPE_FORM_URLENCODED, # "application/x-www-form-urlencoded"
|
|
525
|
+
OIDC_WELL_KNOWN_PATH, # "/.well-known/openid-configuration"
|
|
526
|
+
CLIENT_SECRET_AUTH_METHOD_JWT, # "client_secret_jwt"
|
|
527
|
+
CLIENT_SECRET_AUTH_METHOD_POST, # "client_secret_post"
|
|
528
|
+
CLIENT_SECRET_AUTH_METHOD_BASIC, # "client_secret_basic"
|
|
529
|
+
)
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
## Advanced Configuration
|
|
533
|
+
|
|
534
|
+
### Client Secret Authentication Methods
|
|
535
|
+
|
|
536
|
+
For detailed information on configuring the client-secret auth method
|
|
537
|
+
(`client_secret_jwt`, `client_secret_post`, `client_secret_basic`) and the
|
|
538
|
+
automatic fallback behaviour, see the
|
|
539
|
+
[Client Secret Auth Methods Guide](./docs/client-secret-auth-methods.md).
|
|
540
|
+
|
|
541
|
+
### Proxy, SSL, and Timeout Configuration
|
|
542
|
+
|
|
543
|
+
For detailed information on configuring HTTP proxies, SSL verification, and timeouts, see the [Proxy Configuration Guide](./docs/proxy-configuration.md).
|
|
544
|
+
|
|
545
|
+
Quick examples:
|
|
546
|
+
|
|
547
|
+
```python
|
|
548
|
+
# Using a proxy
|
|
549
|
+
client = OidcClient(
|
|
550
|
+
issuer="https://auth.example.com",
|
|
551
|
+
client_id="your-client-id",
|
|
552
|
+
client_secret="your-client-secret",
|
|
553
|
+
proxy="http://proxy.example.com:8080",
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
# With custom timeout
|
|
557
|
+
client = OidcClient(
|
|
558
|
+
issuer="https://auth.example.com",
|
|
559
|
+
client_id="your-client-id",
|
|
560
|
+
client_secret="your-client-secret",
|
|
561
|
+
timeout=30.0,
|
|
562
|
+
)
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
## Development
|
|
566
|
+
|
|
567
|
+
### Setup Development Environment
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
# Clone the repository
|
|
571
|
+
git clone https://github.com/your-org/axa-fr-oidc.git
|
|
572
|
+
cd axa-fr-oidc
|
|
573
|
+
|
|
574
|
+
# Install uv if not already installed
|
|
575
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
576
|
+
|
|
577
|
+
# Install dependencies
|
|
578
|
+
uv sync --group dev
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Using the Makefile
|
|
582
|
+
|
|
583
|
+
The project includes a `Makefile` for convenient development commands:
|
|
584
|
+
|
|
585
|
+
```bash
|
|
586
|
+
# Show all available commands
|
|
587
|
+
make help
|
|
588
|
+
|
|
589
|
+
# Install dependencies
|
|
590
|
+
make install # Production dependencies only
|
|
591
|
+
make install-dev # All development dependencies
|
|
592
|
+
make install-quality # Quality check tools only
|
|
593
|
+
make install-test # Test dependencies only
|
|
594
|
+
|
|
595
|
+
# Code quality
|
|
596
|
+
make lint # Run ruff linter (includes docstring checks)
|
|
597
|
+
make lint-fix # Run ruff linter with auto-fix
|
|
598
|
+
make format # Run ruff formatter
|
|
599
|
+
make format-check # Check formatting without changes
|
|
600
|
+
make type-check # Run mypy type checker
|
|
601
|
+
|
|
602
|
+
# Security
|
|
603
|
+
make security # Run bandit security checks
|
|
604
|
+
make security-audit # Run pip-audit for dependency vulnerabilities
|
|
605
|
+
|
|
606
|
+
# Testing
|
|
607
|
+
make test # Run tests
|
|
608
|
+
make test-cov # Run tests with coverage report
|
|
609
|
+
|
|
610
|
+
# Combined commands
|
|
611
|
+
make quality # Run all quality checks (lint, format, type-check, security)
|
|
612
|
+
make all # Run quality checks and tests
|
|
613
|
+
|
|
614
|
+
# Cleanup
|
|
615
|
+
make clean # Remove build artifacts and cache files
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Running Tests
|
|
619
|
+
|
|
620
|
+
```bash
|
|
621
|
+
# Using make
|
|
622
|
+
make test
|
|
623
|
+
|
|
624
|
+
# Or directly with uv
|
|
625
|
+
uv run pytest
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Running Quality Checks
|
|
629
|
+
|
|
630
|
+
```bash
|
|
631
|
+
# Run all quality checks at once
|
|
632
|
+
make quality
|
|
633
|
+
|
|
634
|
+
# Or run individual checks
|
|
635
|
+
make lint
|
|
636
|
+
make type-check
|
|
637
|
+
make security
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Installing Specific Dependency Groups
|
|
641
|
+
|
|
642
|
+
```bash
|
|
643
|
+
# Install only test dependencies
|
|
644
|
+
uv sync --group test
|
|
645
|
+
|
|
646
|
+
# Install only linting tools
|
|
647
|
+
uv sync --group lint
|
|
648
|
+
|
|
649
|
+
# Install only security tools
|
|
650
|
+
uv sync --group security
|
|
651
|
+
|
|
652
|
+
# Install everything for development
|
|
653
|
+
uv sync --group dev
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
## Contributing
|
|
657
|
+
|
|
658
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|