Package: descope
Descope SDK for PythonThe Descope SDK for python provides convenient access to the Descope user management and authentication API
for a backend written in python. You can read more on theDescope Website.RequirementsThe SDK supports Python 3.7 and above.Installing the SDKInstall the package with:pipinstalldescopeIf you would like to use the Flask decorators, make sure to install the Flask extras:pipinstalldescope[Flask]SetupA DescopeProject IDis required to initialize the SDK. Find it on theproject page in the Descope Console.fromdescopeimportDescopeClient# Initialized after setting the DESCOPE_PROJECT_ID env vardescope_client=DescopeClient()# ** Or directly **descope_client=DescopeClient(project_id="<Project ID>")Authentication FunctionsThese sections show how to use the SDK to perform various authentication/authorization functions:OTP AuthenticationMagic LinkEnchanted LinkOAuthSSO (SAML / OIDC)TOTP AuthenticationPasswordsSession ValidationRoles & Permission ValidationTenant selectionLogging OutHistoryAPI Managment FunctionThese sections show how to use the SDK to perform permission and user management functions. You will need to create an instance ofDescopeClientby following theSetupguide, before you can use any of these functions:Manage TenantsManage UsersManage Access KeysManage SSO SettingManage PermissionsManage RolesQuery SSO GroupsManage FlowsManage JWTsImpersonateEmbedded linksSearch AuditManage ReBAC AuthzManage ProjectManage SSO ApplicationsIf you wish to run any of our code samples and play with them, check out ourCode Examplessection.If you're performing end-to-end testing, check out theUtils for your end to end (e2e) tests and integration testssection. You will need to use theDescopeClientobject created underSetupguide.For rate limiting information, please confer to theAPI Rate Limitssection.OTP AuthenticationSend a user a one-time password (OTP) using your preferred delivery method (email / SMS). An email address or phone number must be provided accordingly.The user can eithersign up,sign inorsign up or infromdescopeimportDeliveryMethod# Every user must have a login ID. All other user information is optionalemail="desmond@descope.com"user={"name":"Desmond Copeland","phone":"212-555-1234","email":email}masked_address=descope_client.otp.sign_up(method=DeliveryMethod.EMAIL,login_id=email,user=user)The user will receive a code using the selected delivery method. Verify that code using:jwt_response=descope_client.otp.verify_code(method=DeliveryMethod.EMAIL,login_id=email,code=value)session_token=jwt_response[SESSION_TOKEN_NAME].get("jwt")refresh_token=jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt")The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more onsession validationMagic LinkSend a user a Magic Link using your preferred delivery method (email / SMS).
The Magic Link will redirect the user to page where the its token needs to be verified.
This redirection can be configured in code, or generally in theDescope ConsoleThe user can eithersign up,sign inorsign up or infromdescopeimportDeliveryMethodmasked_address=descope_client.magiclink.sign_up_or_in(method=DeliveryMethod.EMAIL,login_id="desmond@descope.com",uri="http://myapp.com/verify-magic-link",# Set redirect URI here or via console)To verify a magic link, your redirect page must call the validation function on the token (t) parameter (https://your-redirect-address.com/verify?t=<token>):jwt_response=descope_client.magiclink.verify(token=token)session_token=jwt_response[SESSION_TOKEN_NAME].get("jwt")refresh_token=jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt")The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more onsession validationEnchanted LinkUsing the Enchanted Link APIs enables users to sign in by clicking a link
delivered to their email address. The email will include 3 different links,
and the user will have to click the right one, based on the 2-digit number that is
displayed when initiating the authentication process.This method is similar toMagic Linkbut differs in two major ways:The user must choose the correct link out of the three, instead of having just one
single link.This supports cross-device clicking, meaning the user can try to log in on one device,
like a computer, while clicking the link on another device, for instance a mobile phone.The Enchanted Link will redirect the user to page where the its token needs to be verified.
This redirection can be configured in code per request, or set globally in theDescope Console.The user can eithersign up,sign inorsign up or inresp=descope_client.enchantedlink.sign_up_or_in(login_id=email,uri="http://myapp.com/verify-enchanted-link",# Set redirect URI here or via console)link_identifier=resp["linkId"]# Show the user which link they should press in their emailpending_ref=resp["pendingRef"]# Used to poll for a valid sessionmasked_email=resp["maskedEmail"]# The email that the message was sent to in a masked formatAfter sending the link, you must poll to receive a valid session using thepending_reffrom
the previous step. A valid session will be returned only after the user clicks the right link.i=0whilenotdoneandi<max_tries:try:i=i+1sleep(4)jwt_response=descope_client.enchantedlink.get_session(pending_ref)done=TrueexceptAuthExceptionase:# Poll while still receiving 401 Unauthorizedife.status_code!=401:# Other failures means something's wrong, abortlogging.info(f"Failed pending session, err:{e}")done=Trueifjwt_response:session_token=jwt_response[SESSION_TOKEN_NAME].get("jwt")refresh_token=jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt")To verify an enchanted link, your redirect page must call the validation function on the token (t) parameter (https://your-redirect-address.com/verify?t=<token>). Once the token is verified, the session polling will receive a validjwt_response.try:descope_client.enchantedlink.verify(token=token)# Token is validexceptAuthExceptionase:# Token is invalidThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more onsession validationOAuthUsers can authenticate using their social logins, using the OAuth protocol. Configure your OAuth settings on theDescope console. To start a flow call:descope_client.oauth.start(provider="google",# Choose an oauth provider out of the supported providersreturn_url="https://my-app.com/handle-oauth",# Can be configured in the console instead of here)The user will authenticate with the authentication provider, and will be redirected back to the redirect URL, with an appendedcodeHTTP URL parameter. Exchange it to validate the user:jwt_response=descope_client.oauth.exchange_token(code)session_token=jwt_response[SESSION_TOKEN_NAME].get("jwt")refresh_token=jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt")The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more onsession validationSSO (SAML / OIDC)Users can authenticate to a specific tenant using SAML/OIDC based on the tenant settings. Configure your SAML/OIDC tenant settings on theDescope console. To start a flow call:descope_client.sso.start(tenant="my-tenant-ID",# Choose which tenant to log intoreturn_url="https://my-app.com/handle-sso",# Can be configured in the console instead of here)The user will authenticate with the authentication provider configured for that tenant, and will be redirected back to the redirect URL, with an appendedcodeHTTP URL parameter. Exchange it to validate the user:jwt_response=descope_client.sso.exchange_token(code)session_token=jwt_response[SESSION_TOKEN_NAME].get("jwt")refresh_token=jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt")The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more onsession validationNote: the descope_client.saml.start(..) and descope_client.saml.exchange_token(..) functions are DEPRECATED, use the above sso functions insteadTOTP AuthenticationThe user can authenticate using an authenticator app, such as Google Authenticator.
Sign up like you would using any other authentication method. The sign up response
will then contain a QR codeimagethat can be displayed to the user to scan using
their mobile device camera app, or the user can enter thekeymanually or click
on the link provided by theprovisioning_url.Existing users can add TOTP using theupdatefunction.fromdescopeimportDeliveryMethod# Every user must have a login ID. All other user information is optionalemail="desmond@descope.com"user={"name":"Desmond Copeland","phone":"212-555-1234","email":email}totp_response=descope_client.totp.sign_up(method=DeliveryMethod.EMAIL,login_id=email,user=user)# Use one of the provided options to have the user add their credentials to the authenticatorprovisioning_url=totp_response["provisioningURL"]image=totp_response["image"]key=totp_response["key"]There are 3 different ways to allow the user to save their credentials in
their authenticator app - either by clicking the provisioning URL, scanning the QR
image or inserting the key manually. After that, signing in is done using the code
the app produces.jwt_response=descope_client.totp.sign_in_code(login_id=email,code=code,# Code from authenticator app)session_token=jwt_response[SESSION_TOKEN_NAME].get("jwt")refresh_token=jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt")The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more onsession validationPasswordsThe user can also authenticate with a password, though it's recommended to
prefer passwordless authentication methods if possible. Sign up requires the
caller to provide a valid password that meets all the requirements configured
for thepassword authentication methodin the Descope console.# Every user must have a login_id and a password. All other user information is optionallogin_id="desmond@descope.com"password="qYlvi65KaX"user={"name":"Desmond Copeland","email":login_id,}jwt_response=descope_client.password.sign_up(login_id=login_id,password=password,user=user,)session_token=jwt_response[SESSION_TOKEN_NAME].get("jwt")refresh_token=jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt")The user can later sign in using the same login_id and password.jwt_response=descope_client.password.sign_in(login_id,password)session_token=jwt_response[SESSION_TOKEN_NAME].get("jwt")refresh_token=jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt")The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more onsession validationIn case the user needs to update their password, one of two methods are available: Resetting their password or replacing their passwordChanging PasswordsNOTE: send_reset will only work if the user has a validated email address. Otherwise password reset prompts cannot be sent.In thepassword authentication methodin the Descope console, it is possible to define which alternative authentication method can be used in order to authenticate the user, in order to reset and update their password.# Start the reset process by sending a password reset prompt. In this example we'll assume# that magic link is configured as the reset method. The optional redirect URL is used in the# same way as in regular magic link authentication.login_id="desmond@descope.com"redirect_url="https://myapp.com/password-reset"descope_client.password.send_reset(login_id,redirect_url)The magic link, in this case, must then be verified like any other magic link (see themagic link sectionfor more details). However, after verifying the user, it is expected
to allow them to provide a new password instead of the old one. Since the user is now authenticated, this is possible via:# The refresh token is required to make sure the user is authenticated.err=descope_client.password.update(login_id,new_password,token)updatecan always be called when the user is authenticated and has a valid session.Alternatively, it is also possible to replace an existing active password with a new one.# Replaces the user's current password with a new onejwt_response=descope_client.password.replace(login_id,old_password,new_password)session_token=jwt_response[SESSION_TOKEN_NAME].get("jwt")refresh_token=jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt")Session ValidationEvery secure request performed between your client and server needs to be validated. The client sends
the session and refresh tokens with every request, and they are validated using one of the following:# Validate the session. Will raise if expiredtry:jwt_response=descope_client.validate_session(session_token)exceptAuthException:# Session expired# If validate_session raises an exception, you will need to refresh the session usingjwt_response=descope_client.refresh_session(refresh_token)# Alternatively, you could combine the two and# have the session validated and automatically refreshed when expiredjwt_response=descope_client.validate_and_refresh_session(session_token,refresh_token)Choose the right session validation and refresh combination that suits your needs.Note: all those validation apis can receive an optional 'audience' parameter that should be provided when using jwt that has the 'aud' claim.Refreshed sessions return the same response as is returned when users first sign up / log in,
containing the session and refresh tokens, as well as all of the JWT claims.
Make sure to return the tokens from the response to the client, or updated the cookie if you're using it.Usually, the tokens can be passed in and out via HTTP headers or via a cookie.
The implementation can defer according to your framework of choice. See oursamplesfor a few examples.If Roles & Permissions are used, validate them immediately after validating the session. See thenext sectionfor more information.Roles & Permission ValidationWhen using Roles & Permission, it's important to validate the user has the required
authorization immediately after making sure the session is valid. Taking thejwt_responsereceived by thesession validation, call the following functions:For multi-tenant uses:# You can validate specific permissionsvalid_permissions=descope_client.validate_tenant_permissions(jwt_response,"my-tenant-ID",["Permission to validate"])ifnotvalid_permissions:# Deny access# Or validate roles directlyvalid_roles=descope_client.validate_tenant_roles(jwt_response,"my-tenant-ID",["Role to validate"])ifnotvalid_roles:# Deny access# Or get the matched roles/permissionsmatched_tenant_roles=descope_client.get_matched_tenant_roles(jwt_response,"my-tenant-ID",["role-name1","role-name2"])matched_tenant_permissions=descope_client.get_matched_tenant_permissions(jwt_response,"my-tenant-ID",["permission-name1","permission-name2"])When not using tenants use:# You can validate specific permissionsvalid_permissions=descope_client.validate_permissions(jwt_response,["Permission to validate"])ifnotvalid_permissions:# Deny access# Or validate roles directlyvalid_roles=descope_client.validate_roles(jwt_response,["Role to validate"])ifnotvalid_roles:# Deny access# Or get the matched roles/permissionsmatched_roles=descope_client.get_matched_roles(jwt_response,["role-name1","role-name2"])matched_permissions=descope_client.get_matched_permissions(jwt_response,["permission-name1","permission-name2"])Tenant selectionFor a user that has permissions to multiple tenants, you can set a specific tenant as the current selected one
This will add an extra attribute to the refresh JWT and the session JWT with the selected tenant IDtenant_id_="t1"jwt_response=descope_client.select_tenant(tenant_id,refresh_token)Logging OutYou can log out a user from an active session by providing theirrefresh_tokenfor that session.
After calling this function, you must invalidate or remove any cookies you have created.descope_client.logout(refresh_token)It is also possible to sign the user out of all the devices they are currently signed-in with. Callinglogout_allwill
invalidate all user's refresh tokens. After calling this function, you must invalidate or remove any cookies you have created.descope_client.logout_all(refresh_token)HistoryYou can get the current session user history.
The request requires a valid refresh token.users_history_resp=descope_client.history(refresh_token)foruser_historyinusers_history_resp:# Do somethingManagement APIIt is very common for some form of management or automation to be required. These can be performed
using the management API. Please note that these actions are more sensitive as they are administrative
in nature. Please use responsibly.SetupTo use the management API you'll need aManagement Keyalong with yourProject ID.
Create one in theDescope Console.fromdescopeimportDescopeClient# Initialized after setting the DESCOPE_PROJECT_ID and the DESCOPE_MANAGEMENT_KEY env varsdescope_client=DescopeClient()# ** Or directly **descope_client=DescopeClient(project_id="<Project ID>",management_key="<Management Key>")Manage TenantsYou can create, update, delete or load tenants:# You can optionally set your own ID when creating a tenantdescope_client.mgmt.tenant.create(name="My First Tenant",id="my-custom-id",# This is optional.self_provisioning_domains=["domain.com"],custom_attributes={"attribute-name":"value},)# Update will override all fields as is. Use carefully.descope_client.mgmt.tenant.update(id="my-custom-id",name="My First Tenant",self_provisioning_domains=["domain.com","another-domain.com"],custom_attributes={"attribute-name":"value},)# Tenant deletion cannot be undone. Use carefully.descope_client.mgmt.tenant.delete("my-custom-id")# Load tenant by idtenant_resp=descope_client.mgmt.tenant.load("my-custom-id")# Load all tenantstenants_resp=descope_client.mgmt.tenant.load_all()tenants=tenants_resp["tenants"]fortenantintenants:# Do something# search all tenantstenants_resp=descope_client.mgmt.tenant.search_all(ids=["id1"],names=["name1"],custom_attributes={"k1":"v1"},self_provisioning_domains=["spd1"])tenants=tenants_resp["tenants"]fortenantintenants:# Do somethingManage UsersYou can create, update, delete or load users, as well as setting new password, expire password and search according to filters:# A user must have a login ID, other fields are optional.# Roles should be set directly if no tenants exist, otherwise set# on a per-tenant basis.descope_client.mgmt.user.create(login_id="desmond@descope.com",email="desmond@descope.com",display_name="Desmond Copeland",user_tenants=[AssociatedTenant("my-tenant-id",["role-name1"]),],sso_app_ids=["appId1"],)# Alternatively, a user can be created and invited via an email message.# Make sure to configure the invite URL in the Descope console prior to using this function,# and that an email address is provided in the information.descope_client.mgmt.user.invite(login_id="desmond@descope.com",email="desmond@descope.com",display_name="Desmond Copeland",user_tenants=[AssociatedTenant("my-tenant-id",["role-name1"]),],sso_app_ids=["appId1"],)# Batch invitedescope_client.mgmt.user.invite_batch(users=[UserObj(login_id="desmond@descope.com",email="desmond@descope.com",display_name="Desmond Copeland",user_tenants=[AssociatedTenant("my-tenant-id",["role-name1"]),],custom_attributes={"ak":"av"},sso_app_ids=["appId1"],)],invite_url="invite.me",send_mail=True,send_sms=True,)# Update will override all fields as is. Use carefully.descope_client.mgmt.user.update(login_id="desmond@descope.com",email="desmond@descope.com",display_name="Desmond Copeland",user_tenants=[AssociatedTenant("my-tenant-id",["role-name1","role-name2"]),],sso_app_ids=["appId1"],)# Update explicit data for a user rather than overriding all fieldsdescope_client.mgmt.user.update_login_id(login_id="desmond@descope.com",new_login_id="bane@descope.com")descope_client.mgmt.user.update_phone(login_id="desmond@descope.com",phone="+18005551234",verified=True,)descope_client.mgmt.user.remove_tenant_roles(login_id="desmond@descope.com",tenant_id="my-tenant-id",role_names=["role-name1"],)# Set SSO applications association to a user.user=descope_client.mgmt.user.set_sso_apps(login_id="desmond@descope.com",sso_app_ids=["appId1","appId2"])# Add SSO applications association to a user.user=descope_client.mgmt.user.add_sso_apps(login_id="desmond@descope.com",sso_app_ids=["appId1","appId2"])# Remove SSO applications association from a user.user=descope_client.mgmt.user.remove_sso_apps(login_id="desmond@descope.com",sso_app_ids=["appId1","appId2"])# User deletion cannot be undone. Use carefully.descope_client.mgmt.user.delete("desmond@descope.com")# Load specific useruser_resp=descope_client.mgmt.user.load("desmond@descope.com")user=user_resp["user"]# If needed, users can be loaded using the user ID as welluser_resp=descope_client.mgmt.user.load_by_user_id("<user-id>")user=user_resp["user"]# Logout user from all devices by login IDdescope_client.mgmt.user.logout_user("<login-id>")# Logout user from all devices by user IDdescope_client.mgmt.user.logout_user_by_user_id("<user-id>")# Search all users, optionally according to tenant and/or role filter# results can be paginated using the limit and page parametersusers_resp=descope_client.mgmt.user.search_all(tenant_ids=["my-tenant-id"])users=users_resp["users"]foruserinusers:# Do something# Get users' authentication historyusers_history_resp=descope_client.mgmt.user.history(["user-id-1","user-id-2"])foruser_historyinusers_history_resp:# Do somethingSet or Expire User PasswordYou can set a new active password for a user that they can sign in with.
You can also set a temporary password that the user will be forced to change on the next login.
For a user that already has an active password, you can expire their current password, effectively requiring them to change it on the next login.# Set a user's temporary passworddescope_client.mgmt.user.set_temporary_password('<login-id>','<some-password>');# Set a user's passworddescope_client.mgmt.user.set_active_password('<login-id>','<some-password>');# Or alternatively, expire a user passworddescope_client.mgmt.user.expirePassword('<login-id>');Manage Access KeysYou can create, update, delete or load access keys, as well as search according to filters:# An access key must have a name and expiration, other fields are optional.# Roles should be set directly if no tenants exist, otherwise set# on a per-tenant basis.# If user_id is supplied, then authorization would be ignored, and access key would be bound to the users authorizationcreate_resp=descope_client.mgmt.access_key.create(name="name",expire_time=1677844931,key_tenants=[AssociatedTenant("my-tenant-id",["role-name1"]),],)key=create_resp["key"]cleartext=create_resp["cleartext"]# make sure to save the returned cleartext securely. It will not be returned again.# Load a specific access keyaccess_key_resp=descope_client.mgmt.access_key.load("key-id")access_key=access_key_resp["key"]# Search all access keys, optionally according to a tenant filterkeys_resp=descope_client.mgmt.access_key.search_all_access_keys(tenant_ids=["my-tenant-id"])keys=keys_resp["keys"]forkeyinkeys:# Do something# Update will override all fields as is. Use carefully.descope_client.mgmt.access_key.update(id="key-id",name="new name",)# Access keys can be deactivated to prevent usage. This can be undone using "activate".descope_client.mgmt.access_key.deactivate("key-id")# Disabled access keys can be activated once again.descope_client.mgmt.access_key.activate("key-id")# Access key deletion cannot be undone. Use carefully.descope_client.mgmt.access_key.delete("key-id")Manage SSO SettingYou can manage SSO settings and map SSO group roles and user attributes.# You can load all tenant SSO settingssso_settings_res=descope_client.mgmt.sso.load_settings("tenant-id")# import based on your configuration needs:fromdescopeimport(SSOOIDCSettings,OIDCAttributeMapping,SSOSAMLSettings,AttributeMapping,RoleMapping,SSOSAMLSettingsByMetadata)# You can Configure SSO SAML settings for a tenant manually.settings=SSOSAMLSettings(idp_url="https://dummy.com/saml",idp_entity_id="entity1234",idp_cert="my certificate",attribute_mapping=AttributeMapping(name="name",given_name="givenName",middle_name="middleName",family_name="familyName",picture="picture",email="email",phone_number="phoneNumber",group="groups"),role_mappings=[RoleMapping(groups=["grp1"],role="rl1")],)descope_client.mgmt.sso.configure_saml_settings(tenant_id,# Which tenant this configuration is forsettings,# The SAML settingsredirect_url="https://your.domain.com",# Global redirection after successful authenticationdomains=["tenant-users.com"]# Users authentication with these domains will be logged in to this tenant)# You can Configure SSO SAML settings for a tenant by fetching them from an IDP metadata URL.settings=SSOSAMLSettingsByMetadata(idp_metadata_url="https://dummy.com/metadata",attribute_mapping=AttributeMapping(name="myName",given_name="givenName",middle_name="middleName",family_name="familyName",picture="picture",email="email",phone_number="phoneNumber",group="groups"),role_mappings=[RoleMapping(groups=["grp1"],role="rl1")],)descope_client.mgmt.sso.configure_saml_settings_by_metadata(tenant_id,# Which tenant this configuration is forsettings,# The SAML settingsredirect_url="https://your.domain.com",# Global redirection after successful authenticationdomains=["tenant-users.com"]# Users authentication with these domains will be logged in to this tenant)# You can Configure SSO OIDC settings for a tenant manually.settings=SSOOIDCSettings(name="myProvider",client_id="myId",client_secret="secret",redirect_url="https://your.domain.com",auth_url="https://dummy.com/auth",token_url="https://dummy.com/token",user_data_url="https://dummy.com/userInfo",scope=["openid","profile","email"],attribute_mapping=OIDCAttributeMapping(login_id="subject",name="name",given_name="givenName",middle_name="middleName",family_name="familyName",email="email",verified_email="verifiedEmail",username="username",phone_number="phoneNumber",verified_phone="verifiedPhone",picture="picture"))descope_client.mgmt.sso.configure_oidc_settings(tenant_id,# Which tenant this configuration is forsettings,# The OIDC provider settingsdomains=["tenant-users.com"]# Users authentication with these domains will be logged in to this tenant)# DEPRECATED (use load_settings(..) function instead)# You can get SSO settings for a tenantsso_settings_res=descope_client.mgmt.sso.get_settings("tenant-id")# DEPRECATED (use configure_saml_settings(..) function instead)# You can configure SSO settings manually by setting the required fields directlydescope_client.mgmt.sso.configure(tenant_id,# Which tenant this configuration is foridp_url="https://idp.com",entity_id="my-idp-entity-id",idp_cert="<your-cert-here>",redirect_url="https://your.domain.com",# Global redirection after successful authenticationdomains=["tenant-users.com"]# Users authentication with these domains will be logged in to this tenant)# DEPRECATED (use configure_saml_settings_by_metadata(..) function instead)# Alternatively, configure using an SSO metadata URLdescope_client.mgmt.sso.configure_via_metadata(tenant_id,# Which tenant this configuration is foridp_metadata_url="https://idp.com/my-idp-metadata",redirect_url="",# Redirect URL will have to be provided in every authentication calldomains=None# Remove the current domains configuration if a value was previously set)# DEPRECATED (use configure_saml_settings() or configure_saml_settings_by_metadata(..) functions instead)# Map IDP groups to Descope roles, or map user attributes.# This function overrides any previous mapping (even when empty). Use carefully.descope_client.mgmt.sso.mapping(tenant_id,# Which tenant this mapping is forrole_mappings=[RoleMapping(["IDP_ADMIN"],"Tenant Admin")],attribute_mapping=AttributeMapping(name="IDP_NAME",phone_number="IDP_PHONE"),)Note: Certificates should have a similar structure to:-----BEGIN CERTIFICATE-----
Certifcate contents
-----END CERTIFICATE-----Manage PermissionsYou can create, update, delete or load permissions:# You can optionally set a description for a permission.descope_client.mgmt.permission.create(name="My Permission",description="Optional description to briefly explain what this permission allows.")# Update will override all fields as is. Use carefully.descope_client.mgmt.permission.update(name="My Permission",new_name="My Updated Permission",description="A revised description")# Permission deletion cannot be undone. Use carefully.descope_client.mgmt.permission.delete("My Updated Permission")# Load all permissionspermissions_resp=descope_client.mgmt.permission.load_all()permissions=permissions_resp["permissions"]forpermissioninpermissions:# Do somethingManage RolesYou can create, update, delete or load roles:# You can optionally set a description and associated permission for a roles.descope_client.mgmt.role.create(name="My Role",description="Optional description to briefly explain what this role allows.",permission_names=["My Updated Permission"],tenant_id="Optionally scope this role for this specific tenant. If left empty, the role will be available to all tenants.")# Update will override all fields as is. Use carefully.descope_client.mgmt.role.update(name="My Role",new_name="My Updated Role",description="A revised description",permission_names=["My Updated Permission","Another Permission"]tenant_id="The tenant ID to which this role is associated, leave empty, if role is a global one")# Role deletion cannot be undone. Use carefully.descope_client.mgmt.role.delete("My Updated Role","<tenant_id>")# Load all rolesroles_resp=descope_client.mgmt.role.load_all()roles=roles_resp["roles"]forroleinroles:# Do somethingManage Flows and ThemeYou can list your flows and also import and export flows and screens, or the project theme:# List all project flowsflows_resp=descope_client.mgmt.flow.list_flows()print(f'Total number of flows:{flows_resp["total"]}')flows=flows_resp["flows"]forflowinflows:# Do something# Delete flows by idsdescope_client.mgmt.flow.delete_flows(flow_ids=["flow-1","flow-2"],)# Export a selected flow by id for the flow and matching screens.exported_flow_and_screens=descope_client.mgmt.flow.export_flow(flow_id="sign-up-or-in",)# Import a given flow and screens to the flow matching the id provided.imported_flow_and_screens=descope_client.mgmt.flow.import_flow(flow_id="sign-up-or-in",flow={},screens=[])# Export your project theme.exported_theme=descope_client.mgmt.flow.export_theme()# Import a theme to your project.imported_theme=descope_client.mgmt.flow.import_flow(theme={})Query SSO GroupsYou can query SSO groups:# Load all groups for a given tenant idgroups_resp=descope_client.mgmt.group.load_all_groups(tenant_id="tenant-id",)# Load all groups for the given user IDs (can be found in the user's JWT)groups_resp=descope_client.mgmt.group.load_all_groups_for_members(tenant_id="tenant-id",user_ids=["user-id-1","user-id-2"],)# Load all groups for the given user's login IDs (used for sign-in)groups_resp=descope_client.mgmt.group.load_all_groups_for_members(tenant_id="tenant-id",login_ids=["login-id-1","login-id-2"],)# Load all group's members by the given group idgroups_resp=descope_client.mgmt.group.load_all_group_members(tenant_id="tenant-id",group_id="group-id,)forgroupingroups_resp:# Do somethingManage JWTsYou can add custom claims to a valid JWT.updated_jwt=descope_client.mgmt.jwt.update_jwt(jwt="original-jwt",custom_claims={"custom-key1":"custom-value1","custom-key2":"custom-value2"},)ImpersonateYou can impersonate to another user
The impersonator user must have theimpersonationpermission in order for this request to work.
The response would be a refresh JWT of the impersonated userrefresh_jwt=descope_client.mgmt.jwt.impersonate(impersonator_id="<Login ID impersonator>",login_id="<Login ID of impersonated person>",validate_consent=True)Note 1: The generate code/link functions, work only for test users, will not work for regular users.Note 2: In case of testing sign-in / sign-up operations with test users, need to make sure to generate the code prior calling the sign-in / sign-up operations.Embedded linksEmbedded links can be created to directly receive a verifiable token without sending it.This token can then be verified using the magic link 'verify' function, either directly or through a flow.token=descope_client.mgmt.user.generate_embedded_link("desmond@descope.com",{"key1":"value1"})Search AuditYou can perform an audit search for either specific values or full-text across the fields. Audit search is limited to the last 30 days.
Below are some examples. For a full list of available search criteria options, see the function documentation.# Full text search on last 10 daysaudits=descope_client.mgmt.audit.search(text="some-text",from_ts=datetime.now(timezone.utc)-timedelta(days=10))# Search successful logins in the last 30 daysaudits=descope_client.mgmt.audit.search(actions=["LoginSucceed"])Manage ReBAC AuthzDescope supports full relation based access control (ReBAC) using a zanzibar like schema and operations.
A schema is comprized of namespaces (entities like documents, folders, orgs, etc.) and each namespace has relation definitions to define relations.
Each relation definition can be simple (either you have it or not) or complex (union of nodes).A simple example for a file system like schema would be:# Example schema for the authz testsname:Filesnamespaces:-name:orgrelationDefinitions:-name:parent-name:membercomplexDefinition:nType:unionchildren:-nType:childexpression:neType:self-nType:childexpression:neType:relationLeftrelationDefinition:parentrelationDefinitionNamespace:orgtargetRelationDefinition:membertargetRelationDefinitionNamespace:org-name:folderrelationDefinitions:-name:parent-name:ownercomplexDefinition:nType:unionchildren:-nType:childexpression:neType:self-nType:childexpression:neType:relationRightrelationDefinition:parentrelationDefinitionNamespace:foldertargetRelationDefinition:ownertargetRelationDefinitionNamespace:folder-name:editorcomplexDefinition:nType:unionchildren:-nType:childexpression:neType:self-nType:childexpression:neType:relationRightrelationDefinition:parentrelationDefinitionNamespace:foldertargetRelationDefinition:editortargetRelationDefinitionNamespace:folder-nType:childexpression:neType:targetSettargetRelationDefinition:ownertargetRelationDefinitionNamespace:folder-name:viewercomplexDefinition:nType:unionchildren:-nType:childexpression:neType:self-nType:childexpression:neType:relationRightrelationDefinition:parentrelationDefinitionNamespace:foldertargetRelationDefinition:viewertargetRelationDefinitionNamespace:folder-nType:childexpression:neType:targetSettargetRelationDefinition:editortargetRelationDefinitionNamespace:folder-name:docrelationDefinitions:-name:parent-name:ownercomplexDefinition:nType:unionchildren:-nType:childexpression:neType:self-nType:childexpression:neType:relationRightrelationDefinition:parentrelationDefinitionNamespace:doctargetRelationDefinition:ownertargetRelationDefinitionNamespace:folder-name:editorcomplexDefinition:nType:unionchildren:-nType:childexpression:neType:self-nType:childexpression:neType:relationRightrelationDefinition:parentrelationDefinitionNamespace:doctargetRelationDefinition:editortargetRelationDefinitionNamespace:folder-nType:childexpression:neType:targetSettargetRelationDefinition:ownertargetRelationDefinitionNamespace:doc-name:viewercomplexDefinition:nType:unionchildren:-nType:childexpression:neType:self-nType:childexpression:neType:relationRightrelationDefinition:parentrelationDefinitionNamespace:doctargetRelationDefinition:viewertargetRelationDefinitionNamespace:folder-nType:childexpression:neType:targetSettargetRelationDefinition:editortargetRelationDefinitionNamespace:docDescope 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.# Load the existing schemaschema=descope_client.mgmt.authz.load_schema()# Save schema and make sure to remove all namespaces not listeddescope_client.mgmt.authz.save_schema(schema,True)# Create a relation between a resource and userdescope_client.mgmt.authz.create_relations([{"resource":"some-doc","relationDefinition":"owner","namespace":"doc","target":"u1",}])# Check if target has the relevant relation# The answer should be true because an owner is also a viewerrelations=descope_client.mgmt.authz.has_relations([{"resource":"some-doc","relationDefinition":"viewer","namespace":"doc","target":"u1",}])# Get list of targets and resources changed since the given date.res=descope_client.mgmt.authz.get_modified()Manage ProjectYou can change the project name, as well as clone the current project to
create a new one.# Change the project namedescope_client.mgmt.project.change_name("new-project-name")# Clone the current project, including its settings and configurations.# Note that this action is supported only with a pro license or above.# Users, tenants and access keys are not cloned.clone_resp=descope_client.mgmt.project.clone("new-project-name")You can manage your project's settings and configurations by exporting your
project's environment. You can also import previously exported data into
the same project or a different one.# Exports the current state of the projectexport=descope_client.mgmt.project.export_project()# Import the previously exported data into the current projectdescope_client.mgmt.project.import_project(export)Manage SSO ApplicationsYou can create, update, delete or load sso applications:# Create OIDC SSO applicationdescope_client.mgmt.sso_application.create_oidc_application(name="My First sso app",login_page_url="http://dummy.com",id="my-custom-id",# This is optional.)# Create SAML SSO applicationdescope_client.mgmt.sso_application.create_saml_application(name="My First sso app",login_page_url="http://dummy.com",id="my-custom-id",# This is optional.use_metadata_info=True,metadata_url="http://dummy.com/metadata)# Update OIDC SSO application# Update will override all fields as is. Use carefully.descope_client.mgmt.sso_application.update_oidc_application(id="my-custom-id",name="My First sso app",login_page_url="http://dummy.com",)# Update SAML SSO application# Update will override all fields as is. Use carefully.descope_client.mgmt.sso_application.update_saml_application(id="my-custom-id",name="My First sso app",login_page_url="http://dummy.com",use_metadata_info=False,entity_id="ent1234",acs_url="http://dummy.com/acs,certificate="my cert")# SSO application deletion cannot be undone. Use carefully.descope_client.mgmt.sso_application.delete("my-custom-id")# Load SSO application by idapp_resp=descope_client.mgmt.sso_application.load("my-custom-id")# Load all SSO applicationsapps_resp=descope_client.mgmt.sso_application.load_all()apps=apps_resp["apps"]forappinapps:# Do something### Utils for your end to end (e2e) tests and integration testsToeaseyoure2etests,weexposeddedicatedmanagementmethods,thatway,youdon't need to use 3rd party messaging services in order to receive sign-in/up Emails or SMS, and avoid the need of parsing the code and token from them.```Python# User for test can be created, this user will be able to generate code/link without# the need of 3rd party messaging services.# Test user must have a loginId, other fields are optional.# Roles should be set directly if no tenants exist, otherwise set# on a per-tenant basis.descope_client.mgmt.user.create_test_user(login_id="desmond@descope.com",email="desmond@descope.com",display_name="Desmond Copeland",user_tenants=[AssociatedTenant("my-tenant-id",["role-name1"]),],)# Now test user got created, and this user will be available until you delete it,# you can use any management operation for test user CRUD.# You can also delete all test users.descope_client.mgmt.user.delete_all_test_users()# OTP code can be generated for test user, for example:resp=descope_client.mgmt.user.generate_otp_for_test_user(DeliveryMethod.EMAIL,"login-id")code=resp["code"]# Now you can verify the code is valid (using descope_client.*.verify for example)# login_options can be provided to set custom claims to the generated jwt.# Same as OTP, magic link can be generated for test user, for example:resp=descope_client.mgmt.user.generate_magic_link_for_test_user(DeliveryMethod.EMAIL,"login-id","")link=resp["link"]# Enchanted link can be generated for test user, for example:resp=descope_client.mgmt.user.generate_enchanted_link_for_test_user("login-id","")link=resp["link"]pending_ref=resp["pendingRef"]API Rate LimitsHandle API rate limits by comparing the exception to the APIRateLimitExceeded exception, which includes the RateLimitParameters map with the key "Retry-After." This key indicates how many seconds until the next valid API call can take place.try:descope_client.magiclink.sign_up_or_in(method=DeliveryMethod.EMAIL,login_id="desmond@descope.com",uri="http://myapp.com/verify-magic-link",)exceptRateLimitExceptionase:retry_after_seconds=e.rate_limit_parameters.get(API_RATE_LIMIT_RETRY_AFTER_HEADER)# This variable indicates how many seconds until the next valid API call can take place.Code SamplesYou can find various usage samples in thesamples folder.Run LocallyPrerequisitesPython 3.7 or higherPoetryinstalledInstall dependenciespoetryinstallRun testsRunning all tests:poetryrunpytesttestsRunning all tests with coverage:poetryrunpytest--junitxml=/tmp/pytest.xml--cov-report=term-missing:skip-covered--cov=descopetests/--cov-report=xml:/tmp/cov.xmlLearn MoreTo learn more please see theDescope Documentation and API reference page.Contact UsIf you need help you can emailDescope SupportLicenseThe Descope SDK for Python is licensed for use under the terms and conditions of theMIT license Agreement.