UI SDK Configuration and Authentication

UI SDK

UI SDK Configuration

In order to initialise the UI SDK on either iOS or Android, a working environment must be provided. The available environments are Sandbox and Production. The Sandbox is used for development and testing purposes. Bookings in the sandbox are not paid for and trip progress is simulated using Karhoo Bot fleets. A custom environment can also be provided.

To configure the UI SDK you will need to provide an implementation of our KarhooUISDKConfiguration interface (e.g. KarhooConfig: KarhooUISDKConfiguration). This lets our UI SDK grab certain dependencies and configuration settings.

// register configuration (AppDelegate) before any usage of the SDK
KarhooUI.set(configuration: KarhooConfig())
// The payment provider's view needs to be instantiated. It can be AdyenPaymentView or BraintreePaymentView depending on the PSP choice
// Adyen integration
val paymentManager = AdyenPaymentManager()
paymentManager.paymentProviderView = AdyenPaymentView()

// Braintree integration
val paymentManager = BraintreePaymentManager()
paymentManager.paymentProviderView = BraintreePaymentView()
  
// Later down the line
val config = KarhooConfig(applicationContext)
config.paymentManager = paymentManager

KarhooUISDK.setConfiguration(config)

The KarhooUISDKConfiguration interface also needs an implementation within your project.

struct KarhooConfig: KarhooUISDKConfiguration {
  	static var onUpdateAuthentication: (@escaping () -> Void) -> Void = { $0() }
  
  	var paymentManager: PaymentManager {
        AdyenPaymentManager()
        // OR
        BraintreePaymentManager()
    }
  
	  /// Return `true` if you want `Karhoo UISDK` to use `Add to calendar` feature, allowing users to add scheduled ride to the device main calendar. This feature required host app’s `info.plist` to have `Privacy - Calendars Usage Description` marked as used. If you do not want to modify your `info.plist`, or you don’t want user to see this feature, return `false`.
  	var useAddToCalendarFeature: Bool { 
      true
    }
  
    // logo used on side menu and trip completion pop up
    func logo() -> UIImage {
        return UIImage("")!
    }

    func environment() -> KarhooEnvironment {
        return .sandbox
    }
  
    func authenticationMethod() -> AuthenticationMethod {
        return .karhooUser
    }
  
    func requireSDKAuthentication(callback: @escaping () -> Void) {
        KarhooConfig.onUpdateAuthentication {
            callback()
        }
    }
}

// With this configuration the UISDK can be initialised in your App/SceneDelegate. 
// This will also ensure the network layer (KarhooSDK) is initialised.
import KarhooUISDK

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        KarhooUI.set(configuration: YourCompanyKarhooConfiguration())
       ..
        return true
    }
}

//Where you are setting up the KarhooConfig
KarhooConfig.onUpdateAuthentication = { callback in
//Refresh your own access token in order to ensure a proper validity period for the Karhoo token
//Then use that token to refresh the credentials inside the SDK
	let authService = Karhoo.getAuthService()
   authService.login(token: yourTtoken).execute { result in
		if result.isSuccess() {
			callback()
		} else {
			// Show error
		}
	}
}
class KarhooConfig(val context: Context, private val authMethod: AuthenticationMethod = AuthenticationMethod.KarhooUser()) :
        KarhooUISDKConfiguration {
    override lateinit var paymentManager: PaymentManager
    var sdkAuthenticationRequired: ((callback: () -> Unit) -> Unit)? = null
      
    override fun logo(): Drawable? {
        return context.getDrawable(R.drawable.karhoo_wordmark)
    }

    override fun environment(): KarhooEnvironment {
        KarhooEnvironment.Sandbox()
    }

    override fun simulatePaymentProvider(): Boolean = false

    override fun context(): Context {
        return context
    }

    override fun authenticationMethod(): AuthenticationMethod {
        return authMethod
    }

    override fun analyticsProvider(): AnalyticProvider? {
        return GoogleAnalyticsProvider()
    }

    override suspend fun requireSDKAuthentication(callback: () -> Unit) {
        sdkAuthenticationRequired?.invoke(callback)
    }
}

//Application file
val config = SDKConfig(context = this.applicationContext)
config.sdkAuthenticationRequired = {
	loginInBackground(it, yourToken)
}
KarhooApi.setConfiguration(configuration = config)
  
