The Kinde Ruby SDK gem allows developers to integrate Kinde API into any ruby-based applications, Rails or non-Rails.
The gem contains all the related oauth2 authorization, and 3 pre-built OAuth flows: client credentials, authorization code and authorization code with PKCE code verifier.
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
Add this line into your Gemfile and run a bundler or install manually through a gem command.
gem 'kinde_sdk'
- In Kinde, go to Settings > Applications > [Your app] > View details.
- 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
- Allowed callback URLs (also known as redirect URIs) - for example,
- Select Save.
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.
The following variables need to be replaced in the code snippets below.
KINDE_HOST
- your Kinde domain - e.g.https://your_kinde_domain.kinde.com
KINDE_REDIRECT_URL
- your callback url, make sure this URL is under your allowed callback redirect URLs. - e.g.http://localhost:8000/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:8000
KINDE_CLIENT_ID
- you can find this on the Application details page - e.g.your_kinde_client_id
KINDE_CLIENT_SECRET
- you can find this on the Application details page - e.g.your_kinde_client_secret
You can easily configure via the gem. For example, in a typical Rails-app it can be configured through the initializer file:
KindeSdk.configure do |c|
c.domain = domain
c.client_id = client_id
c.client_secret = client_secret
c.callback_url = callback_url
c.logout_url = logout_url
# c.scope = 'openid offline email profile' # default value
# c.pkce_enabled = true # default value
# c.authorize_url = '/oauth2/auth' # default value
# c.token_url = '/oauth2/token' # default value
# c.debugging = false # default value
# c.business_name = nil # default value
c.logger = Rails.logger
end
The snippet above contains all the possible configuration values. Here is detailed explanation of them:
Domain
refers to your organization - for example,your-biz.kinde.com
.Client id
andClient secret
can be found in Kinde. Go to Settings > Applications > [yourapplication] > Details.Callback url
(or redirect URI) refers to the callback processing action. The URL must be defined in the Allowed callback URLs section of your application.Logout url
will open when the user signs out. The URL must be defined in the Allowed callback URLs section of your application.Scope
is an Oauth special parameter which is used to limit some rights.PKCE enabled
is a flag that can turn off PKCE auth flow. By default it is activated to improve security.Authorize url
andToken url
are paths to Oauth2 methods in kinde.Debugging
set toTrue
for long request logs. Can be useful while developing your application.Business name
is a parameter which is used in requests building. By default it is extracted from your Kindedomain
endpoint. For example, if your domain isyour-biz.kinde.com
, then business name will be set toyour-biz
. You don’t need to change it.Logger
set to whichever kind of loggers you are using. By default it is set toRails.logger
if gem is used in rails application orLogger.new(STDOUT)
if it is not a rails app.
These variables can be handled with any system you want: .env files, settings.yml or any type of config files. For example, .env file (you can name variables by yourself):
KINDE_DOMAIN=https://example.kinde.com
KINDE_CLIENT_ID=qwe
KINDE_CLIENT_SECRET=wert
KINDE_CALLBACK_URL=http://localhost:3000/callback
KINDE_LOGOUT_URL=http://localhost:3000/logout_callback
can be used as:
KindeSdk.configure do |c|
c.domain = ENV['KINDE_DOMAIN']
c.client_id = ENV['KINDE_CLIENT_ID']
# ....
end
The Kinde client provides methods for easy login / registration. For this, you need to acquire auth url by calling:
KindeSdk.auth_url
# =>
{
url: "https://<domain>/oauth2/auth?client_id=<client_id>&code_challenge=<generated code>&code_challenge_method=S256&redirect_uri=<redirect_uri>&response_type=code&scope=openid+offline+email+profile&state=<random string>",
code_verifier: "<challenge verifier>"
}
By default, gem uses the PKCE verification flow. This means that the code challenge
param will be added to your auth url, and the method returns verification string for the code. This can also be used in token requests. You can disable PKCE by setting pkce_enabled
to false in your configuration. In this case, KindeSdk.auth_url
will only return a url:
KindeSdk.auth_url
# => {url: ......}
If you are about to use PCKE, our recommendation to save code verifier output somewhere near your later tokens output.
Put the link in your web-application page or use it under the hood as a redirect. After visiting the link users are redirected to Kinde’s sign in/sign up form. And after authorizing in Kinde, they are redirected to the callback url.
The next step is to extract code from the callback redirect. Your callback endpoint should contain logic to call the exchange method. Callback is triggered in the body with the code. Use the whole params
object or to extract code from params["code"]
.
Next, exchange access and refresh tokens. You will receive code
as the parameter in the callback endpoint, and code_verifier
(if PKCE enabled) as per the previous step.
KindeSdk.fetch_tokens(code, code_verifier)
# =>
{"access_token"=>"eyJhbGciOiJSUzI1NiIsIm...",
"expires_in"=>86399,
"id_token"=>"eyJhbGciOiJSUz",
"refresh_token"=>"eyJhbGciOiJSUz",
"scope"=>"openid offline email profile",
"token_type"=>"bearer"}
Save the whole hash in your session, redis or any other storage, and use it to build your client.
# In case of preventing cookie overflow, you need to limit what exactly your are saving.# Here is the required minimum of params. But in general you are able save it wherever you want to.# For example, in database, without any limiting.
session[:kinde_auth] = KindeSdk.fetch_tokens(code).slice(:access_token, :refresh_token, :expires_at)
# ...
client = KindeSdk.client(session[:kinde_auth]["access_token"])# => #<KindeSdk::Client:0x00007faf31e5ecb8>
For logout you need to get the logout URL and redirect to it:
redirect_to KindeSdk.logout_url, allow_other_host: true
If you configured logout redirect_url correctly, you will receive a logout callback. Use it to perform clean-ups, clear your session or storage (delete your token) and redirect to wherever you want to.
You need to have already authenticated before you call the API, otherwise an error will occur.
Use the Kinde\KindeSDK\Api\OAuthApi
class, then call the getUser
method.
KindeSdk.client(session[:kinde_auth]["access_token"]).oauth.get_user
# => {id: ..., preferred_email: ..., provided_id: ..., last_name: ..., first_name: ...}
Go to the Users page in Kinde to see who has registered.
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:
client = KindeSdk.client(session[:kinde_auth]["access_token"])
client.get_permission("create:todos")# => {org_code: "org_1234", is_granted: true}
client.permission_granted?("create:todos")# => true
client.permission_granted?("create:orders")# => false
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 #auth_url
method to request an audience be added to the provided token:
KindeSdk.auth_url(audience: "<https://your-app.kinde.com/api>")
For details on how to connect, see Register an API.
By default KindeSdk
requests the following scopes:
profile
email
offline
openid
You can change scopes by configuring then when you install or by direct param passing into auth_url
method:
KindeSdk.auth_url(scope: "openid offline")
We have provided a helper to grab any claim from your id or access tokens. The helper defaults to access tokens:
client = KindeSdk.client(session[:kinde_auth]["access_token"])
client.get_claim("aud")#=> ['api.yourapp.com']
client.get_claim("scp")#=> ["openid", "offline"]
To have a new organization created within your application, you will need to run something like:
client.organizations.create_organization(create_organization_request: {name: "new_org"})
# or `client.organizations.create_organization` without name
Kinde has a unique code for every organization. If you want a user to sign into a particular organization, call the #auth_url
method with org_code
param passing:
KindeSdk.auth_url(org_code: "org_1234", start_page: "registration")# to enforce new user creation form
KindeSdk.auth_url(org_code: "org_1234")# to login by default
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:
client.get_claim("org_codes")# => ["org_1234", "org_5462"]
KindeSdk.client(session[:kinde_auth]["access_token"]).oauth.get_user
# => {id: ..., preferred_email: ..., provided_id: ..., last_name: ..., first_name: ...}
The sections below relate to the Kinde Management API. You need to add a machine to machine application and get an access token to connect:
result = KindeSdk.client_credentials_access(
client_id: ENV["KINDE_MANAGEMENT_CLIENT_ID"],
client_secret: ENV["KINDE_MANAGEMENT_CLIENT_SECRET"]
)
# as an example of usage redis to save access token:
$redis.set("kinde_m2m_token", result["access_token"], ex: result["expires_in"].to_i)
client = KindeSdk.client($redis.get("kinde_m2m_token"))
# get organizations list:
client.organizations.get_orgainzations
# => {"code": "OK", "message": "Success", "next_token": "qweqweqwe", "organizations": [{"code": "org_casda123c", "name": "Default Organization", "is_default": true}]}
# create new organization:
client.organizations.create_organization(create_organization_request: {name: "new_org"})
# this variant for more strict input params validation:
# client.organizations.create_organization(create_organization_request: KindeSdk::CreateOrganizationRequest.new(name: new_org_name))
client.users.create_user
client.organizations.add_organization_users(code: "org_1111", users: ["kp:12311...."])
For proper refreshing you’ll need to use access_token
, refresh_token
and probably expires_in
if you want to know if your access token still active. Use these two methods to work with refreshing:
KindeSdk.token_expired?(session[:kinde_auth])# => false
KindeSdk.refresh_token(session[:kinde_auth])# => {"access_token" => "qwe...", "refresh_token" => "fqw...", .....}
KindeSdk#refresh_token
returns a new token hash, so it needs to be updated in your storage.
Property | Type | Is required | Default | Description |
---|---|---|---|---|
host | string | Yes | Either your Kinde instance url or your custom domain. e.g https://yourapp.kinde.com/ | |
redirectUri | string | Yes | The url that the user will be returned to after authentication | |
clientId | string | Yes | The id of your application - get this from the Kinde admin area | |
clientSecret | string | Yes | The id secret of your application - get this from the Kinde admin area | |
logoutRedirectUri | string | Yes | Where your user will be redirected upon logout | |
scope | string | No | openid profile email offline | The scopes to be requested from Kinde |
additionalParameters | array | No | [] | Additional parameters that will be passed in the authorization request |
additionalParameters - audience | string | No | The audience claim for the JWT |
Property | Description | Arguments | Usage | Sample output |
---|---|---|---|---|
login | Constructs redirect url and sends user to Kinde to sign in | org_code?: string | $kinde->login(); | |
register | Constructs redirect url and sends user to Kinde to sign up | org_code?: string | $kinde->register(); | |
logout | Logs the user out of Kinde | $kinde->logout(); | ||
getToken | Returns the raw access token from URL after logged from Kinde | $kinde->getToken(); | eyJhbGciOiJIUzI1... | |
createOrg | Constructs redirect url and sends user to Kinde to sign up and create a new org for your business | org_name?: string | $kinde->createOrg(); or $kinde->createOrg(['org_name' => 'your organization name'}); | redirect |
getClaim | Gets a claim from an access or id token | claim: string , tokenKey?: string | $kinde->getClaim('given_name', 'id_token'); | David |
getPermission | Returns the state of a given permission | key: string | $kinde->getPermission('read:todos'); | ['orgCode' => 'org_1234', 'isGranted' => true] |
getPermissions | Returns all permissions for the current user for the organization they are logged into | $kinde->getPermissions(); | ['orgCode' => 'org_1234', permissions => ['create:todos', 'update:todos', 'read:todos']] | |
getOrganization | Get details for the organization your user is logged into | $kinde->getOrganization(); | ['orgCode' => 'org_1234'] | |
getUserDetails | Returns the profile for the current user | $kinde->getUserDetails(); | ['given_name' => 'Dave', 'id' => 'abcdef', 'family_name' => 'Smith', 'email' => 'mailto:dave@smith.com'] | |
getUserOrganizations | Gets an array of all organizations the user has access to |
If you need help connecting to Kinde, please contact us at support@kinde.com.
Developer tools