Skip to main content

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_REQUIRED is received after a restart, there are two valid approaches:

  • Stay Logged In (OfflineLoginEvent): if a valid offline or authorization-grant token is stored, post an OfflineLoginEvent to re-authenticate without requiring credentials. See Stay Logged In.
  • Fresh IDP login: perform a full login using getAuthorizationCode as described below.

There is no way to determine in advance which will succeed — LOGIN_REQUIRED only confirms the user is activated on the app client, not whether valid tokens are still present. The recommended approach is to attempt OfflineLoginEvent first; if it returns a status other than StatusType.OK, fall back to a fresh IDP login using the same AuthenticationMode.

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: provideCredentials is 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.

IdpSdkInteractionInterface — provideCredentials (Kotlin)
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:

Login Flow (Android/Kotlin)
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 userId passed to getAuthorizationCode during login should be the enrolled user's identifier, obtained from the StartResultEvent or RestartResultEvent user 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.