The Kinde Elixir SDK allows developers to connect their Elixir app to Kinde.

Register for Kinde

Link to this section

If you haven’t already got a Kinde account, register for free here (no credit card required).

You also need a Kinde domain 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.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.

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

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

You can set your keys in your application configuration. Use config/config.exs. For example:

config :kinde_sdk,
  backend_client_id: "test_x1y2z3a1",
  frontend_client_id: "test_a1b2c3d4",
  client_secret: "test_112233",
  redirect_url: "http://text.com/callback",
  domain: "https://test.kinde.com",
  logout_redirect_url: "http://text.com/logout"

Optionally, you can also set scope as well.

config :kinde_sdk,
  scope: "email"

Environment variables

Link to this section

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

  • 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

After setting .env file you can use System.get_env/1 to retrieve the API key from an environment variables. For example:

config :kinde_sdk,
  backend_client_id: System.get_env("KINDE_BACKEND_CLIENT_ID")

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)
  )

Login and registration

Link to this section

The Kinde client provides methods for easy login / 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)

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. If the token is missing the profile please make sure the correct scopes are requested (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

Link to this section

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.

To helper function to have a new organization created within your application

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

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 when creating a client in additional_parameters_org_code through when you register a new user.

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

The Kinde SDK client comes with a logout method.

conn = KindeClientSDK.logout(conn)

Returns whether if a user is logged 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. 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"]
}

When it accepts a key for a token as KindeClientSDK.get_claim(conn, "jti", :id_token) then returns the claim value 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 SDK 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:

  • One way to do this is by using 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.
  • Another option is to 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.

SDK API reference

Link to this section

Create Kinde client

Link to this section
PropertyTypeIs requiredDefaultDescription
domainstringYesEither your Kinde instance url or your custom domain. e.g https://yourapp.kinde.com/
redirect_urlstringYesThe url that the user will be returned to after authentication
backend_client_idstringYesThe id of your backend application
frontend_client_idstringYesThe id of your frontend application
client_secretstringYesThe id secret of your application - get this from the Kinde admin area
logout_redirect_urlstringYesWhere your user will be redirected upon logout
scopestringNoopenid profile email offlineThe scopes to be requested from Kinde
additional_parametersmapNo%{}Additional parameters that will be passed in the authorization request
additional_parameters_audiencestringNoThe audience claim for the JWT
additional_parameters_org_namestringNoThe org claim for the JWT
additional_parameters_org_codestringNoThe org claim for the JWT

Kinde client functions

Link to this section
PropertyDescriptionArgumentsUsageSample output
loginConstructs redirect url and sends user to Kinde to sign inconnclientKindeClientSDK.login(conn, client)redirect
registerConstructs redirect url and sends user to Kinde to sign upconnclientKindeClientSDK.register(conn, client)redirect
logoutLogs the user out of KindeconnKindeClientSDK.logout(conn)redirect
get_tokenReturns the raw access token from URL after logged from KindeconnKindeClientSDK.get_token(conn)eyJhbGciOiJIUzI1..
create_orgConstructs redirect url and sends user to Kinde to sign up and create a new org for your businessconnatomKindeClientSDK.create_org(conn, client)redirect
get_claimsGets all claims from an access or id tokenconnatomKindeClientSDK.get_claims(conn) or KindeClientSDK.get_claims(conn, :id_token)%{"aud" => [], "azp" => "", ...}
get_claimGets a claim from an access or id tokenconnstringatomKindeClientSDK.get_claim(conn, "jti") or KindeClientSDK.get_claim(conn, "jti", :id_token)"David"
get_permissionsReturns the state of a all permissionsconnatomKindeClientSDK.get_permissions(conn, :id_token)%{org_code: 'org_1234', is_granted: true}
get_permissionReturns the state of a given permissionconnstringKindeClientSDK.get_permission(conn, "create:users")%{org_code: 'org_1234', permissions: ['create:todos', 'update:todos', 'read:todos']}
get_organizationGet details for the organization your user is logged intoconnKindeClientSDK.get_user_organization(conn)%{org_code: "org_9d78"}
get_user_detailReturns the profile for the current userconnKindeClientSDK.get_user_detail(conn)%{email: "dev@smit.com", family_name: "Smith", given_name: "Dave", id: "kp:abcdef"}
get_user_organizationsReturns the org code from the user tokenconnKindeClientSDK.get_user_organizations(conn)%{org_codes: ["org_9d78", "org_aca6c", "org_27e56"]}
get_cache_pidReturns the Kinde cache PID from the connconnKindeClientSDK.get_cache_pid(conn)#PID
save_kinde_clientSaves the Kinde client created into the connconnKindeClientSDK.save_kinde_client(conn):ok
get_kinde_clientReturns the Kinde client created from the connconnKindeClientSDK.get_kinde_client(conn)%KindeClientSDK{ cache_pid: #PID<0.123.0>, domain: "abcd.com", redirect_uri: "…http://text.com/callback", logout_redirect_uri: …}
get_all_dataReturns all the Kinde data (tokens) returnedconnKindeClientSDK.get_all_data(conn)%{access_token: "eyJhbGciOiJSU…”, expires_in: 1234, id_token: “abcdedhjshfsjg”,…}

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 us