Skip to content

Commit 710841f

Browse files
committed
Merge branch 'main' into add-repl-dockerfile
2 parents daa9bcf + c6c5c49 commit 710841f

13 files changed

Lines changed: 653 additions & 413 deletions

.github/workflows/test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ jobs:
3535
env:
3636
BASE_URL: ${{ secrets.BASE_URL }}
3737
AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }}
38+
JWT_KEY: ${{ secrets.JWT_KEY }}
39+
CLIENT_ID: ${{ secrets.CLIENT_ID }}
40+
CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}

README.md

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,45 +37,133 @@ install dependencies.
3737
Install the SDK by running one of the following commands:
3838

3939
```bash
40-
composer require zitadel/client
40+
pip install zitadel_client
4141
```
4242

43-
### Authentication
43+
## Authentication Methods
4444

45-
The SDK supports three authentication methods:
45+
Your SDK offers three ways to authenticate with Zitadel. Each method has its
46+
own benefits—choose the one that fits your situation best.
4647

47-
1. Private Key JWT Authentication
48-
2. Client Credentials Grant
49-
3. Personal Access Tokens (PATs)
48+
#### 1. Private Key JWT Authentication
5049

51-
For most service user scenarios in Zitadel, private key JWT authentication
52-
is the recommended choice due to its benefits in security, performance, and control.
53-
However, client credentials authentication might be considered in specific
54-
situations where simplicity and trust between servers are priorities.
50+
**What is it?**
51+
You use a JSON Web Token (JWT) that you sign with a private key stored in a
52+
JSON file. This process creates a secure token.
5553

