The Kinde Android SDK allows developers to quickly and securely integrate a new or existing application into the Kinde platform.
You can also view the docs in the GitHub repo.
If you don’t already have a Kinde account, register for free here (no credit card required).
You will also need a Kinde domain to get started, e.g. yourapp.kinde.com
KindeSDK is available through Maven. To install it, simply add the following line to your build.gradle
:
...
implementation 'au.kinde.sdk:<version>'
...
You should also include Retrofit and the GSON converter as dependencies:
...
implementation "com.squareup.retrofit2:retrofit:<version>"
implementation "com.squareup.retrofit2:converter-gson:<version>"
...
- 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}://<your_kinde_host>//kinde_callback - for example myapp://myhost.kinde.com//kinde_callback
- Allowed logout redirect URLs:{your_url_scheme}://<your_kinde_host>//kinde_ logoutcallback
- for examplemyapp://myhost.kinde.com//kinde_ logoutcallback
- Select Save.
If you would like to use our Environments feature as part of your development process, you will need to create them within your Kinde account. In this case you would use the Environment subdomain in the code block above.
The SDK reads configuration from meta-data
, so you should add meta-data
to the <application>
section of your AndroidManifest.xml
.
You can find these variables in Kinde. Go to Settings > Applications > [Your app] > View details. Then scroll to the App keys section.
au.kinde.domain:
- your Kinde domainau.kinde.clientId
...
<application ...>
...
<meta-data
android:name="au.kinde.domain"
android:value="your_kinde_url" />
<meta-data
android:name="au.kinde.clientId"
android:value="your_kinde_client_id" />
...
</application>
...
Configuration example:
...
<application ...>
...
<meta-data
android:name="au.kinde.domain"
android:value="app.kinde.com" />\
<meta-data
android:name="au.kinde.clientId"
android:value="test@live" />
...
</application>
...
You’ll need to import the SDK package in your Android app.
...
import android.os.Handler
import android.util.Log
...
class YourActivity : AppCompatActivity() {
...
private lateinit var sdk: KindeSDK
...
override fun onCreate(savedInstanceState: Bundle?) {
...
sdk = KindeSDK(this, object : KindeSDK.SDKListener {
override fun onNewToken(token: String) {
// Need to implement
}
override fun onLogout() {
// Need to implement
}
override fun onException(exception: Exception) {
Handler(Looper.getMainLooper()).post {
Log.e("MyActivity", "Something wrong init KindeSDK: " + exception.message)
}
}
})
...
}
The Kinde client provides methods for a simple login / register flow. Add buttons in your view as follows:
...
findViewById<View>(R.id.b_sign_in).setOnClickListener {
sdk.login(GrantType.PKCE)
}
findViewById<View>(R.id.b_sign_up).setOnClickListener {
sdk.register(GrantType.PKCE)
}
...
Once your user is redirected back to your site from Kinde, it means you’ve logged in successfully. You will need to implement the onNewToken
function from the SDK.
...
sdk = KindeSDK(this, object : KindeSDK.SDKListener {
override fun onNewToken(token: String) {
// Need to implement\
}
...
}
This is implemented in much the same way as logging in or registering. The Kinde SPA client comes with a logout method.
findViewById<View>(R.id.b_sign_out).setOnClickListener {
sdk.logout()
}yar
To access the user information, call the getUser
method.
sdk.getUser()?.let {
Handler(Looper.getMainLooper()).post {
Log.i("MyActivity", it.firstName + " " + it.lastName)
}
}
In Kinde, go to Users to view all users and subscribers.
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.
Set roles and permissions at the Business level in Kinde. Here’s an example of permissions.
String[] 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:
sdk.getPermission("create:todos")
// {orgCode: "org_b235c067b7e4", isGranted: true}
sdk.getPermissions()
// {orgCode: "org_b235c067b7e4", permissions: [ "create:users", "view:users" ]}
A practical example in code might look something like:
if(sdk.getPermission("create:todos").isGranted) {
// create new a todo
}
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 to be added to the provided token.
The audience of a token is the intended recipient of the token.
<meta-data
android:name="au.kinde.audience"
android:value="example@example" />
For details on how to connect, see Register an API.
By default the KindeSDK
requests the following scopes:
profile
email
offline
openid
You can override this by passing scope into the KindeSDK
sdk = KindeSDK(
...
scopes: listOf("openid", "offline", "email", "profile")
...
)
We have provided a helper to grab any claim from your id or access tokens. The helper defaults to access tokens:
sdk.getClaim("aud")
// ["api.yourapp.com"]
sdk.getClaim("given_name", TokenType.ID_TOKEN)
// "David"
To have a new organization created within your application, you will need to run a similar function to below:
...
findViewById<View>(R.id.create_org).setOnClickListener {
sdk.createOrg(orgName = "Your Organization")
}
...
Kinde has a unique code for every organization. You’ll have to pass this code through when you register a new user or sign into a particular organization. Example function below:
findViewById<View>(R.id.b_sign_in).setOnClickListener {
sdk.login(GrantType.PKCE, orgCode = "your_org_code")
}
findViewById<View>(R.id.b_sign_up).setOnClickListener {
sdk.register(GrantType.PKCE, 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:
sdk.getOrganization()
// {'orgCode': 'org_1234'}
sdk.getUserOrganizations()
// {'orgCodes': ['org_1234', 'org_abcd']}
Once the user has successfully authenticated, you’ll have a JWT and possibly a refresh token that should be stored securely.
Property | Type | Is required | Default | Description |
---|---|---|---|---|
activity | AppCompatActivity | Yes | Activity of the application | |
loginRedirect | String | Yes | The url that the user will be returned to after authentication | |
logoutRedirect | String | Yes | Where your user will be redirected upon logout | |
scopes | List<String> | No | listOf("openid", "offline", "email", "profile") | List of scopes to override the default ones |
SDKListener | SDKListener { fun onNewToken(token: String) fun onLogout() fun onException(exception: Exception)} | Yes | The listener that receives callbacks from the SDK |
Property | Description | Arguments | Usage | Sample output |
---|---|---|---|---|
login | Starts the authorization flow | grantType: GrantType?, orgCode: String?``// GrantType { PKCE, NONE``} | sdk.login(GrantType.PKCE) orsdk.login(GrantType.PKCE, orgCode = "your_org_code") | |
register | Starts the registration flow | grantType: GrantType?, orgCode: String? | sdk.register(GrantType.PKCE) orsdk.register(GrantType.PKCE, orgCode = "your_org_code") | |
createOrg | Starts the registration flow and creates a new organization for your business | grantType: GrantType?, orgCode: String? | sdk.createOrg(orgName = "Your Organization") orsdk.register(GrantType.PKCE, orgName = "Your Organization") | |
logout | Logs the user out of Kinde | sdk.logout() | ||
isAuthenticated | Checks that access token is present | sdk.isAuthenticated() | true or false | |
getUserDetails | Gets a claim from an access or id token | sdk.getUserDetails() | {givenName: "Dave"; id: "abcdef"; familyName: "Smith"; email: dave@smith.com"} | |
getClaim | Gets a claim from an access or id token | claim: String, tokenType: TokenType``// TokenType { ID_TOKEN, ACCESS_TOKEN``} | sdk.getClaim('given_name', TokenType.ID_TOKEN); | "David" |
getPermissions | Returns all permissions for the current user for the organization they are logged into | permission: String | sdk.getPermissions() | {orgCode: "org_1234", permissions: ["create:todos", "update:todos", "read:todos""create:todos","update:todos","read:todos"]} |
getPermission | Returns the state of a given permission | sdk.getPermission("read:todos") | {orgCode: "org_1234", isGranted: true} | |
getUserOrganizations | Gets an array of all organizations the user has access to | sdk.getUserOrganizations() | {orgCodes: ["org_1234", "org_5678""org1_234","org_5678"]} | |
getOrganization | Get details for the organization your user is logged into | sdk.getOrganization() | {orgCode: "org_1234"} |
If you need any assistance with getting Kinde connected, reach out to us at support@kinde.com.
Developer tools