chub-dev 0.1.0 → 0.1.2-beta.0
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.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1401 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: identity
|
|
3
|
+
description: "Okta Python SDK coding guidelines for the Okta Management API using official libraries"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "python"
|
|
6
|
+
versions: "2.9.13"
|
|
7
|
+
updated-on: "2026-03-02"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "okta,identity,sso,oauth,authentication"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Okta Python SDK Coding Guidelines
|
|
13
|
+
|
|
14
|
+
You are an Okta API coding expert. Help me with writing code using the Okta Management API calling the official libraries and SDKs.
|
|
15
|
+
|
|
16
|
+
You can find the official SDK documentation and code samples here:
|
|
17
|
+
https://developer.okta.com/docs/reference/api/users/
|
|
18
|
+
https://github.com/okta/okta-sdk-python
|
|
19
|
+
|
|
20
|
+
## Golden Rule: Use the Correct and Current SDK
|
|
21
|
+
|
|
22
|
+
Always use the official Okta Python SDK to interact with the Okta Management API. This is the standard library for all Okta Management API interactions. Do not construct manual HTTP requests or use unofficial libraries.
|
|
23
|
+
|
|
24
|
+
- **Library Name:** Okta Python SDK
|
|
25
|
+
- **PyPI Package:** `okta`
|
|
26
|
+
- **Current Version:** 2.9.13
|
|
27
|
+
- **Important:** This SDK is for the Management API only. For Authentication API, construct your own HTTP requests.
|
|
28
|
+
|
|
29
|
+
**Installation:**
|
|
30
|
+
|
|
31
|
+
- **Correct:** `pip install okta`
|
|
32
|
+
|
|
33
|
+
**APIs and Usage:**
|
|
34
|
+
|
|
35
|
+
- **Correct:** `from okta.client import Client as OktaClient`
|
|
36
|
+
- **Correct:** `client = OktaClient(config)`
|
|
37
|
+
- **Correct:** `await client.create_user(user_request)`
|
|
38
|
+
- **Correct:** `users, resp, err = await client.list_users()`
|
|
39
|
+
- **Incorrect:** Manual HTTP requests to Management API endpoints
|
|
40
|
+
- **Incorrect:** Using unofficial Okta Python packages
|
|
41
|
+
|
|
42
|
+
## System Requirements
|
|
43
|
+
|
|
44
|
+
The Okta Python SDK requires:
|
|
45
|
+
- Python 3.9 or higher
|
|
46
|
+
- An Okta organization URL
|
|
47
|
+
- An API token or OAuth 2.0 credentials
|
|
48
|
+
|
|
49
|
+
## Initialization and API Authentication
|
|
50
|
+
|
|
51
|
+
The Okta Python SDK uses asynchronous operations with `async`/`await` syntax throughout.
|
|
52
|
+
|
|
53
|
+
### API Token Authentication (Simple)
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from okta.client import Client as OktaClient
|
|
57
|
+
import asyncio
|
|
58
|
+
import os
|
|
59
|
+
|
|
60
|
+
config = {
|
|
61
|
+
'orgUrl': os.getenv('OKTA_ORG_URL'),
|
|
62
|
+
'token': os.getenv('OKTA_API_TOKEN')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
client = OktaClient(config)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### OAuth 2.0 Private Key Authentication (Recommended for Service Apps)
|
|
69
|
+
|
|
70
|
+
When using OAuth 2.0 with private key authentication, you don't need an API token. The SDK automatically requests access tokens.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from okta.client import Client as OktaClient
|
|
74
|
+
import os
|
|
75
|
+
|
|
76
|
+
config = {
|
|
77
|
+
'orgUrl': os.getenv('OKTA_ORG_URL'),
|
|
78
|
+
'authorizationMode': 'PrivateKey',
|
|
79
|
+
'clientId': os.getenv('OKTA_CLIENT_ID'),
|
|
80
|
+
'scopes': ['okta.users.manage', 'okta.groups.manage'],
|
|
81
|
+
'privateKey': os.getenv('OKTA_PRIVATE_KEY'), # JWK JSON string or PEM format
|
|
82
|
+
'kid': os.getenv('OKTA_KEY_ID') # Optional if kid is in JWK
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
client = OktaClient(config)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Async Event Loop Pattern
|
|
89
|
+
|
|
90
|
+
All SDK operations are asynchronous and must be executed within an event loop:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import asyncio
|
|
94
|
+
from okta.client import Client as OktaClient
|
|
95
|
+
|
|
96
|
+
async def main():
|
|
97
|
+
config = {
|
|
98
|
+
'orgUrl': 'https://dev-1234567.okta.com',
|
|
99
|
+
'token': 'your_api_token'
|
|
100
|
+
}
|
|
101
|
+
client = OktaClient(config)
|
|
102
|
+
|
|
103
|
+
users, resp, err = await client.list_users()
|
|
104
|
+
print(f"Total users: {len(users)}")
|
|
105
|
+
|
|
106
|
+
# Run the async function
|
|
107
|
+
loop = asyncio.get_event_loop()
|
|
108
|
+
loop.run_until_complete(main())
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Response Pattern
|
|
112
|
+
|
|
113
|
+
All SDK methods return a tuple of `(result, response, error)`:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
users, resp, err = await client.list_users()
|
|
117
|
+
|
|
118
|
+
if err:
|
|
119
|
+
print(f"Error occurred: {err}")
|
|
120
|
+
else:
|
|
121
|
+
print(f"Retrieved {len(users)} users")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## User Management
|
|
125
|
+
|
|
126
|
+
### Create a User
|
|
127
|
+
|
|
128
|
+
Create a new user with profile information and password:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from okta.client import Client as OktaClient
|
|
132
|
+
from okta.models import UserProfile, PasswordCredential, UserCredentials, CreateUserRequest
|
|
133
|
+
import asyncio
|
|
134
|
+
|
|
135
|
+
async def create_user():
|
|
136
|
+
config = {
|
|
137
|
+
'orgUrl': 'https://dev-1234567.okta.com',
|
|
138
|
+
'token': 'your_api_token'
|
|
139
|
+
}
|
|
140
|
+
client = OktaClient(config)
|
|
141
|
+
|
|
142
|
+
# Create user profile
|
|
143
|
+
user_profile = UserProfile({
|
|
144
|
+
'firstName': 'John',
|
|
145
|
+
'lastName': 'Doe',
|
|
146
|
+
'email': 'john.doe@example.com',
|
|
147
|
+
'login': 'john.doe@example.com'
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
# Create password credential
|
|
151
|
+
password_credential = PasswordCredential({
|
|
152
|
+
'value': 'SecurePassword123!'
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
user_credentials = UserCredentials({
|
|
156
|
+
'password': password_credential
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
# Create user request
|
|
160
|
+
create_user_request = CreateUserRequest({
|
|
161
|
+
'profile': user_profile,
|
|
162
|
+
'credentials': user_credentials
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
# Create the user
|
|
166
|
+
user, resp, err = await client.create_user(create_user_request)
|
|
167
|
+
|
|
168
|
+
if err:
|
|
169
|
+
print(f"Error creating user: {err}")
|
|
170
|
+
else:
|
|
171
|
+
print(f"Created user: {user.id}")
|
|
172
|
+
return user
|
|
173
|
+
|
|
174
|
+
asyncio.run(create_user())
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Create User with Activation
|
|
178
|
+
|
|
179
|
+
Create and automatically activate a user:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
async def create_and_activate_user():
|
|
183
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
184
|
+
client = OktaClient(config)
|
|
185
|
+
|
|
186
|
+
user_profile = UserProfile({
|
|
187
|
+
'firstName': 'Jane',
|
|
188
|
+
'lastName': 'Smith',
|
|
189
|
+
'email': 'jane.smith@example.com',
|
|
190
|
+
'login': 'jane.smith@example.com',
|
|
191
|
+
'mobilePhone': '555-123-4567'
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
password_credential = PasswordCredential({'value': 'SecurePassword123!'})
|
|
195
|
+
user_credentials = UserCredentials({'password': password_credential})
|
|
196
|
+
|
|
197
|
+
create_user_request = CreateUserRequest({
|
|
198
|
+
'profile': user_profile,
|
|
199
|
+
'credentials': user_credentials
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
# Activate on creation
|
|
203
|
+
user, resp, err = await client.create_user(
|
|
204
|
+
create_user_request,
|
|
205
|
+
activate=True,
|
|
206
|
+
provider=False
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if err:
|
|
210
|
+
print(f"Error: {err}")
|
|
211
|
+
else:
|
|
212
|
+
print(f"Created and activated user: {user.id}")
|
|
213
|
+
return user
|
|
214
|
+
|
|
215
|
+
asyncio.run(create_and_activate_user())
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Get a User
|
|
219
|
+
|
|
220
|
+
Retrieve a user by ID or login:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
async def get_user(user_id):
|
|
224
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
225
|
+
client = OktaClient(config)
|
|
226
|
+
|
|
227
|
+
# By user ID or login email
|
|
228
|
+
user, resp, err = await client.get_user(user_id)
|
|
229
|
+
|
|
230
|
+
if err:
|
|
231
|
+
print(f"Error: {err}")
|
|
232
|
+
else:
|
|
233
|
+
print(f"User: {user.profile.first_name} {user.profile.last_name}")
|
|
234
|
+
print(f"Email: {user.profile.email}")
|
|
235
|
+
print(f"Status: {user.status}")
|
|
236
|
+
return user
|
|
237
|
+
|
|
238
|
+
# Can use ID or email
|
|
239
|
+
asyncio.run(get_user('00u1234567890abcdef'))
|
|
240
|
+
asyncio.run(get_user('john.doe@example.com'))
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### List All Users
|
|
244
|
+
|
|
245
|
+
Retrieve all users in your organization:
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
async def list_all_users():
|
|
249
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
250
|
+
client = OktaClient(config)
|
|
251
|
+
|
|
252
|
+
users, resp, err = await client.list_users()
|
|
253
|
+
|
|
254
|
+
if err:
|
|
255
|
+
print(f"Error: {err}")
|
|
256
|
+
else:
|
|
257
|
+
print(f"Total users: {len(users)}")
|
|
258
|
+
for user in users:
|
|
259
|
+
print(f" - {user.profile.first_name} {user.profile.last_name} ({user.profile.email})")
|
|
260
|
+
return users
|
|
261
|
+
|
|
262
|
+
asyncio.run(list_all_users())
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### List Users with Query Parameters
|
|
266
|
+
|
|
267
|
+
Filter users with query parameters:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
async def list_users_with_filters():
|
|
271
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
272
|
+
client = OktaClient(config)
|
|
273
|
+
|
|
274
|
+
# Search by query string
|
|
275
|
+
query_params = {'q': 'Robert'}
|
|
276
|
+
users, resp, err = await client.list_users(query_params)
|
|
277
|
+
|
|
278
|
+
if err:
|
|
279
|
+
print(f"Error: {err}")
|
|
280
|
+
else:
|
|
281
|
+
print(f"Found {len(users)} users matching 'Robert'")
|
|
282
|
+
for user in users:
|
|
283
|
+
print(f" - {user.profile.email}")
|
|
284
|
+
|
|
285
|
+
asyncio.run(list_users_with_filters())
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Search Users with SCIM Filter
|
|
289
|
+
|
|
290
|
+
Use SCIM expressions for precise filtering:
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
async def search_users_scim():
|
|
294
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
295
|
+
client = OktaClient(config)
|
|
296
|
+
|
|
297
|
+
# SCIM filter
|
|
298
|
+
query_params = {
|
|
299
|
+
'search': 'profile.nickName eq "bobby"'
|
|
300
|
+
}
|
|
301
|
+
users, resp, err = await client.list_users(query_params)
|
|
302
|
+
|
|
303
|
+
if err:
|
|
304
|
+
print(f"Error: {err}")
|
|
305
|
+
else:
|
|
306
|
+
for user in users:
|
|
307
|
+
print(f"Found: {user.profile.email}")
|
|
308
|
+
|
|
309
|
+
asyncio.run(search_users_scim())
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Filter Users by Time
|
|
313
|
+
|
|
314
|
+
Find users updated after a specific time:
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
async def find_recently_updated_users():
|
|
318
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
319
|
+
client = OktaClient(config)
|
|
320
|
+
|
|
321
|
+
query_params = {
|
|
322
|
+
'filter': 'lastUpdated gt "2025-01-01T00:00:00.000Z"'
|
|
323
|
+
}
|
|
324
|
+
users, resp, err = await client.list_users(query_params)
|
|
325
|
+
|
|
326
|
+
if err:
|
|
327
|
+
print(f"Error: {err}")
|
|
328
|
+
else:
|
|
329
|
+
print(f"Found {len(users)} recently updated users")
|
|
330
|
+
for user in users:
|
|
331
|
+
print(f" - {user.profile.email} (updated: {user.last_updated})")
|
|
332
|
+
|
|
333
|
+
asyncio.run(find_recently_updated_users())
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Update a User
|
|
337
|
+
|
|
338
|
+
Modify user profile information:
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
async def update_user(user_id):
|
|
342
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
343
|
+
client = OktaClient(config)
|
|
344
|
+
|
|
345
|
+
# Get the user first
|
|
346
|
+
user, resp, err = await client.get_user(user_id)
|
|
347
|
+
|
|
348
|
+
if err:
|
|
349
|
+
print(f"Error getting user: {err}")
|
|
350
|
+
return
|
|
351
|
+
|
|
352
|
+
# Update profile fields
|
|
353
|
+
user.profile.nick_name = 'Johnny'
|
|
354
|
+
user.profile.mobile_phone = '+1-555-123-4567'
|
|
355
|
+
user.profile.department = 'Engineering'
|
|
356
|
+
|
|
357
|
+
# Save changes
|
|
358
|
+
updated_user, resp, err = await client.update_user(user_id, user)
|
|
359
|
+
|
|
360
|
+
if err:
|
|
361
|
+
print(f"Error updating user: {err}")
|
|
362
|
+
else:
|
|
363
|
+
print(f"User updated successfully: {updated_user.id}")
|
|
364
|
+
|
|
365
|
+
asyncio.run(update_user('00u1234567890abcdef'))
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Partial Update a User
|
|
369
|
+
|
|
370
|
+
Update specific fields without retrieving the full user object:
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
from okta.models import User
|
|
374
|
+
|
|
375
|
+
async def partial_update_user(user_id):
|
|
376
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
377
|
+
client = OktaClient(config)
|
|
378
|
+
|
|
379
|
+
# Create a user object with only the fields to update
|
|
380
|
+
user = User({
|
|
381
|
+
'profile': {
|
|
382
|
+
'nickName': 'JD',
|
|
383
|
+
'department': 'Engineering'
|
|
384
|
+
}
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
updated_user, resp, err = await client.update_user(user_id, user)
|
|
388
|
+
|
|
389
|
+
if err:
|
|
390
|
+
print(f"Error: {err}")
|
|
391
|
+
else:
|
|
392
|
+
print(f"User partially updated: {updated_user.id}")
|
|
393
|
+
|
|
394
|
+
asyncio.run(partial_update_user('00u1234567890abcdef'))
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Deactivate a User
|
|
398
|
+
|
|
399
|
+
Deactivate a user account:
|
|
400
|
+
|
|
401
|
+
```python
|
|
402
|
+
async def deactivate_user(user_id):
|
|
403
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
404
|
+
client = OktaClient(config)
|
|
405
|
+
|
|
406
|
+
user, resp, err = await client.deactivate_user(user_id)
|
|
407
|
+
|
|
408
|
+
if err:
|
|
409
|
+
print(f"Error deactivating user: {err}")
|
|
410
|
+
else:
|
|
411
|
+
print(f"User deactivated: {user.id}")
|
|
412
|
+
|
|
413
|
+
asyncio.run(deactivate_user('00u1234567890abcdef'))
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Delete a User
|
|
417
|
+
|
|
418
|
+
Permanently delete a user (must be deactivated first):
|
|
419
|
+
|
|
420
|
+
```python
|
|
421
|
+
async def delete_user(user_id):
|
|
422
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
423
|
+
client = OktaClient(config)
|
|
424
|
+
|
|
425
|
+
# First deactivate
|
|
426
|
+
_, resp, err = await client.deactivate_user(user_id)
|
|
427
|
+
if err:
|
|
428
|
+
print(f"Error deactivating: {err}")
|
|
429
|
+
return
|
|
430
|
+
|
|
431
|
+
# Then delete
|
|
432
|
+
resp, err = await client.delete_user(user_id)
|
|
433
|
+
|
|
434
|
+
if err:
|
|
435
|
+
print(f"Error deleting user: {err}")
|
|
436
|
+
else:
|
|
437
|
+
print(f"User deleted permanently")
|
|
438
|
+
|
|
439
|
+
asyncio.run(delete_user('00u1234567890abcdef'))
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Reactivate a User
|
|
443
|
+
|
|
444
|
+
Reactivate a previously deactivated user:
|
|
445
|
+
|
|
446
|
+
```python
|
|
447
|
+
async def reactivate_user(user_id):
|
|
448
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
449
|
+
client = OktaClient(config)
|
|
450
|
+
|
|
451
|
+
query_params = {'sendEmail': False}
|
|
452
|
+
user, resp, err = await client.activate_user(user_id, query_params)
|
|
453
|
+
|
|
454
|
+
if err:
|
|
455
|
+
print(f"Error reactivating user: {err}")
|
|
456
|
+
else:
|
|
457
|
+
print(f"User reactivated: {user.id}")
|
|
458
|
+
|
|
459
|
+
asyncio.run(reactivate_user('00u1234567890abcdef'))
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Suspend and Unsuspend User
|
|
463
|
+
|
|
464
|
+
Temporarily suspend a user:
|
|
465
|
+
|
|
466
|
+
```python
|
|
467
|
+
async def suspend_user(user_id):
|
|
468
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
469
|
+
client = OktaClient(config)
|
|
470
|
+
|
|
471
|
+
user, resp, err = await client.suspend_user(user_id)
|
|
472
|
+
|
|
473
|
+
if err:
|
|
474
|
+
print(f"Error suspending user: {err}")
|
|
475
|
+
else:
|
|
476
|
+
print(f"User suspended: {user.id}")
|
|
477
|
+
|
|
478
|
+
async def unsuspend_user(user_id):
|
|
479
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
480
|
+
client = OktaClient(config)
|
|
481
|
+
|
|
482
|
+
user, resp, err = await client.unsuspend_user(user_id)
|
|
483
|
+
|
|
484
|
+
if err:
|
|
485
|
+
print(f"Error unsuspending user: {err}")
|
|
486
|
+
else:
|
|
487
|
+
print(f"User unsuspended: {user.id}")
|
|
488
|
+
|
|
489
|
+
asyncio.run(suspend_user('00u1234567890abcdef'))
|
|
490
|
+
asyncio.run(unsuspend_user('00u1234567890abcdef'))
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Reset User Password
|
|
494
|
+
|
|
495
|
+
Send a password reset email:
|
|
496
|
+
|
|
497
|
+
```python
|
|
498
|
+
async def reset_user_password(user_id):
|
|
499
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
500
|
+
client = OktaClient(config)
|
|
501
|
+
|
|
502
|
+
query_params = {'sendEmail': True}
|
|
503
|
+
reset_token, resp, err = await client.reset_password(user_id, query_params)
|
|
504
|
+
|
|
505
|
+
if err:
|
|
506
|
+
print(f"Error resetting password: {err}")
|
|
507
|
+
else:
|
|
508
|
+
print(f"Password reset email sent")
|
|
509
|
+
|
|
510
|
+
asyncio.run(reset_user_password('00u1234567890abcdef'))
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Expire User Password
|
|
514
|
+
|
|
515
|
+
Force a user to change password on next login:
|
|
516
|
+
|
|
517
|
+
```python
|
|
518
|
+
async def expire_user_password(user_id):
|
|
519
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
520
|
+
client = OktaClient(config)
|
|
521
|
+
|
|
522
|
+
user, resp, err = await client.expire_password(user_id)
|
|
523
|
+
|
|
524
|
+
if err:
|
|
525
|
+
print(f"Error expiring password: {err}")
|
|
526
|
+
else:
|
|
527
|
+
print(f"Password expired for user: {user.id}")
|
|
528
|
+
|
|
529
|
+
asyncio.run(expire_user_password('00u1234567890abcdef'))
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
## Group Management
|
|
533
|
+
|
|
534
|
+
### Create a Group
|
|
535
|
+
|
|
536
|
+
Create a new group:
|
|
537
|
+
|
|
538
|
+
```python
|
|
539
|
+
from okta.models import Group, GroupProfile
|
|
540
|
+
|
|
541
|
+
async def create_group():
|
|
542
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
543
|
+
client = OktaClient(config)
|
|
544
|
+
|
|
545
|
+
group_profile = GroupProfile({
|
|
546
|
+
'name': 'Engineering Team',
|
|
547
|
+
'description': 'All engineering department members'
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
new_group = Group({'profile': group_profile})
|
|
551
|
+
|
|
552
|
+
group, resp, err = await client.create_group(new_group)
|
|
553
|
+
|
|
554
|
+
if err:
|
|
555
|
+
print(f"Error creating group: {err}")
|
|
556
|
+
else:
|
|
557
|
+
print(f"Created group: {group.id}")
|
|
558
|
+
return group
|
|
559
|
+
|
|
560
|
+
asyncio.run(create_group())
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Get a Group
|
|
564
|
+
|
|
565
|
+
Retrieve a group by ID:
|
|
566
|
+
|
|
567
|
+
```python
|
|
568
|
+
async def get_group(group_id):
|
|
569
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
570
|
+
client = OktaClient(config)
|
|
571
|
+
|
|
572
|
+
group, resp, err = await client.get_group(group_id)
|
|
573
|
+
|
|
574
|
+
if err:
|
|
575
|
+
print(f"Error getting group: {err}")
|
|
576
|
+
else:
|
|
577
|
+
print(f"Group: {group.profile.name}")
|
|
578
|
+
print(f"Description: {group.profile.description}")
|
|
579
|
+
return group
|
|
580
|
+
|
|
581
|
+
asyncio.run(get_group('00g1234567890abcdef'))
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### List All Groups
|
|
585
|
+
|
|
586
|
+
List all groups in the organization:
|
|
587
|
+
|
|
588
|
+
```python
|
|
589
|
+
async def list_all_groups():
|
|
590
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
591
|
+
client = OktaClient(config)
|
|
592
|
+
|
|
593
|
+
groups, resp, err = await client.list_groups()
|
|
594
|
+
|
|
595
|
+
if err:
|
|
596
|
+
print(f"Error listing groups: {err}")
|
|
597
|
+
else:
|
|
598
|
+
print(f"Total groups: {len(groups)}")
|
|
599
|
+
for group in groups:
|
|
600
|
+
print(f" - {group.profile.name} ({group.id})")
|
|
601
|
+
return groups
|
|
602
|
+
|
|
603
|
+
asyncio.run(list_all_groups())
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### Search Groups by Name
|
|
607
|
+
|
|
608
|
+
Search for groups matching a query:
|
|
609
|
+
|
|
610
|
+
```python
|
|
611
|
+
async def search_groups(query):
|
|
612
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
613
|
+
client = OktaClient(config)
|
|
614
|
+
|
|
615
|
+
query_params = {'q': query}
|
|
616
|
+
groups, resp, err = await client.list_groups(query_params)
|
|
617
|
+
|
|
618
|
+
if err:
|
|
619
|
+
print(f"Error searching groups: {err}")
|
|
620
|
+
else:
|
|
621
|
+
print(f"Found {len(groups)} groups matching '{query}'")
|
|
622
|
+
for group in groups:
|
|
623
|
+
print(f" - {group.profile.name}")
|
|
624
|
+
|
|
625
|
+
asyncio.run(search_groups('Engineering'))
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Update a Group
|
|
629
|
+
|
|
630
|
+
Update group profile information:
|
|
631
|
+
|
|
632
|
+
```python
|
|
633
|
+
async def update_group(group_id):
|
|
634
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
635
|
+
client = OktaClient(config)
|
|
636
|
+
|
|
637
|
+
# Get the group first
|
|
638
|
+
group, resp, err = await client.get_group(group_id)
|
|
639
|
+
|
|
640
|
+
if err:
|
|
641
|
+
print(f"Error getting group: {err}")
|
|
642
|
+
return
|
|
643
|
+
|
|
644
|
+
# Update profile
|
|
645
|
+
group.profile.description = 'Updated description for the team'
|
|
646
|
+
|
|
647
|
+
# Save changes
|
|
648
|
+
updated_group, resp, err = await client.update_group(group_id, group)
|
|
649
|
+
|
|
650
|
+
if err:
|
|
651
|
+
print(f"Error updating group: {err}")
|
|
652
|
+
else:
|
|
653
|
+
print(f"Group updated: {updated_group.id}")
|
|
654
|
+
|
|
655
|
+
asyncio.run(update_group('00g1234567890abcdef'))
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### Delete a Group
|
|
659
|
+
|
|
660
|
+
Delete a group:
|
|
661
|
+
|
|
662
|
+
```python
|
|
663
|
+
async def delete_group(group_id):
|
|
664
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
665
|
+
client = OktaClient(config)
|
|
666
|
+
|
|
667
|
+
resp, err = await client.delete_group(group_id)
|
|
668
|
+
|
|
669
|
+
if err:
|
|
670
|
+
print(f"Error deleting group: {err}")
|
|
671
|
+
else:
|
|
672
|
+
print(f"Group deleted successfully")
|
|
673
|
+
|
|
674
|
+
asyncio.run(delete_group('00g1234567890abcdef'))
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### Assign User to Group
|
|
678
|
+
|
|
679
|
+
Add a user to a group:
|
|
680
|
+
|
|
681
|
+
```python
|
|
682
|
+
async def assign_user_to_group(group_id, user_id):
|
|
683
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
684
|
+
client = OktaClient(config)
|
|
685
|
+
|
|
686
|
+
resp, err = await client.add_user_to_group(group_id, user_id)
|
|
687
|
+
|
|
688
|
+
if err:
|
|
689
|
+
print(f"Error adding user to group: {err}")
|
|
690
|
+
else:
|
|
691
|
+
print(f"User {user_id} added to group {group_id}")
|
|
692
|
+
|
|
693
|
+
asyncio.run(assign_user_to_group('00g1234567890abcdef', '00u1234567890abcdef'))
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### Remove User from Group
|
|
697
|
+
|
|
698
|
+
Remove a user from a group:
|
|
699
|
+
|
|
700
|
+
```python
|
|
701
|
+
async def remove_user_from_group(group_id, user_id):
|
|
702
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
703
|
+
client = OktaClient(config)
|
|
704
|
+
|
|
705
|
+
resp, err = await client.remove_user_from_group(group_id, user_id)
|
|
706
|
+
|
|
707
|
+
if err:
|
|
708
|
+
print(f"Error removing user from group: {err}")
|
|
709
|
+
else:
|
|
710
|
+
print(f"User {user_id} removed from group {group_id}")
|
|
711
|
+
|
|
712
|
+
asyncio.run(remove_user_from_group('00g1234567890abcdef', '00u1234567890abcdef'))
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### List Group Members
|
|
716
|
+
|
|
717
|
+
Get all users in a group:
|
|
718
|
+
|
|
719
|
+
```python
|
|
720
|
+
async def list_group_members(group_id):
|
|
721
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
722
|
+
client = OktaClient(config)
|
|
723
|
+
|
|
724
|
+
users, resp, err = await client.list_group_users(group_id)
|
|
725
|
+
|
|
726
|
+
if err:
|
|
727
|
+
print(f"Error listing group members: {err}")
|
|
728
|
+
else:
|
|
729
|
+
print(f"Group has {len(users)} members:")
|
|
730
|
+
for user in users:
|
|
731
|
+
print(f" - {user.profile.first_name} {user.profile.last_name}")
|
|
732
|
+
|
|
733
|
+
asyncio.run(list_group_members('00g1234567890abcdef'))
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### List User's Groups
|
|
737
|
+
|
|
738
|
+
Get all groups a user belongs to:
|
|
739
|
+
|
|
740
|
+
```python
|
|
741
|
+
async def list_user_groups(user_id):
|
|
742
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
743
|
+
client = OktaClient(config)
|
|
744
|
+
|
|
745
|
+
groups, resp, err = await client.list_user_groups(user_id)
|
|
746
|
+
|
|
747
|
+
if err:
|
|
748
|
+
print(f"Error listing user groups: {err}")
|
|
749
|
+
else:
|
|
750
|
+
print(f"User belongs to {len(groups)} groups:")
|
|
751
|
+
for group in groups:
|
|
752
|
+
print(f" - {group.profile.name}")
|
|
753
|
+
|
|
754
|
+
asyncio.run(list_user_groups('00u1234567890abcdef'))
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
## Application Management
|
|
758
|
+
|
|
759
|
+
### Create an Application
|
|
760
|
+
|
|
761
|
+
Create a basic authentication application:
|
|
762
|
+
|
|
763
|
+
```python
|
|
764
|
+
from okta.models import Application
|
|
765
|
+
|
|
766
|
+
async def create_application():
|
|
767
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
768
|
+
client = OktaClient(config)
|
|
769
|
+
|
|
770
|
+
application = Application({
|
|
771
|
+
'name': 'template_basic_auth',
|
|
772
|
+
'label': 'Sample Basic Auth App',
|
|
773
|
+
'signOnMode': 'BASIC_AUTH',
|
|
774
|
+
'settings': {
|
|
775
|
+
'app': {
|
|
776
|
+
'url': 'https://example.com/auth.htm',
|
|
777
|
+
'authURL': 'https://example.com/login.html'
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
})
|
|
781
|
+
|
|
782
|
+
app, resp, err = await client.create_application(application)
|
|
783
|
+
|
|
784
|
+
if err:
|
|
785
|
+
print(f"Error creating application: {err}")
|
|
786
|
+
else:
|
|
787
|
+
print(f"Created application: {app.id}")
|
|
788
|
+
return app
|
|
789
|
+
|
|
790
|
+
asyncio.run(create_application())
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### Create an OAuth 2.0 Application
|
|
794
|
+
|
|
795
|
+
```python
|
|
796
|
+
async def create_oauth_application():
|
|
797
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
798
|
+
client = OktaClient(config)
|
|
799
|
+
|
|
800
|
+
application = Application({
|
|
801
|
+
'name': 'oidc_client',
|
|
802
|
+
'label': 'OAuth 2.0 App',
|
|
803
|
+
'signOnMode': 'OPENID_CONNECT',
|
|
804
|
+
'credentials': {
|
|
805
|
+
'oauthClient': {
|
|
806
|
+
'token_endpoint_auth_method': 'client_secret_post'
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
'settings': {
|
|
810
|
+
'oauthClient': {
|
|
811
|
+
'client_uri': 'https://example.com',
|
|
812
|
+
'logo_uri': 'https://example.com/logo.png',
|
|
813
|
+
'redirect_uris': ['https://example.com/oauth/callback'],
|
|
814
|
+
'response_types': ['code'],
|
|
815
|
+
'grant_types': ['authorization_code', 'refresh_token'],
|
|
816
|
+
'application_type': 'web'
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
})
|
|
820
|
+
|
|
821
|
+
app, resp, err = await client.create_application(application)
|
|
822
|
+
|
|
823
|
+
if err:
|
|
824
|
+
print(f"Error creating OAuth app: {err}")
|
|
825
|
+
else:
|
|
826
|
+
print(f"Created OAuth app: {app.id}")
|
|
827
|
+
if hasattr(app.credentials, 'oauthClient'):
|
|
828
|
+
print(f"Client ID: {app.credentials.oauthClient.client_id}")
|
|
829
|
+
print(f"Client Secret: {app.credentials.oauthClient.client_secret}")
|
|
830
|
+
return app
|
|
831
|
+
|
|
832
|
+
asyncio.run(create_oauth_application())
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
### Get an Application
|
|
836
|
+
|
|
837
|
+
Retrieve an application by ID:
|
|
838
|
+
|
|
839
|
+
```python
|
|
840
|
+
async def get_application(app_id):
|
|
841
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
842
|
+
client = OktaClient(config)
|
|
843
|
+
|
|
844
|
+
app, resp, err = await client.get_application(app_id)
|
|
845
|
+
|
|
846
|
+
if err:
|
|
847
|
+
print(f"Error getting application: {err}")
|
|
848
|
+
else:
|
|
849
|
+
print(f"Application: {app.label}")
|
|
850
|
+
print(f"Sign-on mode: {app.sign_on_mode}")
|
|
851
|
+
return app
|
|
852
|
+
|
|
853
|
+
asyncio.run(get_application('0oa1234567890abcdef'))
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
### List All Applications
|
|
857
|
+
|
|
858
|
+
```python
|
|
859
|
+
async def list_all_applications():
|
|
860
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
861
|
+
client = OktaClient(config)
|
|
862
|
+
|
|
863
|
+
apps, resp, err = await client.list_applications()
|
|
864
|
+
|
|
865
|
+
if err:
|
|
866
|
+
print(f"Error listing applications: {err}")
|
|
867
|
+
else:
|
|
868
|
+
print(f"Total applications: {len(apps)}")
|
|
869
|
+
for app in apps:
|
|
870
|
+
print(f" - {app.label} ({app.id})")
|
|
871
|
+
return apps
|
|
872
|
+
|
|
873
|
+
asyncio.run(list_all_applications())
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### Update an Application
|
|
877
|
+
|
|
878
|
+
```python
|
|
879
|
+
async def update_application(app_id):
|
|
880
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
881
|
+
client = OktaClient(config)
|
|
882
|
+
|
|
883
|
+
# Get the application first
|
|
884
|
+
app, resp, err = await client.get_application(app_id)
|
|
885
|
+
|
|
886
|
+
if err:
|
|
887
|
+
print(f"Error getting application: {err}")
|
|
888
|
+
return
|
|
889
|
+
|
|
890
|
+
# Update label
|
|
891
|
+
app.label = 'Updated Application Name'
|
|
892
|
+
|
|
893
|
+
# Save changes
|
|
894
|
+
updated_app, resp, err = await client.update_application(app_id, app)
|
|
895
|
+
|
|
896
|
+
if err:
|
|
897
|
+
print(f"Error updating application: {err}")
|
|
898
|
+
else:
|
|
899
|
+
print(f"Application updated: {updated_app.id}")
|
|
900
|
+
|
|
901
|
+
asyncio.run(update_application('0oa1234567890abcdef'))
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
### Delete an Application
|
|
905
|
+
|
|
906
|
+
```python
|
|
907
|
+
async def delete_application(app_id):
|
|
908
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
909
|
+
client = OktaClient(config)
|
|
910
|
+
|
|
911
|
+
# First deactivate
|
|
912
|
+
resp, err = await client.deactivate_application(app_id)
|
|
913
|
+
if err:
|
|
914
|
+
print(f"Error deactivating application: {err}")
|
|
915
|
+
return
|
|
916
|
+
|
|
917
|
+
# Then delete
|
|
918
|
+
resp, err = await client.delete_application(app_id)
|
|
919
|
+
|
|
920
|
+
if err:
|
|
921
|
+
print(f"Error deleting application: {err}")
|
|
922
|
+
else:
|
|
923
|
+
print(f"Application deleted successfully")
|
|
924
|
+
|
|
925
|
+
asyncio.run(delete_application('0oa1234567890abcdef'))
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
### Assign User to Application
|
|
929
|
+
|
|
930
|
+
```python
|
|
931
|
+
from okta.models import AppUser
|
|
932
|
+
|
|
933
|
+
async def assign_user_to_application(app_id, user_id):
|
|
934
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
935
|
+
client = OktaClient(config)
|
|
936
|
+
|
|
937
|
+
app_user = AppUser({'id': user_id})
|
|
938
|
+
|
|
939
|
+
assigned_user, resp, err = await client.assign_user_to_application(app_id, app_user)
|
|
940
|
+
|
|
941
|
+
if err:
|
|
942
|
+
print(f"Error assigning user to application: {err}")
|
|
943
|
+
else:
|
|
944
|
+
print(f"User {user_id} assigned to application {app_id}")
|
|
945
|
+
return assigned_user
|
|
946
|
+
|
|
947
|
+
asyncio.run(assign_user_to_application('0oa1234567890abcdef', '00u1234567890abcdef'))
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
### Assign User with Credentials to Application
|
|
951
|
+
|
|
952
|
+
```python
|
|
953
|
+
async def assign_user_with_credentials(app_id, user_id):
|
|
954
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
955
|
+
client = OktaClient(config)
|
|
956
|
+
|
|
957
|
+
app_user = AppUser({
|
|
958
|
+
'id': user_id,
|
|
959
|
+
'credentials': {
|
|
960
|
+
'userName': 'user@example.com',
|
|
961
|
+
'password': {'value': 'AppSpecificPassword123!'}
|
|
962
|
+
},
|
|
963
|
+
'profile': {
|
|
964
|
+
'role': 'Admin',
|
|
965
|
+
'department': 'Engineering'
|
|
966
|
+
}
|
|
967
|
+
})
|
|
968
|
+
|
|
969
|
+
assigned_user, resp, err = await client.assign_user_to_application(app_id, app_user)
|
|
970
|
+
|
|
971
|
+
if err:
|
|
972
|
+
print(f"Error: {err}")
|
|
973
|
+
else:
|
|
974
|
+
print(f"User assigned with credentials")
|
|
975
|
+
|
|
976
|
+
asyncio.run(assign_user_with_credentials('0oa1234567890abcdef', '00u1234567890abcdef'))
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
### Remove User from Application
|
|
980
|
+
|
|
981
|
+
```python
|
|
982
|
+
async def remove_user_from_application(app_id, user_id):
|
|
983
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
984
|
+
client = OktaClient(config)
|
|
985
|
+
|
|
986
|
+
resp, err = await client.delete_application_user(app_id, user_id)
|
|
987
|
+
|
|
988
|
+
if err:
|
|
989
|
+
print(f"Error removing user from application: {err}")
|
|
990
|
+
else:
|
|
991
|
+
print(f"User {user_id} removed from application {app_id}")
|
|
992
|
+
|
|
993
|
+
asyncio.run(remove_user_from_application('0oa1234567890abcdef', '00u1234567890abcdef'))
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
### Assign Group to Application
|
|
997
|
+
|
|
998
|
+
```python
|
|
999
|
+
from okta.models import ApplicationGroupAssignment
|
|
1000
|
+
|
|
1001
|
+
async def assign_group_to_application(app_id, group_id):
|
|
1002
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
1003
|
+
client = OktaClient(config)
|
|
1004
|
+
|
|
1005
|
+
assignment = ApplicationGroupAssignment({'priority': 0})
|
|
1006
|
+
|
|
1007
|
+
result, resp, err = await client.create_application_group_assignment(
|
|
1008
|
+
app_id,
|
|
1009
|
+
group_id,
|
|
1010
|
+
assignment
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
if err:
|
|
1014
|
+
print(f"Error assigning group to application: {err}")
|
|
1015
|
+
else:
|
|
1016
|
+
print(f"Group {group_id} assigned to application {app_id}")
|
|
1017
|
+
return result
|
|
1018
|
+
|
|
1019
|
+
asyncio.run(assign_group_to_application('0oa1234567890abcdef', '00g1234567890abcdef'))
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
### Remove Group from Application
|
|
1023
|
+
|
|
1024
|
+
```python
|
|
1025
|
+
async def remove_group_from_application(app_id, group_id):
|
|
1026
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
1027
|
+
client = OktaClient(config)
|
|
1028
|
+
|
|
1029
|
+
resp, err = await client.delete_application_group_assignment(app_id, group_id)
|
|
1030
|
+
|
|
1031
|
+
if err:
|
|
1032
|
+
print(f"Error removing group from application: {err}")
|
|
1033
|
+
else:
|
|
1034
|
+
print(f"Group {group_id} removed from application {app_id}")
|
|
1035
|
+
|
|
1036
|
+
asyncio.run(remove_group_from_application('0oa1234567890abcdef', '00g1234567890abcdef'))
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
### List Application Users
|
|
1040
|
+
|
|
1041
|
+
Get all users assigned to an application:
|
|
1042
|
+
|
|
1043
|
+
```python
|
|
1044
|
+
async def list_application_users(app_id):
|
|
1045
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
1046
|
+
client = OktaClient(config)
|
|
1047
|
+
|
|
1048
|
+
app_users, resp, err = await client.list_application_users(app_id)
|
|
1049
|
+
|
|
1050
|
+
if err:
|
|
1051
|
+
print(f"Error listing application users: {err}")
|
|
1052
|
+
else:
|
|
1053
|
+
print(f"Application has {len(app_users)} users:")
|
|
1054
|
+
for app_user in app_users:
|
|
1055
|
+
print(f" - User ID: {app_user.id}")
|
|
1056
|
+
|
|
1057
|
+
asyncio.run(list_application_users('0oa1234567890abcdef'))
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
### List Application Groups
|
|
1061
|
+
|
|
1062
|
+
Get all groups assigned to an application:
|
|
1063
|
+
|
|
1064
|
+
```python
|
|
1065
|
+
async def list_application_groups(app_id):
|
|
1066
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
1067
|
+
client = OktaClient(config)
|
|
1068
|
+
|
|
1069
|
+
groups, resp, err = await client.list_application_group_assignments(app_id)
|
|
1070
|
+
|
|
1071
|
+
if err:
|
|
1072
|
+
print(f"Error listing application groups: {err}")
|
|
1073
|
+
else:
|
|
1074
|
+
print(f"Application has {len(groups)} groups assigned:")
|
|
1075
|
+
for group in groups:
|
|
1076
|
+
print(f" - Group ID: {group.id}")
|
|
1077
|
+
|
|
1078
|
+
asyncio.run(list_application_groups('0oa1234567890abcdef'))
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
## Custom Headers Management
|
|
1082
|
+
|
|
1083
|
+
Set custom headers sent with every request (requires SDK v1.3.0+):
|
|
1084
|
+
|
|
1085
|
+
```python
|
|
1086
|
+
async def use_custom_headers():
|
|
1087
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
1088
|
+
client = OktaClient(config)
|
|
1089
|
+
|
|
1090
|
+
# Set custom headers
|
|
1091
|
+
client.set_custom_headers({'Custom-Header': 'custom value', 'X-Request-ID': '12345'})
|
|
1092
|
+
|
|
1093
|
+
# Make API calls with custom headers
|
|
1094
|
+
users, resp, err = await client.list_users()
|
|
1095
|
+
|
|
1096
|
+
# Get current custom headers
|
|
1097
|
+
headers = client.get_custom_headers()
|
|
1098
|
+
print(f"Custom headers: {headers}")
|
|
1099
|
+
|
|
1100
|
+
# Clear custom headers
|
|
1101
|
+
client.clear_custom_headers()
|
|
1102
|
+
print(f"Headers after clear: {client.get_custom_headers()}")
|
|
1103
|
+
|
|
1104
|
+
asyncio.run(use_custom_headers())
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
## Error Handling
|
|
1108
|
+
|
|
1109
|
+
Always check the error return value:
|
|
1110
|
+
|
|
1111
|
+
```python
|
|
1112
|
+
async def handle_errors():
|
|
1113
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
1114
|
+
client = OktaClient(config)
|
|
1115
|
+
|
|
1116
|
+
user, resp, err = await client.get_user('nonexistent@example.com')
|
|
1117
|
+
|
|
1118
|
+
if err:
|
|
1119
|
+
print(f"Error occurred: {err}")
|
|
1120
|
+
if hasattr(err, 'status'):
|
|
1121
|
+
if err.status == 404:
|
|
1122
|
+
print("User not found")
|
|
1123
|
+
elif err.status == 401:
|
|
1124
|
+
print("Authentication failed - check your API token")
|
|
1125
|
+
elif err.status == 403:
|
|
1126
|
+
print("Forbidden - insufficient permissions")
|
|
1127
|
+
elif err.status == 429:
|
|
1128
|
+
print("Rate limit exceeded")
|
|
1129
|
+
else:
|
|
1130
|
+
print(f"User found: {user.profile.email}")
|
|
1131
|
+
|
|
1132
|
+
asyncio.run(handle_errors())
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
### Comprehensive Error Handling Pattern
|
|
1136
|
+
|
|
1137
|
+
```python
|
|
1138
|
+
async def create_user_with_error_handling(user_data):
|
|
1139
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
1140
|
+
client = OktaClient(config)
|
|
1141
|
+
|
|
1142
|
+
try:
|
|
1143
|
+
user_profile = UserProfile(user_data['profile'])
|
|
1144
|
+
password_credential = PasswordCredential({'value': user_data['password']})
|
|
1145
|
+
user_credentials = UserCredentials({'password': password_credential})
|
|
1146
|
+
|
|
1147
|
+
create_user_request = CreateUserRequest({
|
|
1148
|
+
'profile': user_profile,
|
|
1149
|
+
'credentials': user_credentials
|
|
1150
|
+
})
|
|
1151
|
+
|
|
1152
|
+
user, resp, err = await client.create_user(create_user_request)
|
|
1153
|
+
|
|
1154
|
+
if err:
|
|
1155
|
+
print(f"Error creating user: {err}")
|
|
1156
|
+
|
|
1157
|
+
if hasattr(err, 'status'):
|
|
1158
|
+
if err.status == 400:
|
|
1159
|
+
print("Bad request - check user data format")
|
|
1160
|
+
elif err.status == 401:
|
|
1161
|
+
print("Authentication failed")
|
|
1162
|
+
elif err.status == 403:
|
|
1163
|
+
print("Forbidden - insufficient permissions")
|
|
1164
|
+
elif err.status == 409:
|
|
1165
|
+
print("Conflict - user already exists")
|
|
1166
|
+
elif err.status == 429:
|
|
1167
|
+
print("Rate limit exceeded - retry after delay")
|
|
1168
|
+
elif err.status >= 500:
|
|
1169
|
+
print("Server error - Okta service issue")
|
|
1170
|
+
|
|
1171
|
+
return {'success': False, 'error': str(err)}
|
|
1172
|
+
else:
|
|
1173
|
+
print(f"User created successfully: {user.id}")
|
|
1174
|
+
return {'success': True, 'user': user}
|
|
1175
|
+
|
|
1176
|
+
except Exception as e:
|
|
1177
|
+
print(f"Unexpected error: {e}")
|
|
1178
|
+
return {'success': False, 'error': str(e)}
|
|
1179
|
+
|
|
1180
|
+
user_data = {
|
|
1181
|
+
'profile': {
|
|
1182
|
+
'firstName': 'Alice',
|
|
1183
|
+
'lastName': 'Johnson',
|
|
1184
|
+
'email': 'alice.johnson@example.com',
|
|
1185
|
+
'login': 'alice.johnson@example.com'
|
|
1186
|
+
},
|
|
1187
|
+
'password': 'SecurePassword123!'
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
asyncio.run(create_user_with_error_handling(user_data))
|
|
1191
|
+
```
|
|
1192
|
+
|
|
1193
|
+
## Pagination
|
|
1194
|
+
|
|
1195
|
+
Handle pagination for large result sets:
|
|
1196
|
+
|
|
1197
|
+
```python
|
|
1198
|
+
async def paginate_users():
|
|
1199
|
+
config = {'orgUrl': 'https://dev-1234567.okta.com', 'token': 'your_api_token'}
|
|
1200
|
+
client = OktaClient(config)
|
|
1201
|
+
|
|
1202
|
+
# Limit results per page
|
|
1203
|
+
query_params = {'limit': 20}
|
|
1204
|
+
|
|
1205
|
+
users, resp, err = await client.list_users(query_params)
|
|
1206
|
+
|
|
1207
|
+
if err:
|
|
1208
|
+
print(f"Error: {err}")
|
|
1209
|
+
else:
|
|
1210
|
+
print(f"Retrieved {len(users)} users")
|
|
1211
|
+
|
|
1212
|
+
# Check if there are more pages
|
|
1213
|
+
if hasattr(resp, 'has_next') and resp.has_next():
|
|
1214
|
+
print("More pages available")
|
|
1215
|
+
|
|
1216
|
+
asyncio.run(paginate_users())
|
|
1217
|
+
```
|
|
1218
|
+
|
|
1219
|
+
## Complete Example Application
|
|
1220
|
+
|
|
1221
|
+
```python
|
|
1222
|
+
import asyncio
|
|
1223
|
+
import os
|
|
1224
|
+
from okta.client import Client as OktaClient
|
|
1225
|
+
from okta.models import (
|
|
1226
|
+
UserProfile, PasswordCredential, UserCredentials, CreateUserRequest,
|
|
1227
|
+
Group, GroupProfile, Application
|
|
1228
|
+
)
|
|
1229
|
+
|
|
1230
|
+
async def main():
|
|
1231
|
+
# Initialize client
|
|
1232
|
+
config = {
|
|
1233
|
+
'orgUrl': os.getenv('OKTA_ORG_URL'),
|
|
1234
|
+
'token': os.getenv('OKTA_API_TOKEN')
|
|
1235
|
+
}
|
|
1236
|
+
client = OktaClient(config)
|
|
1237
|
+
|
|
1238
|
+
try:
|
|
1239
|
+
# Create a user
|
|
1240
|
+
print("Creating user...")
|
|
1241
|
+
user_profile = UserProfile({
|
|
1242
|
+
'firstName': 'Alice',
|
|
1243
|
+
'lastName': 'Johnson',
|
|
1244
|
+
'email': 'alice.johnson@example.com',
|
|
1245
|
+
'login': 'alice.johnson@example.com'
|
|
1246
|
+
})
|
|
1247
|
+
|
|
1248
|
+
password_credential = PasswordCredential({'value': 'SecurePassword123!'})
|
|
1249
|
+
user_credentials = UserCredentials({'password': password_credential})
|
|
1250
|
+
|
|
1251
|
+
create_user_request = CreateUserRequest({
|
|
1252
|
+
'profile': user_profile,
|
|
1253
|
+
'credentials': user_credentials
|
|
1254
|
+
})
|
|
1255
|
+
|
|
1256
|
+
user, resp, err = await client.create_user(create_user_request, activate=True)
|
|
1257
|
+
if err:
|
|
1258
|
+
print(f"Error creating user: {err}")
|
|
1259
|
+
return
|
|
1260
|
+
print(f"Created user: {user.id}")
|
|
1261
|
+
|
|
1262
|
+
# Create a group
|
|
1263
|
+
print("\nCreating group...")
|
|
1264
|
+
group_profile = GroupProfile({
|
|
1265
|
+
'name': 'Project Team Alpha',
|
|
1266
|
+
'description': 'Members of Project Alpha'
|
|
1267
|
+
})
|
|
1268
|
+
new_group = Group({'profile': group_profile})
|
|
1269
|
+
|
|
1270
|
+
group, resp, err = await client.create_group(new_group)
|
|
1271
|
+
if err:
|
|
1272
|
+
print(f"Error creating group: {err}")
|
|
1273
|
+
return
|
|
1274
|
+
print(f"Created group: {group.id}")
|
|
1275
|
+
|
|
1276
|
+
# Add user to group
|
|
1277
|
+
print("\nAdding user to group...")
|
|
1278
|
+
resp, err = await client.add_user_to_group(group.id, user.id)
|
|
1279
|
+
if err:
|
|
1280
|
+
print(f"Error adding user to group: {err}")
|
|
1281
|
+
else:
|
|
1282
|
+
print("User added to group successfully")
|
|
1283
|
+
|
|
1284
|
+
# Create an application
|
|
1285
|
+
print("\nCreating application...")
|
|
1286
|
+
application = Application({
|
|
1287
|
+
'name': 'template_basic_auth',
|
|
1288
|
+
'label': 'Team Application',
|
|
1289
|
+
'signOnMode': 'BASIC_AUTH',
|
|
1290
|
+
'settings': {
|
|
1291
|
+
'app': {
|
|
1292
|
+
'url': 'https://example.com/app',
|
|
1293
|
+
'authURL': 'https://example.com/login'
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
})
|
|
1297
|
+
|
|
1298
|
+
app, resp, err = await client.create_application(application)
|
|
1299
|
+
if err:
|
|
1300
|
+
print(f"Error creating application: {err}")
|
|
1301
|
+
return
|
|
1302
|
+
print(f"Created application: {app.id}")
|
|
1303
|
+
|
|
1304
|
+
# Assign group to application
|
|
1305
|
+
print("\nAssigning group to application...")
|
|
1306
|
+
from okta.models import ApplicationGroupAssignment
|
|
1307
|
+
assignment = ApplicationGroupAssignment({'priority': 0})
|
|
1308
|
+
|
|
1309
|
+
result, resp, err = await client.create_application_group_assignment(
|
|
1310
|
+
app.id,
|
|
1311
|
+
group.id,
|
|
1312
|
+
assignment
|
|
1313
|
+
)
|
|
1314
|
+
if err:
|
|
1315
|
+
print(f"Error assigning group: {err}")
|
|
1316
|
+
else:
|
|
1317
|
+
print("Group assigned to application successfully")
|
|
1318
|
+
|
|
1319
|
+
# List all users in the group
|
|
1320
|
+
print("\nGroup members:")
|
|
1321
|
+
members, resp, err = await client.list_group_users(group.id)
|
|
1322
|
+
if err:
|
|
1323
|
+
print(f"Error listing group members: {err}")
|
|
1324
|
+
else:
|
|
1325
|
+
for member in members:
|
|
1326
|
+
print(f" - {member.profile.first_name} {member.profile.last_name}")
|
|
1327
|
+
|
|
1328
|
+
print("\n✅ All operations completed successfully!")
|
|
1329
|
+
|
|
1330
|
+
except Exception as e:
|
|
1331
|
+
print(f"Unexpected error: {e}")
|
|
1332
|
+
|
|
1333
|
+
if __name__ == '__main__':
|
|
1334
|
+
asyncio.run(main())
|
|
1335
|
+
```
|
|
1336
|
+
|
|
1337
|
+
## Environment Variables Setup
|
|
1338
|
+
|
|
1339
|
+
Create a `.env` file for configuration:
|
|
1340
|
+
|
|
1341
|
+
```bash
|
|
1342
|
+
OKTA_ORG_URL=https://dev-1234567.okta.com
|
|
1343
|
+
OKTA_API_TOKEN=your_api_token_here
|
|
1344
|
+
```
|
|
1345
|
+
|
|
1346
|
+
Load environment variables:
|
|
1347
|
+
|
|
1348
|
+
```python
|
|
1349
|
+
import os
|
|
1350
|
+
from dotenv import load_dotenv
|
|
1351
|
+
from okta.client import Client as OktaClient
|
|
1352
|
+
|
|
1353
|
+
load_dotenv()
|
|
1354
|
+
|
|
1355
|
+
config = {
|
|
1356
|
+
'orgUrl': os.getenv('OKTA_ORG_URL'),
|
|
1357
|
+
'token': os.getenv('OKTA_API_TOKEN')
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
client = OktaClient(config)
|
|
1361
|
+
```
|
|
1362
|
+
|
|
1363
|
+
## OAuth 2.0 Private Key Configuration
|
|
1364
|
+
|
|
1365
|
+
For OAuth 2.0 authentication, use environment variables:
|
|
1366
|
+
|
|
1367
|
+
```bash
|
|
1368
|
+
OKTA_ORG_URL=https://dev-1234567.okta.com
|
|
1369
|
+
OKTA_CLIENT_ID=your_client_id
|
|
1370
|
+
OKTA_PRIVATE_KEY={"kty":"RSA","kid":"key-id","n":"...","e":"AQAB","d":"..."}
|
|
1371
|
+
OKTA_KEY_ID=key-id
|
|
1372
|
+
```
|
|
1373
|
+
|
|
1374
|
+
```python
|
|
1375
|
+
import os
|
|
1376
|
+
from dotenv import load_dotenv
|
|
1377
|
+
from okta.client import Client as OktaClient
|
|
1378
|
+
|
|
1379
|
+
load_dotenv()
|
|
1380
|
+
|
|
1381
|
+
config = {
|
|
1382
|
+
'orgUrl': os.getenv('OKTA_ORG_URL'),
|
|
1383
|
+
'authorizationMode': 'PrivateKey',
|
|
1384
|
+
'clientId': os.getenv('OKTA_CLIENT_ID'),
|
|
1385
|
+
'scopes': ['okta.users.manage', 'okta.groups.manage', 'okta.apps.manage'],
|
|
1386
|
+
'privateKey': os.getenv('OKTA_PRIVATE_KEY'),
|
|
1387
|
+
'kid': os.getenv('OKTA_KEY_ID')
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
client = OktaClient(config)
|
|
1391
|
+
```
|
|
1392
|
+
|
|
1393
|
+
## Useful Links
|
|
1394
|
+
|
|
1395
|
+
- Official Documentation: https://developer.okta.com/
|
|
1396
|
+
- API Reference: https://developer.okta.com/docs/reference/
|
|
1397
|
+
- Users API: https://developer.okta.com/docs/reference/api/users/
|
|
1398
|
+
- Groups API: https://developer.okta.com/docs/reference/api/groups/
|
|
1399
|
+
- Applications API: https://developer.okta.com/docs/reference/api/apps/
|
|
1400
|
+
- GitHub Repository: https://github.com/okta/okta-sdk-python
|
|
1401
|
+
- PyPI Package: https://pypi.org/project/okta/
|