56-
For more details on these authentication methods, please refer
57-
to the [Zitadel documentation on authenticating service users](https://zitadel.com/docs/guides/integrate/service-users/authenticate-service-users).
54+
**When should you use it?**
55+
- **Best for production:** It offers strong security.
56+
- **Advanced control:** You can adjust token settings like expiration.
5857

58+
**How do you use it?**
59+
1. Save your private key in a JSON file.
60+
2. Use the provided method to load this key and create a JWT-based
61+
authenticator.
5962

60-
### Example
63+
**Example:**
6164

6265
```python
6366
import zitadel_client as zitadel
67+
from zitadel_client.auth.web_token_authenticator import WebTokenAuthenticator
68+
69+
base_url = "https://example.zitadel.com"
70+
key_file = "/path/to/jwt-key.json"
71+
72+
authenticator = WebTokenAuthenticator.from_json(base_url, key_file)
73+
zitadel = zitadel.Zitadel(authenticator)
74+
75+
try:
76+
response = zitadel.users.add_human_user({
77+
"username": "john.doe",
78+
"profile": {"givenName": "John", "familyName": "Doe"},
79+
"email": {"email": "john@doe.com"}
80+
})
81+
print("User created:", response)
82+
except Exception as e:
83+
print("Error:", e)
84+
```
85+
86+
#### 2. Client Credentials Grant
87+
88+
**What is it?**
89+
This method uses a client ID and client secret to get a secure access token,
90+
which is then used to authenticate.
91+
92+
**When should you use it?**
93+
- **Simple and straightforward:** Good for server-to-server communication.
94+
- **Trusted environments:** Use it when both servers are owned or trusted.
95+
96+
**How do you use it?**
97+
1. Provide your client ID and client secret.
98+
2. Build the authenticator
6499

65-
with zitadel.Zitadel("your-zitadel-base-url", 'your-valid-token') as client:
66-
try:
67-
response = client.users.add_human_user(
68-
body=zitadel.V2AddHumanUserRequest(
69-
username="john.doe",
70-
profile=zitadel.V2SetHumanProfile(given_name="John", family_name="Doe"),
71-
email=zitadel.V2SetHumanEmail(email="johndoe@doe.com")
72-
)
73-
)
74-
print("User created:", response)
75-
except Exception as e:
76-
raise e
100+
**Example:**
101+
102+
```python
103+
import zitadel_client as zitadel
104+
from zitadel_client.auth.client_credentials_authenticator import ClientCredentialsAuthenticator
105+
106+
base_url = "https://example.zitadel.com"
107+
client_id = "your-client-id"
108+
client_secret = "your-client-secret"
109+
110+
authenticator = ClientCredentialsAuthenticator.builder(base_url, client_id, client_secret).build()
111+
zitadel = zitadel.Zitadel(authenticator)
112+
113+
try:
114+
response = zitadel.users.add_human_user({
115+
"username": "john.doe",
116+
"profile": {"givenName": "John", "familyName": "Doe"},
117+
"email": {"email": "john@doe.com"}
118+
})
119+
print("User created:", response)
120+
except Exception as e:
121+
print("Error:", e)
77122
```
78123

124+
#### 3. Personal Access Tokens (PATs)
125+
126+
**What is it?**
127+
A Personal Access Token (PAT) is a pre-generated token that you can use to
128+
authenticate without exchanging credentials every time.
129+
130+
**When should you use it?**
131+
- **Easy to use:** Great for development or testing scenarios.
132+
- **Quick setup:** No need for dynamic token generation.
133+
134+
**How do you use it?**
135+
1. Obtain a valid personal access token from your account.
136+
2. Create the authenticator with: `PersonalAccessTokenAuthenticator`
137+
138+
**Example:**
139+
140+
```python
141+
import zitadel_client as zitadel
142+
from zitadel_client.auth.personal_access_token_authenticator import PersonalAccessTokenAuthenticator
143+
144+
base_url = "https://example.zitadel.com"
145+
valid_token = "your-valid-token"
146+
147+
authenticator = PersonalAccessTokenAuthenticator(base_url, valid_token)
148+
zitadel = zitadel.Zitadel(authenticator)
149+
150+
try:
151+
response = zitadel.users.add_human_user({
152+
"username": "john.doe",
153+
"profile": {"givenName": "John", "familyName": "Doe"},
154+
"email": {"email": "john@doe.com"}
155+
})
156+
print("User created:", response)
157+
except Exception as e:
158+
print("Error:", e)
159+
```
160+
161+
---
162+
163+
Choose the authentication method that best suits your needs based on your
164+
environment and security requirements. For more details, please refer to the
165+
[Zitadel documentation on authenticating service users](https://zitadel.com/docs/guides/integrate/service-users/authenticate-service-users).
166+
79167
### Debugging
80168
The SDK supports debug logging, which can be enabled for troubleshooting
81169
and debugging purposes. You can enable debug logging by setting the `debug`

spec/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22
from dotenv import load_dotenv
33

4+
45
@pytest.fixture(scope="session", autouse=True)
56
def load_env():
67
"""Load the .env file for the entire test session."""
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import os
2+
import uuid
3+
4+
import pytest
5+
6+
import zitadel_client as zitadel
7+
from zitadel_client.auth.client_credentials_authenticator import ClientCredentialsAuthenticator
8+
9+
10+
@pytest.fixture
11+
def client_id():
12+
"""Fixture to return a valid personal access token."""
13+
return os.getenv("CLIENT_ID")
14+
15+
16+
@pytest.fixture
17+
def client_secret():
18+
"""Fixture to return a valid personal access token."""
19+
return os.getenv("CLIENT_SECRET")
20+
21+
22+
@pytest.fixture
23+
def base_url():
24+
"""Fixture to return the base URL."""
25+
return os.getenv("BASE_URL")
26+
27+
28+
@pytest.fixture
29+
def user_id(client_id, client_secret, base_url):
30+
"""Fixture to create a user and return their ID."""
31+
with zitadel.Zitadel(ClientCredentialsAuthenticator.builder(base_url, client_id, client_secret).build()) as client:
32+
try:
33+
response = client.users.add_human_user(
34+
body=zitadel.models.V2AddHumanUserRequest(
35+
username=uuid.uuid4().hex,
36+
profile=zitadel.models.V2SetHumanProfile(given_name="John", family_name="Doe"),
37+
email=zitadel.models.V2SetHumanEmail(email=f"johndoe{uuid.uuid4().hex}@caos.ag")
38+
)
39+
)
40+
print("User created:", response)
41+
return response.user_id
42+
except Exception as e:
43+
pytest.fail(f"Exception while creating user: {e}")
44+
45+
46+
def test_should_deactivate_and_reactivate_user_with_valid_token(user_id, client_id, client_secret, base_url):
47+
"""Test to (de)activate the user with a valid token."""
48+
with zitadel.Zitadel(ClientCredentialsAuthenticator.builder(base_url, client_id, client_secret).build()) as client:
49+
try:
50+
deactivate_response = client.users.deactivate_user(user_id=user_id)
51+
print("User deactivated:", deactivate_response)
52+
53+
reactivate_response = client.users.reactivate_user(user_id=user_id)
54+
print("User reactivated:", reactivate_response)
55+
# Adjust based on actual response format
56+
# assert reactivate_response["status"] == "success"
57+
except Exception as e:
58+
pytest.fail(f"Exception when calling deactivate_user or reactivate_user with valid token: {e}")
59+
60+
61+
def test_should_not_deactivate_or_reactivate_user_with_invalid_token(user_id, base_url):
62+
"""Test to attempt (de)activating the user with an invalid token."""
63+
with zitadel.Zitadel(ClientCredentialsAuthenticator.builder(base_url, "id", "secret").build()) as client:
64+
try:
65+
client.users.deactivate_user(user_id=user_id)
66+
pytest.fail("Expected exception when deactivating user with invalid token, but got response.")
67+
except Exception as e:
68+
print("Caught expected UnauthorizedException:", e)
69+
70+
try:
71+
client.users.reactivate_user(user_id=user_id)
72+
pytest.fail("Expected exception when reactivating user with invalid token, but got response.")
73+
except Exception as e:
74+
print("Caught expected UnauthorizedException:", e)

spec/sdk_test_using_personal_access_token_authentication_spec.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
1-
import pytest
2-
import uuid
31
import os
2+
import uuid
3+
4+
import pytest
5+
46
import zitadel_client as zitadel
57
from zitadel_client.auth.personal_access_token_authenticator import PersonalAccessTokenAuthenticator
68
from zitadel_client.exceptions import UnauthorizedException
79

10+
811
@pytest.fixture
912
def valid_token():
1013
"""Fixture to return a valid personal access token."""
1114
return os.getenv("AUTH_TOKEN")
1215

16+
1317
@pytest.fixture
1418
def invalid_token():
1519
"""Fixture to return an invalid token."""
1620
return "whoops"
1721

22+
1823
@pytest.fixture
1924
def base_url():
2025
"""Fixture to return the base URL."""
2126
return os.getenv("BASE_URL")
2227

28+
2329
@pytest.fixture
2430
def user_id(valid_token, base_url):
2531
"""Fixture to create a user and return their ID."""
@@ -37,6 +43,7 @@ def user_id(valid_token, base_url):
3743
except Exception as e:
3844
pytest.fail(f"Exception while creating user: {e}")
3945

46+
4047
def test_should_deactivate_and_reactivate_user_with_valid_token(user_id, valid_token, base_url):
4148
"""Test to (de)activate the user with a valid token."""
4249
with zitadel.Zitadel(PersonalAccessTokenAuthenticator(base_url, valid_token)) as client:
@@ -51,6 +58,7 @@ def test_should_deactivate_and_reactivate_user_with_valid_token(user_id, valid_t
5158
except Exception as e:
5259
pytest.fail(f"Exception when calling deactivate_user or reactivate_user with valid token: {e}")
5360

61+
5462
def test_should_not_deactivate_or_reactivate_user_with_invalid_token(user_id, invalid_token, base_url):
5563
"""Test to attempt (de)activating the user with an invalid token."""
5664
with zitadel.Zitadel(PersonalAccessTokenAuthenticator(base_url, invalid_token)) as client:
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import os
2+
import tempfile
3+
import uuid
4+
5+
import pytest
6+
7+
import zitadel_client as zitadel
8+
from zitadel_client.auth.web_token_authenticator import WebTokenAuthenticator
9+
10+
11+
@pytest.fixture
12+
def key_file():
13+
jwt_key = os.getenv("JWT_KEY")
14+
if jwt_key is None:
15+
pytest.fail("JWT_KEY is not set in the environment")
16+
with tempfile.NamedTemporaryFile(delete=False, mode="w") as tf:
17+
tf.write(jwt_key)
18+
return tf.name
19+
20+
21+
@pytest.fixture
22+
def base_url():
23+
"""Fixture to return the base URL."""
24+
return os.getenv("BASE_URL")
25+
26+
27+
@pytest.fixture
28+
def user_id(key_file, base_url):
29+
"""Fixture to create a user and return their ID."""
30+
with zitadel.Zitadel(WebTokenAuthenticator.from_json(base_url, key_file)) as client:
31+
try:
32+
response = client.users.add_human_user(
33+
body=zitadel.models.V2AddHumanUserRequest(
34+
username=uuid.uuid4().hex,
35+
profile=zitadel.models.V2SetHumanProfile(given_name="John", family_name="Doe"),
36+
email=zitadel.models.V2SetHumanEmail(email=f"johndoe{uuid.uuid4().hex}@caos.ag")
37+
)
38+
)
39+
print("User created:", response)
40+
return response.user_id
41+
except Exception as e:
42+
pytest.fail(f"Exception while creating user: {e}")
43+
44+
45+
def test_should_deactivate_and_reactivate_user_with_valid_token(user_id, key_file, base_url):
46+
"""Test to (de)activate the user with a valid token."""
47+
with zitadel.Zitadel(WebTokenAuthenticator.from_json(base_url, key_file)) as client:
48+
try:
49+
deactivate_response = client.users.deactivate_user(user_id=user_id)
50+
print("User deactivated:", deactivate_response)
51+
52+
reactivate_response = client.users.reactivate_user(user_id=user_id)
53+
print("User reactivated:", reactivate_response)
54+
# Adjust based on actual response format
55+
# assert reactivate_response["status"] == "success"
56+
except Exception as e:
57+
pytest.fail(f"Exception when calling deactivate_user or reactivate_user with valid token: {e}")

0 commit comments

Comments
 (0)