The Kinde Elixir SDK allows developers to connect their Elixir app to Kinde. This document is relevant for up to Elixir v1.2.0.

You can also view the Elixir docs and Elixir starter kit in GitHub.

Register for Kinde

Link to this section

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.

Install erlang and elixir. Update the deps, update path to the SDK in mix.exs.

{:kinde_sdk, "~> 1.2.0"}

Add to your extra applications in mix.exs.

def application do
   [
     extra_applications: [:logger, :kinde_sdk]
   ]
end

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:4000/callback
    • Allowed logout redirect URLs - for example, http://localhost:4000
  3. Select Save.

Note: The http://localhost:4000 is an example of a commonly used local development URL. It should be replaced with the URL where your app is running.

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

API Keys

Follow these steps to set up the project:

  1. Create a .env file at the root directory.
  2. Add the following, but change the “values” to match your own information.
export KINDE_BACKEND_CLIENT_ID="test_x1y2z3a1"
export KINDE_FRONTEND_CLIENT_ID="test_a1b2c3d4"
export KINDE_CLIENT_SECRET="test_112233"
export KINDE_REDIRECT_URL="http://text.com/callback"
export KINDE_DOMAIN="https://test.kinde.com"
export KINDE_LOGOUT_REDIRECT_URL="http://text.com/logout"
export KINDE_PKCE_LOGOUT_URL="http://test.com/logout"
export KINDE_PKCE_REDIRECT_URL="http://test.com/pkce-callback"
export KINDE_BASE_URL="https://app.kinde.com"
  1. If required, set the scopes. You can include scopes such as openid profile offline, in addition to email.
config :kinde_sdk, scope: "email"
  1. Open the console and write source .env before any mix command.

Environment variables

You can also set these variables in .env file within your project directory. The following variables need to be replaced in the code snippets.

  • KINDE_HOST - your Kinde domain - e.g. https://yourkindedomain.kinde.com
  • KINDE_REDIRECT_URL - your callback url, make sure this URL is under your allowed callback redirect URLs. - e.g. http://localhost:4000/callback
  • KINDE_POST_LOGOUT_REDIRECT_URL - where you want users to be redirected to after logging out, make sure this URL is under your allowed logout redirect URLs. - e.g. http://localhost:4000
  • KINDE_CLIENT_ID - you can find this on the Application details page
  • KINDE_CLIENT_SECRET - you can find this on the Application details page

Execute following in your terminal to run:

mix deps.get
mix phx.server

OAuth Flows (Grant Types)

Link to this section

The KindeClientSDK struct implements three OAuth flows:

  • Client Credentials flow
  • Authorization Code flow
  • Authorization Code with PKCE flow

Each flow can be used with their corresponding grant type when initializing a client.

OAuth FlowGrant TypeType
Client Credentials:client_credentialsatom
Authorization Code:authorization_codeatom
Authorization Code with PKCE:authorization_code_flow_pkceatom

Integrate with your app

Link to this section

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

{conn, client} =
  KindeClientSDK.init(
    conn,
    Application.get_env(:kinde_sdk, :domain),
    Application.get_env(:kinde_sdk, :redirect_url),
    Application.get_env(:kinde_sdk, :backend_client_id),
    Application.get_env(:kinde_sdk, :client_secret),
    :client_credentials,
    Application.get_env(:kinde_sdk, :logout_redirect_url)
  )

Log in and registration

Link to this section

The Kinde client provides methods for easy log in and registration.

You can add buttons in your HTML as follows:

<div class="navigation">
    <a href="/login" type="button">Login</a>
    <a href="/register" type="button">Register</a>
</div>

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

conn = KindeClientSDK.login(conn, client)
conn = KindeClientSDK.register(conn, client)

Register your first user by signing up yourself. You’ll see your newly registered user on the Users page of the relevant organization (or default organization) in Kinde.

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.

def callback(conn, _params) do
    {conn, client} = KindeClientSDK.get_token(conn)

    data = KindeClientSDK.get_all_data(conn)
end

We use the Kinde helper function to get the tokens generated by login and get_token.

data = KindeClientSDK.get_all_data(conn)
IO.inspect(data.login_time_stamp, label: "Login Time Stamp")

Or first calling the get_token function:

{conn, client} = KindeClientSDK.get_token(conn)

Example of a returned token:

%{
	"access_token" => "eyJhbGciOiJSUzI1...",
	"expires_in" => 89274,
	"scope" => "openid profile email offline",
	"token_type" => "bearer"
}

This function returns the user object including Kinde ID. This function reads the information from the id_token that is returned after successful authentication. Include the required scopes if not added already (openid profile email offline).

KindeClientSDK.get_user_detail(conn)

Note: You need to have already authenticated before you call the API, otherwise an error will occur.

View users in Kinde

Link to this section

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

