Step 2. Configure the sample app
In this section you open the sample project in Android Studio, and view the integration points in the TODO pane.
You’ll visit each integration point in the sample app to understand how to complete a DaVinci flow, including handling the different nodes and their collectors, obtaining an access token and user information, and finally signing out of the session.
-
In Android Studio, click Open, navigate to the
sdk-sample-apps/android/kotlin-davinci
folder that has the downloaded sample source code in, and then click Open.Android Studio opens and loads the DaVinci tutorial project.
-
In the Project pane, navigate to samples > app.
-
On the View menu, select Tool Windows, and then click TODO.
The sample code is annotated with
TODO
comments to help you locate the integration points, where code changes are required.Figure 1. Integration points annotated withTODO
comments -
In the TODO pane, double-click the STEP 1 line.
Android Studio opens
DaVinciViewModel.kt
:DaVinciViewModel.kt
//TODO: Integration Point. STEP 1 val daVinci = DaVinci { logger = Logger.STANDARD // Oidc as module module(Oidc) { clientId = "<Client ID>" discoveryEndpoint = "<Discovery Endpoint>" scopes = mutableSetOf("<scope1>", "<scope2>", "…") redirectUri = "<Redirect URI>" } }
This snippet initializes the
DaVinci
module, and leverages the OpenID Connect (OIDC) module to configure the settings to connect to your PingOne instance.-
Replace <Client ID> with the ID of the client you are connecting to in PingOne.
Example:
clientId = "6c7eb89a-66e9-ab12-cd34-eeaf795650b2"
Refer to Get configuration values from PingOne for instructions of where to find this value.
-
Replace <Discovery Endpoint> with the OIDC Discovery Endpoint value from the client you are connecting to in PingOne.
Example:
discoveryEndpoint = "https://auth.pingone.ca/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration"
Refer to Get configuration values from PingOne for instructions of where to find this value.
-
In the
scopes
property, add the scopes you want to assign users who complete authentication using the client.Example:
scopes = mutableSetOf("openid", "email", "profile")
-
Replace <Redirect URI> with the application ID of your sample app, followed by
://oauth2redirect
.Example:
redirectUri = "org.forgerock.demo://oauth2redirect"
The
redirectUri
value you use must exactly match one of the Redirect URIs value you enter in the native OAuth 2.0 application you created earlier. -
Optionally, delete the
TODO
comment to remove it from the list.
The result resembles the following:
DaVinciViewModel.kt
val daVinci = DaVinci { logger = Logger.STANDARD // Oidc as module module(Oidc) { clientId = "6c7eb89a-66e9-ab12-cd34-eeaf795650b2" discoveryEndpoint = "https://auth.pingone.ca/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration" scopes = mutableSetOf("openid", "email", "profile") redirectUri = "org.forgerock.demo://oauth2redirect" } }
-
-
In the TODO pane, double-click the STEP 2 line.
Android Studio opens
DaVinciViewModel.kt
:DaVinciViewModel.kt
//TODO: Integration Point. STEP 2 // Start the DaVinci flow, next node from the flow will be returned // Update the state with the next node /* val next = daVinci.start() state.update { it.copy(prev = next, node = next) } */
This snippet calls
start()
to start the DaVinci flow, and assigns the returned node to the variablenext
.It also updates the app’s state to store the response as both
prev
andnode
.-
Uncomment the highlighted text.
-
Optionally, delete the
TODO
comment to remove it from the list.
-
-
In the TODO pane, double-click the STEP 3 line.
Android Studio opens
DaVinciViewModel.kt
:DaVinciViewModel.kt
//TODO: Integration Point. STEP 3 // Continue the DaVinci flow, next node from the flow will be returned // Update the state with the next node /* val next = current.next() state.update { it.copy(prev = current, node = next) } */
This snippet calls
next()
to continue the DaVinci flow, by proceeding to the next available node. It assigns the newly returned node to the variablenext
.It also updates the app’s state to store the new response as
node
, and the current node asprev
.-
Uncomment the highlighted text.
-
Optionally, delete the
TODO
comment to remove it from the list.
-
-
In the TODO pane, double-click the STEP 4 line.
Android Studio opens
DaVinci.kt
:DaVinci.kt
//TODO: Integration Point. STEP 4 // Render the current node depending on its type /* when (val node = state.node) { is ContinueNode → { Render(node = node, onNodeUpdated, onStart) { onNext(node) } } is FailureNode → { Log.e("DaVinci", node.cause.message, node.cause) Render(node = node) } is ErrorNode → { Render(node) // Render the previous node if (state.prev is ContinueNode) { Render(node = state.prev, onNodeUpdated, onStart) { onNext(state.prev) } } } is SuccessNode → { LaunchedEffect(true) { onSuccess?.let { onSuccess() } } } else → {} } */
This snippet watches for a change in state, and takes an action based on the ode type returned by DaVinci.
For example, if it is a
FailureNode
log an error message. If the node is aContinueNode
that continues the flow, call the render function to display the necessary fields on the screen.-
Uncomment the highlighted text.
-
Optionally, delete the
TODO
comment to remove it from the list.
-
-
In the TODO pane, double-click the STEP 5 line.
Android Studio opens
ContinueNode.kt
:ContinueNode.kt
//TODO: Integration Point. STEP 5 // Intermediate step in the Davinci Flow. The ContinueNode is a node that is used to // continue the flow. It can have multiple collectors that are used to collect user input. // Render the UI for each collector that are part of the ContinueNode. /* continueNode.collectors.forEach { when (it) { is FlowCollector → { hasAction = true FlowButton(it, onNext) } is PasswordCollector → { Password(it, onNodeUpdated) } is SubmitCollector → { hasAction = true SubmitButton(it, onNext) } is TextCollector → Text(it, onNodeUpdated) } } */
This snippet handles the various collectors that are returned by the current node.
Loop through all of the collectors in the node and render an appropriate field in your app.
For example, this snippet handles text and password fields, and two types of button, a submit and a flow type.
-
Uncomment the highlighted text.
-
Optionally, delete the
TODO
comment to remove it from the list.
-
-
In the TODO pane, double-click the STEP 6 line.
Android Studio opens
TokenViewModel.kt
:TokenViewModel.kt
//TODO: Integration Point. STEP 6 // Retrieve the access token /* User.user()?.let { when (val result = it.token()) { is Failure → { state.update { it.copy(token = null, error = result.value) } } is Success → { state.update { it.copy(token = result.value, error = null) } } } } ?: run { state.update { it.copy(token = null, error = null) } } */
This snippet gets called when the flow reaches a
SuccessNode
and gets an access token on behalf of the authenticated user.-
Uncomment the highlighted text.
-
Optionally, delete the
TODO
comment to remove it from the list.
-
-
In the TODO pane, double-click the STEP 7 line.
Android Studio opens
UserProfileViewModel.kt
:UserProfileViewModel.kt
//TODO: Integration Point. STEP 7 // Retrieve the user info /* User.user()?.let { user → when (val result = user.userinfo(false)) { is Result.Failure → state.update { s → s.copy(user = null, error = result.value) } is Result.Success → state.update { s → s.copy(user = result.value, error = null) } } } */
This snippet gets the user’s info from DaVinci, for example their preferred name for display within the sample app.
-
Uncomment the highlighted text.
-
Optionally, delete the
TODO
comment to remove it from the list.
-
-
In the TODO pane, double-click the STEP 8 line.
Android Studio opens
LogoutViewModel.kt
:LogoutViewModel.kt
//TODO: Integration Point. STEP 8 // logout the user /* User.user()?.logout() */
This snippet calls the
logout()
function on theUser
object to sign the user out of the app, and end their session in DaVinci.-
Uncomment the highlighted text.
-
Optionally, delete the
TODO
comment to remove it from the list.
-