Skip to content

Commit b42a0c6

Browse files
committed
feat: added documentation
1 parent d79a1d3 commit b42a0c6

File tree

1 file changed

+104
-1
lines changed

1 file changed

+104
-1
lines changed

docs/index.md

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,105 @@
1-
# Keycloack OIDC API Client
1+
# Keycloak OIDC API Client
22

3+
This is a (work in Progress) collection of client-side APIs to simplify the interaction with Keycloak via Python.
4+
5+
## Installation
6+
7+
```
8+
pip install keycloak-oidc-api-client
9+
```
10+
11+
## APIs Usage
12+
13+
### Init
14+
15+
```python
16+
from keycloak_oidc_api_client import Client
17+
18+
my_keycloak_base_url = "https://iam.acme.com"
19+
20+
client: Client = Client(base_url=my_keycloak_base_url)
21+
```
22+
23+
### Get the User Code
24+
25+
```python
26+
import webbrowser
27+
28+
from keycloak_oidc_api_client.api.default.generate_user_code import sync as generate_user_code
29+
from keycloak_oidc_api_client.models.user_code_request import UserCodeRequest
30+
from keycloak_oidc_api_client.models.user_code_response import UserCodeResponse
31+
32+
client_id: str = "my-client-id"
33+
default_realm: str = "my-realm"
34+
35+
with Client(base_url=my_keycloak_base_url) as keykoack_client:
36+
user_code_response: UserCodeResponse | Error | None = generate_user_code(
37+
realm=default_realm,
38+
client=keykoack_client,
39+
body=UserCodeRequest(client_id=client_id, scope="my-scope my-other-scope"),
40+
)
41+
42+
if isinstance(user_code_response, UserCodeResponse):
43+
webbrowser.open(user_code_response.verification_uri_complete)
44+
```
45+
46+
### Obtain a device token
47+
48+
```python
49+
import loguru
50+
import time
51+
52+
from keycloak_oidc_api_client.api.default.request_token import sync as request_token
53+
from keycloak_oidc_api_client.models.request_token import RequestToken
54+
from keycloak_oidc_api_client.models.request_token_response import RequestTokenResponse
55+
56+
request_token_response: RequestTokenResponse | Error | None = None
57+
58+
if not request_token_response and isinstance(user_code_response, UserCodeResponse):
59+
start_time = time.time()
60+
61+
with Client(base_url=my_keycloak_base_url) as keykoack_client:
62+
while time.time() - start_time < user_code_response.expires_in:
63+
request_token_response = request_token(
64+
realm=default_realm,
65+
client=keykoack_client,
66+
body=RequestToken(
67+
client_id=client_id,
68+
grant_type="urn:ietf:params:oauth:grant-type:device_code",
69+
device_code=user_code_response.device_code,
70+
),
71+
)
72+
73+
if isinstance(request_token_response, RequestTokenResponse):
74+
logger.success("Found it!")
75+
break
76+
else:
77+
logger.debug(f"User hasn't confirmed yet, sleeping for {user_code_response.interval} seconds")
78+
time.sleep(user_code_response.interval)
79+
```
80+
81+
### Access to a protected resource
82+
83+
| :warning: WARNING |
84+
|:--------------------------------------------------------------------|
85+
| This is a simple use case and it is not covered by this client APIs |
86+
87+
```python
88+
import httpx
89+
90+
my_resource_url = "https://acme.com/my/protected/resource.bak"
91+
92+
http_client: Client = httpx.Client()
93+
94+
response: Response = http_client.get(
95+
url=resource_url,
96+
headers={
97+
"Authorization": f"{request_token_response.token_type} {request_token_response.access_token}"
98+
},
99+
follow_redirects=True,
100+
)
101+
102+
with open("resource.bak", "wb") as download_file:
103+
for chunk in response.iter_bytes(chunk_size=8192):
104+
download_file.write(chunk)
105+
```

0 commit comments

Comments
 (0)