Create an organization

There is an additional create_org method which allows an organization to be created. This method calls the current sign-up logic by setting the is_create_org parameter to true.

Use this helper function to create an organization.

conn = KindeClientSDK.create_org(conn, client)
conn = KindeClientSDK.create_org(conn, client)

Sign up and sign in to organizations

Kinde has a unique code for every organization. You’ll have to pass this code when creating a client or registering a new user: additional_parameters_org_code.

If you want a user to sign into a particular organization, pass this code along with the sign in method.

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

The Kinde SDK client comes with a logout method.

conn = KindeClientSDK.logout(conn)

Returns whether if a user is signed in by verifying that the access token is still valid.

KindeClientSDK.authenticated?(conn)

We have provided a helper to grab any claim from your ID or access tokens, which accepts a key for a token and returns the claim value. There is also an optional argument to define which token to check. The helper defaults to access tokens.

KindeClientSDK.get_claims(conn)

KindeClientSDK.get_claim(conn, "jti", :id_token)

This function will returns claims as follows.

%{
	"aud" => [],
	"azp" => "",
	"exp" => 1649314,
	"gty" => ["client_credentials"],
	"iat" => 1662914,
	"iss" => "https://smith.com",
	"jti" => "7fa15b8-086-495-bba-b191b2aa2f",
	"scp" => ["openid", "profile", "email", "offline"]
}

For example, when a key is accepted for a token as KindeClientSDK.get_claim(conn, "jti", :id_token) the return claim value would be as follows.

"57012a4-82ca-41f-8f19-0a5c3cc653"

When a user signs in to an organization the access token your product/application contains a custom claim with an array of permissions for that user.

You can set permissions in your Kinde account. Here’s an example.

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

For more details See Define user permissions.

We provide helper functions to more easily access permissions.

KindeClientSDK.get_permissions(conn)
KindeClientSDK.get_permission(conn, "create:todos")

get_permission checks the permission value and returns if it is granted or not (i.e. checks if permission key exists in the permissions claim array) and checks the relevant org code by checking against claim org_code.

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.

additional_params = %{
      audience: "api.yourapp.com"
    }

KindeClientSDK.init(
  conn,
  Application.get_env(:kinde_sdk, :domain),
  Application.get_env(:kinde_sdk, :redirect_url),
  Application.get_env(:kinde_sdk, :backend_client_id),
  Application.get_env(:kinde_sdk, :client_secret),
  :authorization_code_flow_pkce,
  Application.get_env(:kinde_sdk, :logout_redirect_url),
  "openid profile email offline",
  additional_params
  )

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.

Persisting authentication state on page refresh or new tab

Link to this section

When a user refreshes the page or opens a new tab, the authentication state can be lost. To work around this issue, there are two possible solutions:

  • Use cookies to store the authentication token. This can be done by setting an httpOnly cookie with the authentication token, which will be sent to the server with every request, allowing the server to maintain the authentication state.
  • Use a session store to store the authentication token. Elixir has several session store options available, including using a database, in-memory cache, or distributed cache.

Once one of these solutions is implemented, there is no need for additional action to persist the authentication state.

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

API reference - Create Kinde Client

Link to this section

Either your Kinde 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

backend_client_id

Link to this section

The unique ID of your backend application in Kinde.

Type: string

Required: Yes

frontend_client_id

Link to this section

The unique ID of your frontend application in Kinde.

Type: string

Required: Yes

The unique client secret associated with your application in Kinde.

Type: string

Required: No

logout_redirect_url

Link to this section

Where your user will be redirected upon logout.

Type: string

Required: No, except for PKCE flow

The scopes to be requested from Kinde.

Type: string

Required: No

Default: openid profile email offline

additional_parameters

Link to this section

Additional parameters that will be passed in the authorization request.

Type: map

Required: No

Default: %{}

additional_parameters_audience

Link to this section

The audience claim for the JWT.

Type: string

Required: No

additional_parameters_org_name

Link to this section

The org claim for the JWT.

Type: string

Required: No

additional_parameters_org_code

Link to this section

The org claim for the JWT.

Type: string

Required: No

API reference - Kinde Client Functions

Link to this section

Constructs a redirect URL and sends the user to Kinde to sign in.

Arguments: conn, client

Usage:

KindeClientSDK.login(conn, client)

Sample output: redirect

Constructs a redirect URL and sends the user to Kinde to sign up.

Arguments: conn, client

Usage:

KindeClientSDK.register(conn, client)

Sample: redirect

Logs the user out of Kinde.

Arguments: conn

Usage:

KindeClientSDK.logout(conn)

Sample: redirect

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

Arguments: conn

Usage:

KindeClientSDK.get_token(conn)

Sample:

eyJhbGciOiJIUzI1...

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

Arguments: conn, atom

Usage:

KindeClientSDK.create_org(conn, client)

Sample: redirect

Gets a claim from an access or ID token.

Arguments: conn, string, atom

Usage:

KindeClientSDK.get_claim(conn, "jti") or KindeClientSDK.get_claim(conn, "jti", :id_token)

Sample:

%{name: "iss", value: "https://elixirsdk2.kinde.com"}

Gets all claims from an access or ID token.

Arguments: conn, atom

Usage:

KindeClientSDK.get_claims(conn)
 or 
KindeClientSDK.get_claims(conn, :id_token)

Sample:

%{"aud" => [], "azp" => "", ...}

get_permission

Link to this section

Returns the state of a given permission.

Arguments: conn, string

Usage:

KindeClientSDK.get_permission(conn, "create:users")

Sample:

%{org_code: 'org_1234', is_granted: true}

get_permissions

Link to this section

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

Arguments: conn, atom

Usage:

KindeClientSDK.get_permissions(conn, :id_token)

Sample:

%{org_code: 'org_1234', permissions: ['create:todos', 'update:todos', 'read:todos']}

get_organization

Link to this section

Get details for the organization your user is signed into.

Arguments: conn

Usage:

KindeClientSDK.get_user_organization(conn)

Sample:

%{org_code: "org_9d78"}

get_organizations

Link to this section

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

Arguments: conn

Usage:

KindeClientSDK.get_user_organizations(conn)

Sample:

%{org_codes: ["org_9d78", "org_aca6c", "org_27e56"]}

get_user_details

Link to this section

Returns the profile for the current user.

Arguments: conn

Usage:

KindeClientSDK.get_user_detail(conn)

Sample:

%{
email: "dev@smit.com", family_name: "Smith", given_name: "Dave", id: "kp:abcdef"
}

Returns the Kinde cache PID from the conn.

Arguments: conn

Usage:

KindeClientSDK.get_cache_pid(conn)

Sample:

#PID

save_kinde_client

Link to this section

Saves the Kinde client created into the conn.

Arguments: conn

Usage:

KindeClientSDK.save_kinde_client(conn)

Sample:

:ok

get_kinde_client

Link to this section

Returns the Kinde client created from the conn.

Arguments: conn

Usage:

KindeClientSDK.get_kinde_client(conn)

Sample:

%KindeClientSDK{
cache_pid: #PID<0.123.0>,
domain: "abcd.com",
redirect_uri: "…/callback",
logout_redirect_uri:}

Returns all the Kinde data (tokens) returned.

Arguments: conn

Usage:

KindeClientSDK.get_all_data(conn)

Sample:

%{
access_token: "eyJhbGciOiJSU…”,
expires_in: 1234,
id_token: “abcdedhjshfsjg”,}

Feature flag helper functions

Link to this section

Details of a feature-flag.

Arguments: conn, code

Usage:

KindeClientSDK.get_flag(conn, code)

Sample output:

flag: %{
"code" => "theme",
"is_default" => false,
"type" => "string",
"value" => "grayscale"
}

The default value of a feature flag.

Arguments: conn, code, default value

Usage:

KindeClientSDK.get_flag(conn, code, default_value)

Sample output:

flag: %{
"code" =>
"create_competition",
"is_default" => true, "value" => false
}

The type and default value of a feature flag.

Arguments: conn, code, default value, flag_type

Usage:

KindeClientSDK.get_flag(conn, code, default_value, flag_type)

Sample output:

flag: %{
"code" =>
"theme",
"is_default" => true,
"value" => "black"
}

get_boolean_flag/2

Link to this section

Returns the boolean flag.

Arguments: conn, code

Usage:

KindeClientSDK.get_boolean_flag(conn, code)

Sample output:

**true** | **false** |
flag: "Error e.g flag does not exist and no default provided”

get_boolean_flag/3

Link to this section

Returns the boolean flag value.

Arguments: conn, code, default value

Usage:

KindeClientSDK.get_boolean_flag(conn, code, default_value)

Sample output:

flag: false

get_string_flag/2

Link to this section

Returns the string flag value.

Arguments: conn, code

Usage:

KindeClientSDK.get_string_flag(conn, code)

Sample output: corresponding values from object or error-messages

get_string_flag/3

Link to this section

Returns the string flag value.

Arguments: conn, code, default value

Usage:

KindeClientSDK.get_string_flag(conn, code, default_value)

Sample output:

flag: "black”

get_integer_flag/2

Link to this section

Returns the integer flag value.

Arguments: conn, code

Usage:

KindeClientSDK.get_integer_flag(conn, code)

Sample output: corresponding values from object or error-messages

get_integer_flag/3

Link to this section

Returns the integer flag value.

Arguments: conn, code, default value

Usage:

KindeClientSDK.get_integer_flag(conn, code, default_value)

Sample output:

flag: 46

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