private var deferredRequests: MutableList<(()-> Unit)> = arrayListOf()
private fun loginInBackground(callback: () -> Unit, token: String) {
	if (!requestedAuthentication) {
		Log.e(TAG, "Need an external authentication")
		requestedAuthentication = true
		deferredRequests.add(callback)

		GlobalScope.launch {
//Refresh your own access token in order to ensure a proper validity period for the Karhoo token
//Then use that token to refresh the credentials inside the SDK 
      KarhooApi.authService.login(token).execute { result ->
        when (result) {
        	is Resource.Success -> {
						Log.e(TAG, "We got a new token from the back-end")
						deferredRequests.map {
							it.invoke()
						}
						deferredRequests.clear()
						requestedAuthentication = false
					}
					is Resource.Failure -> toastErrorMessage(result.error)
 				}
			}
		}
  } else {
    deferredRequests.add(callback)
  }
}

🚧

iOS privacy permission

If you'd like to make a user able to add a scheduled ride to the device calendar, please set useAddToCalendarFeature value to true and add the proper item to host app info.plist.

Authentication

The UI SDK currently supports 3 different types of authentication methods, each can be used for different use cases depending on the integration model:

  • Username/password: The users created and managed in the Karhoo platform
  • Token Exchange or Third-party authentication: The users are created and managed in your own identity system
  • Guest authentication: No sign-in required

The AuthenticationMethod is set as part of the SDK configuration, therefore it is important to configure the SDK for the right configuration mechanism first

Username/password

SDK Configuration

final class KarhooConfig: KarhooUISDKConfiguration {

    static var auth: AuthenticationMethod = .karhooUser

    func environment() -> KarhooEnvironment {
        return .sandbox
    }

    func authenticationMethod() -> AuthenticationMethod {
        return .karhooUser
    }
}
class KarhooConfig(val context: Context) :
        KarhooUISDKConfiguration {

    override fun authenticationMethod(): AuthenticationMethod {
        return AuthenticationMethod.KarhooUser()
    }
}

User Authentication

The KarhooApi.userService provides all the required functionality for common user management tasks such as registering a new user, logging in an existing user, updating the user details and resetting their password.

Since we have specified AuthenticationMethod.KarhooUser as the authentication method, you need to use the KarhooApi.userService.loginUser API to authenticate the user:

val userService = KarhooApi.userService
val userLogin = UserLogin(email = "[email protected]", password = "password")

