Login
When a StartResultEvent with LOGIN_REQUIRED as sdkState is received in response to a StartEvent, you should initiate the Login flow.
Note: When
LOGIN_REQUIREDis received after a restart, there are two valid approaches:
- Stay Logged In (
OfflineLoginEvent): if a valid offline or authorization-grant token is stored, post anOfflineLoginEventto re-authenticate without requiring credentials. See Stay Logged In.- Fresh IDP login: perform a full login using
getAuthorizationCodeas described below.There is no way to determine in advance which will succeed —
LOGIN_REQUIREDonly confirms the user is activated on the app client, not whether valid tokens are still present. The recommended approach is to attemptOfflineLoginEventfirst; if it returns a status other thanStatusType.OK, fall back to a fresh IDP login using the sameAuthenticationMode.
This involves calling IdpSdkNativeInterface.getAuthorizationCode and supplying the user's username and password via the IdpSdkInteractionInterface callback. Follow the three-step flow in the correct sequence to ensure a successful login.
IdpSdk Login Flow Diagram
The event flow diagram illustrates the sequence of events during the login process when using the IdpSdk directly.
Implementation Examples
Android/Kotlin
Implement IdpSdkInteractionInterface to supply credentials when the SDK requests them. The SDK calls provideCredentials with the required input fields via interactionData.inputFieldIds — iterate over them and return a HashMap with the filled values.
Warning:
provideCredentialsis called from a separate thread. Your implementation must be thread-safe; any access to shared state in this scope must be synchronized. It may also be called multiple times in multi-step flows. See provideCredentials Behaviour for details on field IDs, multi-step flows, and error propagation.
class LoginHandler {
companion object : IdpSdkInteractionInterface {
const val LOGIN_CLIENT = "KssIdpLogin"
// Called by the SDK from a background thread to collect user credentials.
// Must be thread-safe.
override fun provideCredentials(interactionData: IdpSdkInteractionData): IdpSdkProvideCredentialsResult {
val credentials = HashMap<String?, String?>()
for (fieldId in interactionData.inputFieldIds) {
when (fieldId) {
IdpSdkConstants.USERNAME -> credentials[fieldId] = "<username>"
IdpSdkConstants.PASSWORD -> credentials[fieldId] = "<password>"
}
}
// To cancel the flow and propagate an error, return:
// IdpSdkProvideCredentialsResult(IdpSdkError(subsystem, errorCode, errorDescription))
return IdpSdkProvideCredentialsResult(credentials)
}
}
}
The full three-step login flow:
fun login() {
// Step 1 — get AST client data from the MC-SDK
val getAstClientDataEvent = GetAstClientDataEvent(IdpSdkApp.TENANT_ID)
MasterController.getInstance()?.postEvent(getAstClientDataEvent)?.then { resultEvent ->
when (resultEvent) {
is GetAstClientDataResultEvent -> {
if (resultEvent.status != StatusType.OK) {
Log.e(TAG, "GetAstClientData failed: ${resultEvent.errorDescription}")
return@then
}
// Step 2 — get authorization code from the IdpSdk
val astClientDataInfo = AstClientDataInfo(
resultEvent.clientData,
resultEvent.astClientId,
resultEvent.codeChallenge,
resultEvent.codeChallengeMethod
)
// userId is the identifier of the enrolled user (from StartResultEvent)
val userId: String? = StartRestartHandler.getFirstUserId()
IdpSdkNativeInterface.getAuthorizationCode(
astClientDataInfo,
LOGIN_CLIENT,
this, // IdpSdkInteractionInterface — provideCredentials called here
userId,
traceParent // optional W3C-compliant trace parent, or null
)?.thenApply { idpSdkResult ->
if (idpSdkResult.hasError()) {
Log.e(TAG, "getAuthorizationCode failed: ${idpSdkResult.idpSdkError}")
return@thenApply
}
// Step 3 — pass the authorization code to the MC-SDK
val authorizationCode = idpSdkResult.authorizationCode
val setEvent = SetAuthorisationCodeEvent(
IdpSdkApp.TENANT_ID,
AuthenticationMode.BIOMETRIC,
authorizationCode,
LOGIN_CLIENT
)
MasterController.getInstance()?.postEvent(setEvent)?.then { setResult ->
when (setResult) {
is SetAuthorisationCodeResultEvent -> {
if (setResult.status == StatusType.OK) {
Log.i(TAG, "Login successful")
} else {
Log.e(TAG, "SetAuthorisationCode failed: ${setResult.errorDescription}")
}
}
else -> error("Unexpected result for SetAuthorisationCodeEvent")
}
}
}
}
else -> error("Unexpected result for GetAstClientDataEvent")
}
}
}
NOTE: The
userIdpassed togetAuthorizationCodeduring login should be the enrolled user's identifier, obtained from theStartResultEventorRestartResultEventuser list.
Request Parameters
The following credential fields are requested by the IdpSdk during login via provideCredentials:
IdpSdkConstants.USERNAME — the user's username
IdpSdkConstants.PASSWORD — the user's password
The clientId passed to getAuthorizationCode is provided by the KOBIL IDP services (e.g. KssIdpLogin).
Important Notes
For more information about MC-SDK event usage during IdpSdk flows, please refer to GetAstClientData and SetAuthorisationCode.
Response Handling
IdpSdkNativeInterface.getAuthorizationCode returns a CompletableFuture<IdpSdkResult>. Check idpSdkResult.hasError() before accessing idpSdkResult.authorizationCode.
In response to the SetAuthorisationCodeEvent, the MC-SDK sends a SetAuthorisationCodeResultEvent with the appropriate status.