The Kinde Python SDK allows developers to quickly and securely integrate a new or an existing Python application into the Kinde platform.

Before you begin

Link to this section
  • Kinde Python SDK supports Python 3.8+
  • If you haven’t already got a Kinde account, register for free here (no credit card required). Registering gives you a Kinde domain, which you need to get started, e.g. yourapp.kinde.com.

For new projects, you can also find our Starter Kit on GitHub.

Install PIP and then execute the following command:

pip install kinde-python-sdk

Set callback URLs

Link to this section
  1. In Kinde, go to Settings > Applications > [Your app] > View details.
  2. Add your callback URLs in the relevant fields. For example:
    • Allowed callback URLs (also known as redirect URIs) - for example, http://localhost:8000/callback
    • Allowed logout redirect URLs - for example, http://localhost:8000
  3. Select Save.

Add environments

Link to this section

Kinde comes with a production environment, but you can set up other environments if you want to. Note that each environment needs to be set up independently, so you need to use the Environment subdomain in the code block above for those new environments.

Configure your app

Link to this section

Environment variables

The following variables need to be replaced in the code snippets below.

  • KINDE_HOST - your Kinde domain, e.g. https://yourdomain.kinde.com
  • KINDE_CLIENT_ID - In Kinde, go to Settings > Applications > [your application] > View details.
  • KINDE_CLIENT_SECRET - In Kinde, go to Settings > Applications > [your application] > View details.
  • KINDE_REDIRECT_URL - your callback urls or redirect URIs, e.g. http://localhost:8000/callback
  • KINDE_POST_LOGOUT_REDIRECT_URL - where you want users to be redirected to after signing out, e.g. http://localhost:8000

Integrate with your app

Link to this section

Create a new instance of the Kinde Auth client object before you initialize your app.

...
from kinde_sdk import Configuration
from kinde_sdk.kinde_api_client import GrantType, KindeApiClient
...

configuration = Configuration(host=KINDE_HOST)
kinde_api_client_params = {
    "configuration": configuration,
    "domain": KINDE_HOST,
    "client_id": KINDE_CLIENT_ID,
    "client_secret": KINDE_CLIENT_SECRET,
    "grant_type": GRANT_TYPE, # client_credentials | authorization_code | authorization_code_with_pkce
    "callback_url": KINDE_REDIRECT_URL
}
kinde_client = KindeApiClient(**kinde_api_client_params)

With PKCE flow, the code_verifier is required.

from authlib.common.security import generate_token
CODE_VERIFIER = generate_token(48)
kinde_api_client_params["code_verifier"] = CODE_VERIFIER

Sign in and sign up

Link to this section

The Kinde client provides methods for easy sign in and sign up. You can add buttons in your HTML as follows:

<div class="navigation">
    <a href="{{ url_for('login') }}" type="button">Sign in</a>
    <a href="{{ url_for('register') }}" type="button">Sign up</a>
</div>

You will also need to route /login and /register to the SDK methods:

@app.route("/login")
def login():
    return app.redirect(kinde_client.get_login_url())


@app.route("/register")
def register():
    return app.redirect(kinde_client.get_register_url())

Manage redirects

Link to this section

When the user is redirected back to your site from Kinde, this will call your callback URL defined in the KINDE_REDIRECT_URL variable. You will need to route /callback to call a function to handle this.

@app.route("/callback")
def callback():
    kinde_client.fetch_token(authorization_response=request.url)
    print(configuration.access_token) # Token here

You can also get the current authentication status with is_authenticated.

if kinde_client.is_authenticated():
		# Core here

Note: The kinde_client object that is created stores the access_token. This means you need to create a kinde_client object for each unique user that is signing in to your application, so that you can keep track of whether the user is authenticated or not.

The SDK comes with a logout method that returns a logout URL.

kinde_client.logout(redirect_to=KINDE_POST_LOGOUT_REDIRECT_URL)

# implementation
@app.route("/logout")
def logout():
    return app.redirect(
        kinde_client.logout(redirect_to=KINDE_POST_LOGOUT_REDIRECT_URL)
    )

Get user information

Link to this section

To access the user information, use the get_user_details helper function:

kinde_client.get_user_details();
# returns
{
	"given_name":"Dave",
	"id":"abcdef",
	"family_name":"Smith",
	"email":"dave@smith.com",
	"picture": "https://link_to_avatar_url.kinde.com"
}

View users in Kinde

Link to this section

Go to the Users page in Kinde to see who has registered.

User permissions

Link to this section

After a user signs in and they are verified, the token return includes permissions for that user. User permissions are set in Kinde, but you must also configure your application to unlock these functions.

permissions = [
    "create:todos",
    "update:todos",
    "read:todos",
    "delete:todos",
    "create:tasks",
    "update:tasks",
    "read:tasks",
    "delete:tasks",
]

We provide helper functions to more easily access permissions:

kinde_client.get_permission("create:todos")
# {"org_code": "org_b235c067b7e4", is_granted: True}

kinde_client.get_permissions()
# {"org_code": "org_b235c067b7e4", permissions: [ "create:users", "view:users" ]}

A practical example in code might look something like:

if kinde_client.get_permission("create:todos").get("is_granted")):
# create new a todo

We have provided a helper to grab any feature flag from access_token:

kinde_client.get_flag("theme");
# returns
{
    "code": "theme",
    "type": "string",
    "value": "pink",
    "is_default": False # whether the fallback value had to be used
}

# Another usage case
kinde_client.get_flag("is_dark_mode");
# returns
{
    "code": "is_dark_mode",
    "type": "boolean",
    "value": True,
    "is_default": False
}

# This flag does not exist - default value provided
kinde_client.get_flag("create_competition", default_value = False);
# returns
{
    "code": "create_competition",
    "type" => "boolean",
    "value": False,
    "is_default": True # because fallback value had to be used
}

# The flag type was provided as string, but it is an integer
kinde_client.get_flag("competitions_limit", default_value = 3, flat_type = "s");
# should error out - Flag "competitions_limit" is type integer - requested type string


# This flag does not exist, and no default value provided
kinde_client.get_flag("new_feature");
# should error out - This flag was not found, and no default value has been provided

We also provide wrapper functions which should leverage getFlag above.

Get boolean flags

kinde_client.get_boolean_flag("is_dark_mode");
# True

kinde_client.get_boolean_flag("is_dark_mode", False);
# True

kinde_client.get_boolean_flag("new_feature", False);
# False (flag does not exist so falls back to default)

kinde_client.get_boolean_flag("new_feature");
# Error - flag does not exist and no default provided

kinde_client.get_boolean_flag("theme", False);
# Error - Flag "theme" is of type string not boolean

Get string flags

kinde_client.get_string_flag("theme");
# "pink"

kinde_client.get_string_flag("theme", False);
# "pink"

kinde_client.get_string_flag("cta_color", False);
# "blue" (flag does not exist so falls back to default)

kinde_client.get_string_flag("cta_color");
# Error - flag does not exist and no default provided

kinde_client.get_string_flag("is_dark_mode", False);
# Error - Flag "is_dark_mode" is of type boolean not string

Get integer flags

kinde_client.get_integer_flag("competitions_limit");
# 5

kinde_client.get_integer_flag("competitions_limit", 3);
# 5

kinde_client.get_integer_flag("team_count", 2);
# 2 (flag does not exist so falls back to default)

kinde_client.get_integer_flag("team_count");
# Error - flag does not exist and no default provided

kinde_client.get_integer_flag("is_dark_mode", False);
# Error - Flag "is_dark_mode" is of type string not integer

An audience is the intended recipient of an access token - for example the API for your application. The audience argument can be passed to the Kinde client to request an audience be added to the provided token.

The audience of a token is the intended recipient of the token.

kinde_api_client_params["audience"] = "api.yourapp.com"

For details on how to connect, see Register an API

Overriding scope

Link to this section

By default the KindeSDK requests the following scopes:

  • profile
  • email
  • offline
  • openid

You can override this by passing scope into the KindeSDK.

kinde_api_client_params["scope"] = "profile email offline openid"

Getting claims

Link to this section

We have provided a helper to grab any claim from your id or access tokens. The helper defaults to access tokens:

kinde_client.get_claim("aud")
# {"name": "aud", "value": ["api.yourapp.com"]}

kinde_client.get_claim("given_name", "id_token")
# {"name": "given_name", "value": "David"}

Create an organization

Link to this section

To create a new organization within your application, you will need to run a similar function to below:

return app.redirect(kinde_client.create_org())

Sign up and sign in to organizations

Link to this section

Kinde has a unique code for every organization. You’ll have to pass this code through when you register a new user or sign into a particular organization. Example function below:

kinde_api_client_params["org_code"] = 'your_org_code'

@app.route("/login")
def login():
    return app.redirect(kinde_client.get_login_url())


@app.route("/register")
def register():
    return app.redirect(kinde_client.get_register_url())

Following authentication, Kinde provides a json web token (jwt) to your application. Along with the standard information we also include the org_code and the permissions for that organization (this is important as a user can belong to multiple organizations and have different permissions for each).

Example of a returned token:

{
   "aud": [],
   "exp": 1658475930,
   "iat": 1658472329,
   "iss": "https://your_subdomain.kinde.com",
   "jti": "123457890",
   "org_code": "org_1234",
   "permissions": ["read:todos", "create:todos"],
   "scp": [
		   "openid",
		   "profile",
		   "email",
		   "offline"
   ],
   "sub": "kp:123457890"
}

The id_token will also contain an array of organizations that a user belongs to - this is useful if you wanted to build out an organization switcher for example.

{
		...
		"org_codes": ["org_1234", "org_4567"],
		...
};

There are two helper functions you can use to extract information:

kinde_client.get_organization()
# {"org_code": "org_1234"}

kinde_client.get_user_organizations()
# {"org_codes": ["org_1234", "org_abcd"]}

For more information about how organizations work in Kinde, see Kinde organizations for developers.

Once the user has successfully authenticated, you’ll get a JWT and possibly a refresh token that should be stored securely.

SDK API reference

Link to this section

Either your Kinde instance url or your custom domain. e.g. https://yourapp.kinde.com.