userService.loginUser(userLogin: userLogin).execute { result in
     switch result {
        case .success(let user):
            print("User: \(user)")
        case .failure(let error):
            print("error: \(error.code) \(error.message)")
    }
}
val loginRequest = UserLogin(email = "[email protected]", password = "password"
KarhooApi.userService.loginUser(loginRequest).execute { result ->
   when (result) {
      is Resource.Success -> {
         val userInfo: UserInfo = result.data
         Log.d("Welcome ${userInfo?.firstName}")
         // Proceed to next step
      }
      is Resource.Failure -> {
         if (result.error == KarhooError.UserAlreadyLoggedIn) {
            Log.d("Welcome back")
            // Proceed to next step
         } else {
            Log.d((result.error)
         }
      }
   }
}

Token Exchange

You will first need to integrate your external authentication system with the Karhoo platform before initialising the SDK with your client id. When using this authentication method, the authentication service must be used to login and revoke access before interacting with other Karhoo services.

SDK Configuration

struct KarhooConfig: KarhooSDKConfigurationProvider {
 
	func authenticationMethod() -> AuthenticationMethod {
  	let settings = TokenExchangeSettings(clientId: "", scope: "")
    	return .tokenExchange(settings: settings)
   }
}
class KarhooConfig : KarhooSDKConfiguration {
  
    override fun authenticationMethod(): AuthenticationMethod {
    		return AuthenticationMethod.TokenExhange(clientId = "",
        			                                   scope = "") 
		}
}

User Authentication

The KarhooApi.authService provides all the required functionality for common user management tasks such as registering a new user, logging in an existing user, updating the user details and resetting their password.

Since we have specified AuthenticationMethod.TokenExhange as the authentication method, the KarhooApi.userService.loginUser API needs to be used to authenticate the user:

let authService = Karhoo.getAuthService()

authService.login(token: String).execute(callback: { result in
     switch result {
        case .success(let user):
            print("User: \(user)")
        case .failure(let error):
            print("error: \(error.code) \(error.message)")
    	}
   })
}
val authService = KarhooApi.authService

authService.login(token = "123csXXs").execute { result ->
    when (result) {
        is Resource.Success -> Log.d(result.data) // Handle data
        is Resource.Failure -> Log.d(result.error.internalMessage) //Handle errors
    }
}

Guest authentication

The user does not require any credentials in order to access the Karhoo platform in the guest authentication method. You need to configure the SDK with valid parameters.

struct KarhooConfig: KarhooSDKConfigurationProvider {
  func authenticationMethod() -> AuthenticationMethod {
  	let guestSettings = GuestSettings(identifier: "",
                                    referer: "",
                                    organisationId: "")
    	return .guest(settings: guestSettings)
   }
}
class KarhooConfig : KarhooSDKConfiguration {

	override fun authenticationMethod(): AuthenticationMethod {
    	return AuthenticationMethod.Guest(identifier = "client_identifier", referer = "referer", organisationId = "organisation_id")
  }
}

📘

Important changes from UISDK Android v1.7.4 / iOS 1.9.4

The UISDK also provides the requireSDKAuthentication method in the KarhooUISDKConfiguration interface to notify whenever an external authentication is required. This happens only when all attempts to refresh the access token needed for authenticating requests have failed.
This method receives a callback parameter which should be invoked when the external authentication has finalized. An example of how the requireSDKAuthentication flow should be handled is found above.

🚧

iOS

Inside KarhooSDKConfiguration.requireSDKAuthentication closure, you should not log out the user. You need to provide new authentication credentials to Karhoo SDK without deleting the old one.

Analytics

Our UI SDK includes an event-model that allows a subset of user interactions to be provided for you to be used in your Analytics framework. The specific Analytics strategy is for you to choose but the scope of events that can be captured is constrained by the events generated by the Karhoo UI SDK.

For a full list of supported events, please follow these links to the Android and iOS analytics interface declarations on GitHub.

You need to implement the above protocol and then configure the UI SDK to use it.

struct KarhooConfig: KarhooUISDKConfiguration {
    func analytics() -> Analytics {
        KarhooAnalytics()
    }
}
KarhooUISDK.analytics = KarhooAnalytics()

Legal notices

The UI SDK allows for explicit consumer acceptance of the terms and conditions prior to booking. You can enable explicit user consent by adding a checkbox to terms and conditions as part of the UI SDK configuration.

struct KarhooConfig: KarhooUISDKConfiguration {
    var isExplicitTermsAndConditionsConsentRequired: Bool { true }
}
class KarhooConfig : KarhooSDKConfiguration {
  
    override fun isExplicitTermsAndConditionsConsentRequired(): Boolean {
        return true
    }
}

You can also enable the display of ‘legal notices’ which can be used to summarise key elements of the longer terms and conditions of use documents that are linked into the user experience. The legal notice is not presented by default and the relevant strings are empty. If you want to display a custom legal notice to the users, you need to override and provide the following strings.
In the case of iOS, the strings need to be provided in Localizable.strings

📘

The supported links are valid URLs or email addresses with mailto: prefix as provided in the example below

Also, for the feature to have the best behaviour, don't forget to include a placeholder in the kh_uisdk_legal_notice_text value just like in the example below.

"kh_uisdk_legal_notice_label" = "Legal notice";
"kh_uisdk_legal_notice_link" = "mailto:[email protected]";
"kh_uisdk_legal_notice_text" = "The data collected is electronically processed by Karhoo sending email to %1$@. Your data may need to be translated";
"kh_uisdk_legal_notice_title" = "Flit Technologies";
<string name="kh_uisdk_legal_notice_label">Legal notice</string>
<string name="kh_uisdk_legal_notice_title">Karhoo</string>
<string name="kh_uisdk_legal_notice_link">mailto:[email protected]</string>
<string name="kh_uisdk_legal_notice_text">The data collected is electronically processed by Karhoo sending email to %s. Your data may need to be translated</string>
  • kh_uisdk_legal_notice_label: Override this key to set the title of the component
  • kh_uisdk_legal_notice_text: The main text with legal information
  • kh_uisdk_legal_notice_link: What shall be opened when the user clicks on the text
  • kh_uisdk_legal_notice_title: What is the word (or sequence of words) to be marked as a hyperlink