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

You can view the Kinde iOS SDK and iOS starter kit in GitHub.

Before you begin

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.

KindeSDK is available through CocoaPods. To install it, add the following line to your Podfile:

pod 'KindeSDK'

Please note that KindeSDK is typically used with Cocoapods dynamic linking (use_frameworks!), as it takes a dependency on AppAuth.

If integrating with other pods that require static linking, follow the instructions provided by Cocoapods.

Kinde configuration

Link to this section

Here you want to put in the callback URLs for your iOS app:

  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): <your_url_scheme>://kinde_callback
    • Allowed logout redirect URLs: <your_url_scheme>://kinde_logoutcallback
  3. Select Save.

Note: your_url_scheme can be any valid custom URL scheme, such as your app’s bundle ID or an abbreviation. It must match the scheme component of the Allowed callback URLs (redirect URIs) and Allowed logout redirect URLs you configured in the Application details page for your Kinde application.

If you would like to use our Environments feature as part of your development process. You will need to create them first within your Kinde account. In this case you would use the Environment subdomain in the code block above.

Configuring your app

Link to this section

Environment variables

Link to this section

The Kinde Auth service is configured with an instance of the Config class. The example uses the bundled kinde-auth.json for configuration.

To get the details, go to Settings > Applications > [your app] > View details. Then scroll to the App keys section.

  • issuer: your Kinde domain
  • clientId - you can find this on the Application details page
  • redirectUri (Allowed callback URL): After the user authenticates we will callback to this address. Make sure this URL is under your allowed callback URLs.
  • postLogoutRedirectUri (Allowed 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.
{
	"issuer": "https://<your-business>.kinde.com",
	"clientId": "<your-client-id>",
	"redirectUri": "<your-url-scheme>://kinde_callback",
	"postLogoutRedirectUri": "<your-url-scheme>://kinde_logoutcallback",
	"scope": "offline openid"
}

Replace the values in with your own values. For example:

{
	"issuer": "https://app.kinde.com",
	"clientId": "abc@live",
	"redirectUri": "com.example.App://kinde_callback",
	"postLogoutRedirectUri": "com.example.App://kinde_logoutcallback",
	"scope": "offline openid"
}

Integrate with your app

Link to this section

Before KindeSDKAPI.auth can be used, a call to KindeSDKAPI.configure() must be made, typically in AppDelegate as part of application(launchOptions) for a UIKit app, or the @main initialization logic for a SwiftUI app.

AppDelegate.swift

...
import KindeSDK
...

class AppDelegate: UIResponder, UIApplicationDelegate {
	...
	func application(...) {
		...
		// The Kinde authentication service must be configured before use
		KindeSDKAPI.configure(logger: Logger())
		...
	}
	...
}

Login and register

Link to this section

The Kinde client provides methods for an easy to implement login / register flow.

You can add buttons in your view as follows: (we’re using UIKit).

...
import KindeSDK
...

override func viewDidLoad() {
	...
	view.addSubview(signInButton)
	view.addSubview(signUpButton)
	signInButton.addTarget(self, action: #selector(signIn), for: .primaryActionTriggered)
	signUpButton.addTarget(self, action: #selector(register), for: .primaryActionTriggered)
	...
}

@objc private func signIn(_ target: UIButton) {
	KindeSDKAPI.auth.login { result in
		switch result {
			case let .failure(error):
				if !KindeSDKAPI.auth.isUserCancellationErrorCode(error) {
					self.alert("Login failed: \(error.localizedDescription)")
				}
			case .success:
				// Do something here
		}
	}
}

@objc private func register(_ target: UIButton) {
	KindeSDKAPI.auth.register { result in
		switch result {
			case let .failure(error):
				if !KindeSDKAPI.auth.isUserCancellationErrorCode(error) {
					self.alert("Registration failed: \(error.localizedDescription)")
				}
			case .success:
				// Do something here
		}
	}
}

Handle redirect

Link to this section

Once your user is redirected back to your site from Kinde (it means you’ve logged in successfully), use the getToken method from KindeSDKAPI class to get a user token from Kinde.

Let’s look at an example of successful login.

KindeSDKAPI.auth.login { result in
	switch result {
		case let .failure(error):
			if !KindeSDKAPI.auth.isUserCancellationErrorCode(error) {
				self.alert("Login failed: \(error.localizedDescription)")
			}
		case .success:
			self.onLoggedIn() // Calling this function
		}
}

func onLoggedIn() {
	self.isAuthenticated = true
	self.getToken()
}

private func getToken() {
  Task {
    await asyncGetToken()
  }
}

private func asyncGetToken() async -> String {
  do {
    let token = try await KindeSDKAPI.auth.getToken()
    return token
  } catch {
    return ""
  }
}

This is implemented in much the same way as logging in or registering. The Kinde SPA client comes with a logout method.

@objc private func logout(_ target: UIButton) {
		KindeSDKAPI.auth.logout { result in
		if result {
			// Do something
		}
	}
}

Get user information

Link to this section

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

KindeSDKAPI.auth.getUserDetails()
// User(id: 1233, email: "dave@smith.com", given_name: "Dave", family_name: "Smith")

View users in Kinde

Link to this section

Navigate to the Users page within Kinde to see your newly registered user.

User Permissions

Link to this section

Once a user has been verified, your application will be returned the JWT token with an array of permissions for that user. You will need to configure your application to read permissions and unlock the respective functions.

You set Permissions in your Kinde account, the below is an example set of permissions.

let 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:

KindeSDKAPI.auth.getPermission(name: "create:todos");
// Permission(organization: Organization(code: "org_1234"), isGranted: true)

KindeSDKAPI.auth.getPermissions();
// Permissions(organization: Organization(code: "org_1234"), permissions: ["create:todos", "update:todos", "read:todos"])

A practical example in code might look something like:

let isGranted = KindeSDKAPI.auth.getPermission(name: "create:todos")?.isGranted ?? false
if isGranted {
    // show Create Todo button in UI
}

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.

{
	"issuer": "https://<your-business>.kinde.com",
	"clientId": "<your-client-id>",
	"redirectUri": "<your-url-scheme>://kinde_callback",
	"postLogoutRedirectUri": "<your-url-scheme>://kinde_logoutcallback",
	"scope": "offline openid email profile",
	"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

{
	"issuer": "https://<your-business>.kinde.com",
	"clientId": "<your-client-id>",
	"redirectUri": "<your-url-scheme>://kinde_callback",
	"postLogoutRedirectUri": "<your-url-scheme>://kinde_logoutcallback",
	"scope": "offline openid email profile",
	"audience": "api.yourapp.com"
}

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:

KindeSDKAPI.auth.getClaim(key: "aud");
// ["api.yourapp.com"]

KindeSDKAPI.auth.getClaim(key: "given_name", token: .idToken);
// "David"

Create an organization

Link to this section

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

@objc private func createOrg(_ target: UIButton) async {
	do {
		try await KindeSDKAPI.auth.createOrg(orgName: "Your Organization")
	} catch {
		...
	}
}

Sign in and sign up 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.

Example function below:

KindeSDKAPI.auth.register(orgCode: "your_org_code");

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

KindeSDKAPI.auth.login(orgCode: "your_org_code");

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:

KindeSDKAPI.auth.getOrganization();
// Organization(code: "org_1234")

KindeSDKAPI.auth.getUserOrganizations();
// UserOrganizations(orgCodes: [Organization(code: "org_1234"), Organization(code: "org_abcd")])

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

We have provided a helper to return any features flag from the access token:

KindeSDKAPI.auth.getFlag(code: "theme")
// returns
Flag(code: "theme", type: Optional(KindeSDK.Flag.ValueType.string), value: black, isDefault: false)

KindeSDKAPI.auth.getFlag(code: "no-feature-flag")
// returns notFound

KindeSDKAPI.auth.getFlag(code: "no-feature-flag", defaultValue: "default-value")
// returns
Flag(code: "no-feature-flag", type: nil, value: "default-value", isDefault: true)

KindeSDKAPI.auth.getFlag(code: "no-feature-flag", defaultValue: "default-value", flagType: Flag.ValueType.bool)
// returns incorrectType("Flag \"theme\" is type string - requested type boolean")

We also require wrapper functions by type which should leverage getFlag above.

Get boolean flags

Link to this section
KindeSDKAPI.auth.getBooleanFlag(code: "is_dark_mode")
// true

KindeSDKAPI.auth.getBooleanFlag(code: "is_dark_mode", defaultValue: false)
// true

KindeSDKAPI.auth.getBooleanFlag(code: "new_feature")
// notFound

KindeSDKAPI.auth.getBooleanFlag(code: "new_feature", defaultValue: false)
// false

KindeSDKAPI.auth.getBooleanFlag(code: "theme")
// incorrectType("Flag \"theme\" is type string - requested type boolean")

Get string flags

Link to this section
KindeSDKAPI.auth.getStringFlag(code: "theme")
// pink

KindeSDKAPI.auth.getStringFlag(code: "theme", defaultValue: "black")
// pink

KindeSDKAPI.auth.getStringFlag(code: "cta_color")
// notFound

KindeSDKAPI.auth.getStringFlag(code: "cta_color", defaultValue: "blue")
// blue

KindeSDKAPI.auth.getStringFlag(code: "is_dark_mode")
// incorrectType("Flag \"is_dark_mode\" is type boolean - requested type string")

Get integer flags

Link to this section
KindeSDKAPI.auth.getIntegerFlag(code: "user_limit")
// 5

KindeSDKAPI.auth.getIntegerFlag(code: "user_limit", defaultValue: 3)
// 5

KindeSDKAPI.auth.getIntegerFlag(code: "team_count")
// notFound

KindeSDKAPI.auth.getIntegerFlag(code: "team_count", defaultValue: 4)
// 4

KindeSDKAPI.auth.getIntegerFlag(code: "is_dark_mode")
// incorrectType("Flag \"is_dark_mode\" is type boolean - requested type interger")

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

SDK API Reference

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

The unique ID of your application in Kinde.

Type: string

Required: Yes

postLogoutRedirectUri

Link to this section

Where your user will be redirected when they sign out.

Type: string

Required: No

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

KindeSDK methods

Link to this section

Constructs redirect url and sends user to Kinde to sign in.

Arguments:

orgCode?: String

Usage:

KindeSDKAPI.auth.login(); or KindeSDKAPI.auth.login(orgCode: “your organization code”) //

Allow orgCode to be provided if a specific org is signed in to.

Constructs redirect url and sends user to Kinde to sign up.

Arguments:

orgCode?: String

Usage:

KindeSDKAPI.auth.register(); or KindeSDKAPI.auth.register(orgCode: “your organization code”) //

Allow orgCode to be provided if a specific org is registered to.

enablePrivateAuthSession

Link to this section

Call this method before login/register process to enable or disable message prompt.

Usage:

KindeSDKAPI.auth.enablePrivateAuthSession(true);

Logs the user out of Kinde.

Usage:

KindeSDKAPI.auth.logout();

Returns the raw token from URL after logged from Kinde.

Usage:

KindeSDKAPI.auth.getToken()

Sample output:

eyJhbGciOiJSUzI...

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

Arguments:

orgName: String

Usage:

KindeSDKAPI.auth.createOrg(orgName: "Your Organization");

Gets a claim from an access or ID token.

Arguments:

key: String, token: TokenType = .accessToken

Usage:

KindeSDKAPI.auth.getClaim(key: ”given_name”, token: .idToken);

Sample output: "David"

Returns the state of a given permission.

Arguments:

name: String

Usage:

KindeSDKAPI.auth.getPermission(name: ”read:todos”);

Sample output:

Permissions
(organization: Organization(code: "org_1234"), isGranted: true)

getPermissions

Link to this section

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

Usage:

KindeSDKAPI.auth.getPermissions();

Sample output:

Permissions
	(organization: Organization(code: "org_1234"),
	permissions: ["create:todos", "update:todos", "read:todos"])

getOrganization

Link to this section

Get details for the organization your user is signed into.

Usage:

KindeSDKAPI.auth.getOrganization();

Sample output:

Organization(code: "org_1234")

getUserOrganizations

Link to this section

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

Usage:

KindeSDKAPI.auth.getUserOrganizations();

Sample output:

UserOrganizations
	(orgCodes: [Organization(code: "org_1234"), Organization(code: "org_abcd")])

getUserDetails

Link to this section

Get details for the organization your user is signed into.

Usage:

KindeSDKAPI.auth.getOrganization();

Sample output:

User
	(id: 1233,
	email: "dave@smith.com",
	given_name: "Dave",
	family_name: "Smith")

isAuthenticated

Link to this section

Return the boolean to demonstrate whether the user is authenticated or not.

Usage:

KindeSDKAPI.auth.isAuthenticated()

Sample output: true or false

Get a flag from the feature_flags claim of the access_token

Arguments:

code: String
defaultValue?: Any
flagType?: Flag.ValueType

Usage:

KindeSDKAPI.auth.getFlag(code: "theme")

Sample output:

Flag(code: "theme", type: Optional(KindeSDK.Flag.ValueType.string), value: black, isDefault: false)

getBooleanFlag

Link to this section

Get a boolean flag from the feature_flags claim of the access token

Arguments:

code: String
defaultValue?: String

Usage:

KindeSDKAPI.auth.getBooleanFlag(code: "is_dark_mode")

Sample output: true

Get a string flag from the feature_flags claim of the access token

Arguments:

code: String
defaultValue?: String

Usage:

KindeSDKAPI.auth.getStringFlag(code: "theme")

Sample output: pink

getIntegerFlag

Link to this section

Get a integer flag from the feature_flags claim of the access token

Arguments:

code: String
defaultValue?: String

Usage:

KindeSDKAPI.auth.getIntegerFlag(code: "user_limit")

Sample output: 5

If you need help connecting to Kinde, 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