iOS SDK
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 sectionIf 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
.
Installation
Link to this sectionKindeSDK 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 sectionCallback URLs
Link to this sectionHere you want to put in the callback URLs for your iOS app:
- 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):
<your_url_scheme>://kinde_callback
- Allowed logout redirect URLs:
<your_url_scheme>://kinde_logoutcallback
- Allowed callback URLs (also known as redirect URIs):
- 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.
Environments
Link to this sectionIf 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 sectionEnvironment variables
Link to this sectionThe 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 domainclientId
- you can find this on the Application details pageredirectUri
(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 email profile"}
Replace the values in <angle brackets> 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 email profile"}
Integrate with your app
Link to this sectionBefore 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()) ... } ...}
Multiple environments
Link to this sectionYou may need to test or deploy your app across different environments, such as development, staging, or production. The Kinde iOS SDK now supports configuration for multiple environments, allowing you to easily switch between them. Follow these steps to set up and configure environments in your Kinde account and integrate them into your iOS app.
Note: Unless you are on a Kinde Plus or Scale plan, you can only create one additional non-production environment.
Setting up environments
Link to this sectionBefore you can configure multiple environments in your iOS app, you must first create these environments in your Kinde dashboard. After setting up your environments, create an application under each environment. You will get different App Details (such as issuer, clientId, redirectUri, etc.) for each environment you create. These details will be used to configure your iOS app for the specific environment.
- On the Kinde home page, click the Environment dropdown in the top-left corner and select All environments.
- Click Add environment and follow the prompts to create each new non-production environment (deployment, staging, etc).
- For each environment, create a new application under the respective environment.
- After creating the app, you’ll receive different App Details (such as
issuer
,clientId
,redirectUri
, etc.) for each environment.
Configuring multiple environments
Link to this sectionOnce the environments are set up in your Kinde dashboard, you can configure your iOS app to handle multiple environments by using a JSON file for each environment. Create a JSON file for each environment in your project. For example:
kinde-auth-production.json
kinde-auth-development.json
kinde-auth-staging.json
(if applicable)
Each file should contain the configuration details for that specific environment. Here’s an example configuration:
{ "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"}
To set up multiple environments in your iOS app, use the KindeSDKAPI.configure()
method and pass the environment configuration file name. Here’s how to do it:
Example: switch to the production environment
Link to this section// Use the 'production' environment configurationKindeSDKAPI.configure(fileName: "kinde-auth-production")
Example: switch to the development environment
Link to this section// Switch to the 'development' environmentKindeSDKAPI.configure(fileName: "kinde-auth-development")
To switch to a different environment, simply change the configuration file name in the KindeSDKAPI.configure()
method.
Custom Logger (Optional)
Link to this sectionYou can use your own Custom Logger as well by extending LoggerProtocol
.
CustomLogger.swift
...import os.logimport KindeSDK...struct CustomLogger: LoggerProtocol {..........
func log(_ message: String) { print("[INFO]: \(message)") }
func log(error: Error) { print("[ERROR]: \(error.localizedDescription)") }}
Login and register
Link to this sectionThe 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 sectionOnce 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 "" }}
Logout
Link to this sectionThis 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 sectionTo 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 sectionNavigate to the Users page within Kinde to see your newly registered user.
User Permissions
Link to this sectionOnce 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 ?? falseif isGranted { // show Create Todo button in UI}
Audience
Link to this sectionAn 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 sectionBy 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 sectionWe 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"
Organizations
Link to this sectionCreate an organization
Link to this sectionTo 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 sectionKinde 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.
Feature flags
Link to this sectionWe have provided a helper to return any features flag from the access token:
KindeSDKAPI.auth.getFlag(code: "theme")// returnsFlag(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")// returnsFlag(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 sectionKindeSDKAPI.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 sectionKindeSDKAPI.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 sectionKindeSDKAPI.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")
Token Storage
Link to this sectionOnce 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 sectionissuer
Link to this sectionEither your Kinde URL or your custom domain. e.g https://yourapp.kinde.com
.
Type: string
Required: Yes
redirectUri
Link to this sectionThe URL that the user will be returned to after authentication.
Type: string
Required: Yes
clientId
Link to this sectionThe unique ID of your application in Kinde.
Type: string
Required: Yes
postLogoutRedirectUri
Link to this sectionWhere your user will be redirected when they sign out.
Type: string
Required: No
scope
Link to this sectionThe scopes to be requested from Kinde.
Type: string
Required: No
Default: openid profile email offline
audience
Link to this sectionThe audience claim for the JWT.
Type: string
Required: No
KindeSDK methods
Link to this sectionlogin
Link to this sectionConstructs 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.
register
Link to this sectionConstructs 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 sectionCall this method before login/register process to enable or disable message prompt.
Usage:
KindeSDKAPI.auth.enablePrivateAuthSession(true);
logout
Link to this sectionLogs the user out of Kinde.
Usage:
KindeSDKAPI.auth.logout();
getToken
Link to this sectionReturns the raw token from URL after logged from Kinde.
Usage:
KindeSDKAPI.auth.getToken()
Sample output:
eyJhbGciOiJSUzI...
createOrg
Link to this sectionConstructs 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");
getClaim
Link to this sectionGets 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"
getPermission
Link to this sectionReturns 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 sectionReturns 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 sectionGet details for the organization your user is signed into.
Usage:
KindeSDKAPI.auth.getOrganization();
Sample output:
Organization(code: "org_1234")
getUserOrganizations
Link to this sectionGets 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 sectionGet 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 sectionReturn the boolean to demonstrate whether the user is authenticated or not.
Usage:
KindeSDKAPI.auth.isAuthenticated()
Sample output: true
or false
getFlag
Link to this sectionGet a flag from the feature_flags claim of the access_token
Arguments:
code: StringdefaultValue?: AnyflagType?: 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 sectionGet a boolean flag from the feature_flags
claim of the access token
Arguments:
code: StringdefaultValue?: String
Usage:
KindeSDKAPI.auth.getBooleanFlag(code: "is_dark_mode")
Sample output: true
getStringFlag
Link to this sectionGet a string flag from the feature_flags
claim of the access token
Arguments:
code: StringdefaultValue?: String
Usage:
KindeSDKAPI.auth.getStringFlag(code: "theme")
Sample output: pink
getIntegerFlag
Link to this sectionGet a integer flag from the feature_flags
claim of the access token
Arguments:
code: StringdefaultValue?: String
Usage:
KindeSDKAPI.auth.getIntegerFlag(code: "user_limit")
Sample output: 5
If you need help connecting to Kinde, contact us at support@kinde.com.