Type: string

Required: Yes

The url that the user will be returned to after authentication.

Type: string

Required: Yes

The ID of your application in Kinde.

Type: string

Required: Yes

Define the grant type when using the SDK.

Type: string

Required: Yes

The unique client secret associated with your application in Kinde.

Type: string

Required: No

PKCE works by having the app generate a random value at the beginning of the flow called a Code Verifier.

Type: string

Required: No, except for PKCE flow

The scopes to be requested from Kinde.

Type: string

Required: No

Default: openid profile email offline

The audience claim for the JWT.

Type: string

Required: No

Additional parameters that will be passed in the authorization request.

Type: string

Required: No

KindeSDK methods

Link to this section

Constructs a redirect URL and sends the user to Kinde to sign in. Optional parameters are used for custom sign-up and sign-in and are documented here.

Arguments (optional):

auth_url_params: Optional[Dict[str, Dict[str, str]]]

Usage:

kinde_client.get_login_url()

# Or with optional auth URL parameters
kinde_client.get_login_url({
		"auth_url_params": {
			"connection_id": "conn_6a95dec504d34dc286dc80e8df9f6099"
		}
})

Sample output:

https://your_host.kinde.com/oauth2/auth?response_type=code&

get_register_url

Link to this section

Constructs a redirect URL and sends the user to Kinde to sign up. Optional parameters are used for custom sign-up and sign-in and are documented here.

Arguments (optional):

auth_url_params: Optional[Dict[str, Dict[str, str]]]

Usage:

kinde_client.get_register_url()

# Or with optional auth URL parameters
kinde_client.get_register_url({
		"auth_url_params": {
			"connection_id": "conn_6a95dec504d34dc286dc80e8df9f6099"
		}
})

Sample:

https://your_host.kinde.com/oauth2/auth?response_type=code&

Logs the user out of Kinde.

Arguments:

redirect_to: str

Usage:

kinde_client.logout(redirect_to="KINDE_POST_LOGOUT_REDIRECT_URL")

Sample:

https://your_host.kinde.com/logout?redirect=https://

Returns the raw access token from URL after logged in from Kinde.

Arguments:

authorization_response: str

Usage:

kinde_client.fetch_token(authorization_response=[http://localhost:8000?code=42..e9&state=d..t](https://example.com/github?code=42..e9&state=d..t))

Sample:

eyJhbGciOiJIUzI1...

Get new access token from Kinde if existed refresh_token.

Usage:

kinde_client.refresh_token()

Return the redirect URL to sign up and create a new organization in your business.

Usage:

kinde_client.create_org()

Sample:

https://your_host.kinde.com/oauth2/auth?response_type=code&

Gets a claim from an access or ID token.

Arguments:

claim: str, token_name?: str # default: access_token

Usage:

kinde_client.get_claim("given_name", "id_token")

Sample:

{"name": "given_name", "value": "David"}

get_permission

Link to this section

Returns the state of a given permission.

Arguments:

key: str

Usage:

kinde_client.get_permission(”read:todos”)

Sample:

{”org_code”: "org_b235c067b7e4", is_granted: True}

get_permissions

Link to this section

Returns all permissions for the current user for the organization they are signed into.

Usage:

kinde_client.get_permissions()

Sample:

{"org_code": "org_b235c067b7e4", permissions: [ "create:users", "view:users" ]}

get_organization

Link to this section

Get details for the organization your user is signed into.

Usage:

kinde_client.get_organization()

Sample:

{"org_code": "org_1234"}

get_organizations

Link to this section

Gets an array of all organizations the user has access to.

Usage:

kinde_client.get_user_organizations()

Sample:

{"org_codes": ["org_1234", "org_abcd"]}

get_user_details

Link to this section

Returns the profile for the current user.

Usage:

kinde_client.get_user_details()

Sample:

{
	"given_name":"Dave",
	"id":"abcdef",
	"family_name":"Smith",
	"email":"dave@smith.com",
	"picture": "https://link_to_avatar_url.abc.com"
}

Gets a feature flag from an access token.

Arguments:

code: str
default_value?: str
flag_type?: str

Usage:

kinde_client.get_flag("theme");

Sample:

{
    "code": "theme",
    "type": "string",
    "value": "pink",
    "is_default": False
}

get_boolean_flag

Link to this section

Gets a boolean feature flag from an access token.

Arguments:

code: str
default_value?: str

Usage:

kinde_client.get_boolean_flag("is_dark_mode");

Sample: True or False

get_string_flag

Link to this section

Gets a string feature flag from an access token.

Arguments:

code: str
default_value?: str

Usage:

kinde_client.get_string_flag("theme");

Sample: “pink”

get_integer_flag

Link to this section

Gets a integer feature flag from an access token

Arguments:

code: str
default_value?: str

Usage:

kinde_client.get_integer_flag("competitions_limit");

Sample: 5

is_authenticated()

Link to this section

To check user authenticated or not.

Sample: true or false

If you need help connecting to Kinde, please contact us at support@kinde.com.


Talk to us

If you can’t find what you’re looking for in our help center — email our team

Contact support