Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 125 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ These sections show how to use the SDK to perform permission and user management
13. [Manage FGA (Fine-grained Authorization)](#manage-fga-fine-grained-authorization)
14. [Manage Project](#manage-project)
15. [Manage SSO Applications](#manage-sso-applications)
16. [Manage Outbound Applications](#manage-outbound-applications)

If you wish to run any of our code samples and play with them, check out our [Code Examples](#code-examples) section.

Expand Down Expand Up @@ -1178,7 +1179,7 @@ type doc
permission can_create: owner | parent.owner
permission can_edit: editor | can_create
permission can_view: viewer | can_edit
```
```

Descope SDK allows you to fully manage the schema and relations as well as perform simple (and not so simple) checks regarding the existence of relations.

Expand Down Expand Up @@ -1301,6 +1302,129 @@ apps = apps_resp["apps"]
# Do something
```

### Manage Outbound Applications

You can create, update, delete, load outbound applications and fetch tokens for them:

```python
# Create a basic outbound application
response = descope_client.mgmt.outbound_application.create_application(
name="my new app",
description="my desc",
client_secret="secret123", # Optional
id="my-custom-id", # Optional
)
app_id = response["app"]["id"]

# Create a full OAuth outbound application with all parameters
from descope.management.common import URLParam, AccessType, PromptType

# Create URL parameters for authorization
auth_params = [
URLParam("response_type", "code"),
URLParam("client_id", "my-client-id"),
URLParam("redirect_uri", "https://myapp.com/callback")
]

# Create URL parameters for token endpoint
token_params = [
URLParam("grant_type", "authorization_code"),
URLParam("client_id", "my-client-id")
]

# Create prompt types
prompts = [PromptType.LOGIN, PromptType.CONSENT]

full_app = descope_client.mgmt.outbound_application.create_application(
name="My OAuth App",
description="A full OAuth outbound application",
logo="https://example.com/logo.png",
id="my-custom-id", # Optional custom ID
client_secret="my-secret-key",
client_id="my-client-id",
discovery_url="https://accounts.google.com/.well-known/openid_configuration",
authorization_url="https://accounts.google.com/o/oauth2/v2/auth",
authorization_url_params=auth_params,
token_url="https://oauth2.googleapis.com/token",
token_url_params=token_params,
revocation_url="https://oauth2.googleapis.com/revoke",
default_scopes=["https://www.googleapis.com/auth/userinfo.profile"],
default_redirect_url="https://myapp.com/callback",
callback_domain="myapp.com",
pkce=True, # Enable PKCE
access_type=AccessType.OFFLINE, # Request refresh tokens
prompt=prompts
)

# Update an outbound application with all parameters
# Update will override all fields as is. Use carefully.
descope_client.mgmt.outbound_application.update_application(
id="my-app-id",
name="my updated app",
description="updated description",
logo="https://example.com/logo.png",
client_secret="new-secret", # Optional
client_id="new-client-id",
discovery_url="https://accounts.google.com/.well-known/openid_configuration",
authorization_url="https://accounts.google.com/o/oauth2/v2/auth",
authorization_url_params=auth_params,
token_url="https://oauth2.googleapis.com/token",
token_url_params=token_params,
revocation_url="https://oauth2.googleapis.com/revoke",
default_scopes=["https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"],
default_redirect_url="https://myapp.com/updated-callback",
callback_domain="myapp.com",
pkce=True,
access_type=AccessType.OFFLINE,
prompt=[PromptType.LOGIN, PromptType.CONSENT, PromptType.SELECT_ACCOUNT]
)

# Delete an outbound application by id
# Outbound application deletion cannot be undone. Use carefully.
descope_client.mgmt.outbound_application.delete_application("my-app-id")

# Load an outbound application by id
app = descope_client.mgmt.outbound_application.load_application("my-app-id")

# Load all outbound applications
apps_resp = descope_client.mgmt.outbound_application.load_all_applications()
apps = apps_resp["apps"]
for app in apps:
# Do something with each app

# Fetch user token with specific scopes
user_token = descope_client.mgmt.outbound_application.fetch_token_by_scopes(
"my-app-id",
"user-id",
["read", "write"],
{"refreshToken": True}, # Optional
"tenant-id" # Optional
)

# Fetch latest user token
latest_user_token = descope_client.mgmt.outbound_application.fetch_token(
"my-app-id",
"user-id",
"tenant-id", # Optional
{"forceRefresh": True} # Optional
)

# Fetch tenant token with specific scopes
tenant_token = descope_client.mgmt.outbound_application.fetch_tenant_token_by_scopes(
"my-app-id",
"tenant-id",
["read", "write"],
{"refreshToken": True} # Optional
)

# Fetch latest tenant token
latest_tenant_token = descope_client.mgmt.outbound_application.fetch_tenant_token(
"my-app-id",
"tenant-id",
{"forceRefresh": True} # Optional
)
```

### Utils for your end to end (e2e) tests and integration tests

To ease your e2e tests, we exposed dedicated management methods,
Expand Down
48 changes: 46 additions & 2 deletions descope/management/common.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,32 @@
from typing import List, Optional
from typing import List, Optional, Dict, Any
from enum import Enum


class AccessType(Enum):
OFFLINE = "offline"
ONLINE = "online"


class PromptType(Enum):
NONE = "none"
LOGIN = "login"
CONSENT = "consent"
SELECT_ACCOUNT = "select_account"


class URLParam:
def __init__(self, name: str, value: str):
self.name = name
self.value = value

def to_dict(self) -> dict:
return {"name": self.name, "value": self.value}


def url_params_to_dict(url_params: Optional[List[URLParam]] = None) -> list:
if url_params is None:
return []
return [param.to_dict() for param in url_params]


class MgmtV1:
Expand All @@ -19,6 +47,21 @@ class MgmtV1:
sso_application_load_path = "/v1/mgmt/sso/idp/app/load"
sso_application_load_all_path = "/v1/mgmt/sso/idp/apps/load"

# outbound application
outbound_application_create_path = "/v1/mgmt/outbound/app/create"
outbound_application_update_path = "/v1/mgmt/outbound/app/update"
outbound_application_delete_path = "/v1/mgmt/outbound/app/delete"
outbound_application_load_path = "/v1/mgmt/outbound/app"
outbound_application_load_all_path = "/v1/mgmt/outbound/apps"
outbound_application_fetch_token_by_scopes_path = "/v1/mgmt/outbound/app/user/token"
outbound_application_fetch_token_path = "/v1/mgmt/outbound/app/user/token/latest"
outbound_application_fetch_tenant_token_by_scopes_path = (
"/v1/mgmt/outbound/app/tenant/token"
)
outbound_application_fetch_tenant_token_path = (
"/v1/mgmt/outbound/app/tenant/token/latest"
)

# user
user_create_path = "/v1/mgmt/user/create"
test_user_create_path = "/v1/mgmt/user/create/test"
Expand Down Expand Up @@ -365,7 +408,8 @@ def sort_to_dict(sort: List[Sort]) -> list:
)
return sort_list


def map_to_values_object(input_map: dict):
if not input_map:
return {}
return {k: {"values": v} for k, v in input_map.items()}
return {k: {"values": v} for k, v in input_map.items()}
Loading
Loading