Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mds/okta 524282/okta 528021 update mobile sdk overview #4332

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified ...okta/vuepress-site/.vuepress/public/img/mobile-sdk/mobile-idx-basic-objects.png
100644 → 100755
mauricesharp-okta marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
mauricesharp-okta marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
```kotlin
import com.okta.authfoundation.client.OidcClientResult
import com.okta.authfoundationbootstrap.CredentialBootstrap
import com.okta.idx.kotlin.dto.IdxRemediation.Type.ISSUE
import com.okta.idx.kotlin.dto.IdxResponse
import com.okta.idx.kotlin.client.IdxClientResult

private suspend fun handleResponse(response: IdxResponse) {
// Check if the sign-in flow is successful.
if (response.isLoginSuccessful) {
// Exchange the sign-in session token for a connection token.
when (val exchangeCodesResult = client?.exchangeInteractionCodeForTokens(response.remediations[ISSUE]!!)) {
is IdxClientResult.Error -> {
// Exchange the sign-in session token for a token.
when (val exchangeCodesResult = flow?.exchangeInteractionCodeForTokens(response.remediations[ISSUE]!!)) {
mauricesharp-okta marked this conversation as resolved.
Show resolved Hide resolved
is OidcClientResult.Error -> {
// Handle the error.
}
is IdxClientResult.Success -> {
is OidcClientResult.Success -> {
// Handle a successful sign-in flow.
// The token is in `exchangeCodesResult.result`.
// Store it securely for future use.
CredentialBootstrap.defaultCredential().storeToken(exchangeCodesResult.result)
}
else -> {
// Handle the error.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@

Initialize AuthFoundationBootstrap in your `Application` sublcass. This code shows loading the values from a property file in your project.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this. It's repeated further down.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I slightly modified things as there are two things in this para. The first sentence is definitely a repeat. The second is trying to say that the code example is using a property file. I changed the first para to be one sentence:

The following example loads the configuration values for the flow from a property file in your project.

It's in the next commit.


First, create a property file, for example, `okta.properties` in the project root. Add the values for your Okta application integration to the file.
mauricesharp-okta marked this conversation as resolved.
Show resolved Hide resolved

```
mauricesharp-okta marked this conversation as resolved.
Show resolved Hide resolved
discoveryUrl=https://{yourIssuerUrl}/oauth2/default/.well-known/openid-configuration
clientId={yourClientId}
redirectUri=com.okta.sample.android:/login
```

Add this configuration to your `app/build.gradle` to make the properties available in the build configuration:

```gradle
def oktaProperties = new Properties()
rootProject.file("okta.properties").withInputStream { oktaProperties.load(it) }

defaultConfig {
...

buildConfigField "String", 'DISCOVERY_URL', "\"${oktaProperties.getProperty('discoveryUrl')}\""
buildConfigField "String", 'CLIENT_ID', "\"${oktaProperties.getProperty('clientId')}\""
buildConfigField "String", 'REDIRECT_URI', "\"${oktaProperties.getProperty('redirectUri')}\""

...
}
```

In your `Applicaiton` subclass, initialize `AuthFoundationBootstrap` from the `BuildConfig` by calling `initializeAuthFoundation` from `onCreate`.
rajdeepnanua-okta marked this conversation as resolved.
Show resolved Hide resolved

```kotlin
import com.okta.android.samples.authenticator.BuildConfig
import com.okta.authfoundation.AuthFoundationDefaults
import com.okta.authfoundation.client.OidcClient
import com.okta.authfoundation.client.OidcConfiguration
import com.okta.authfoundation.client.SharedPreferencesCache
import com.okta.authfoundation.credential.CredentialDataSource.Companion.createCredentialDataSource
import com.okta.authfoundationbootstrap.CredentialBootstrap
import okhttp3.HttpUrl.Companion.toHttpUrl

fun initializeAuthFoundation() {
// Initializes Auth Foundation and Credential Bootstrap classes.
AuthFoundationDefaults.cache = SharedPreferencesCache.create(this)
val oidcConfiguration = OidcConfiguration(
clientId = BuildConfig.CLIENT_ID,
defaultScope = "openid email profile offline_access",
)
val client = OidcClient.createFromDiscoveryUrl(
oidcConfiguration,
BuildConfig.DISCOVERY_URL.toHttpUrl(),
)
CredentialBootstrap.initialize(client.createCredentialDataSource(this))
}
```
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@

This example creates the client in the viewmodel `launch` coroutine. Start by calling `IdxClient.start()` and on success, request the first response by calling `resume()` on the result.
This example creates the flow in the viewmodel `launch` coroutine. Start by calling `CredentialBootstrap.oidcClient.createInteractionCodeFlow` and on success, request the first response by calling `resume()` on the result.
mauricesharp-okta marked this conversation as resolved.
Show resolved Hide resolved

```kotlin
@Volatile
private var client: IdxClient? = null
import com.okta.authfoundation.client.OidcClientResult
import com.okta.authfoundationbootstrap.CredentialBootstrap
import com.okta.idx.kotlin.client.InteractionCodeFlow
import com.okta.idx.kotlin.client.InteractionCodeFlow.Companion.createInteractionCodeFlow
import com.okta.idx.kotlin.dto.IdxResponse

@Volatile
private var flow: InteractionCodeFlow? = null

private fun createClient() {
viewModelScope.launch {
// Initialize the SDK client and start sign-in flow.
when (val clientResult = IdxClient.start(OktaIdxClientConfigurationProvider.get())) {
is IdxClientResult.Error -> {
// Initialize the SDK and start sign-in flow.
when (
val clientResult = CredentialBootstrap.oidcClient.createInteractionCodeFlow(
redirectUrl = BuildConfig.REDIRECT_URI,
)
) {
is OidcClientResult.Error -> {
// Handle the error.
}
is IdxClientResult.Success -> {
client = clientResult.result
is OidcClientResult.Success -> {
flow = clientResult.result
// Request the first response by calling resume and handle the asynchronous response.
when (val resumeResult = clientResult.result.resume()) {
is IdxClientResult.Error -> {
is OidcClientResult.Error -> {
// Handle the error.
}
is IdxClientResult.Success -> handleResponse(resumeResult.result)
is OidcClientResult.Success -> handleResponse(resumeResult.result)
}
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class="full">

!["A diagram that shows the main objects associated with each step in the sign-in flow."](/img/mobile-sdk/mobile-idx-objects-and-flow-kotlin.png)

</div>

The main objects associated with each step in the flow are:

| Sign-in step | Objects |
| :--------------------------------- |:---------------------------------|
| Initialize SDK | InteractionCodeFlow |
| Request initial step | InteractionCodeFlow |
| Receive step | IdxResponse |
| Check completion, cancel, or error | IdxResponse <br/> IdxRemediation |
| Gather user input | IdxRemediation <br/> Capability |
| Send input | InteractionCodeFlow |
| Done | IdxResponse |
mauricesharp-okta marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
The following code shows these steps in order:

```kotlin
import com.okta.authfoundation.client.OidcClientResult
import com.okta.idx.kotlin.dto.IdxResponse

private suspend fun handleResponse(response: IdxResponse) {
// Check if the sign-in flow is successful.
if (response.isLoginSuccessful) {
Expand Down Expand Up @@ -36,11 +39,11 @@ private suspend fun handleResponse(response: IdxResponse) {
*/
private fun proceed(remediation: IdxRemediation) {
viewModelScope.launch {
when (val resumeResult = client?.proceed(remediation)) {
is IdxClientResult.Error -> {
when (val resumeResult = flow?.proceed(remediation)) {
is OidcClientResult.Error -> {
// Handle the error.
}
is IdxClientResult.Success -> {
is OidcClientResult.Success -> {
handleResponse(resumeResult.result)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
This code assumes storing the access token after a successful sign-in flow:

```kotlin
import com.okta.authfoundation.client.OidcClientResult
import com.okta.authfoundation.credential.RevokeTokenType
import com.okta.authfoundationbootstrap.CredentialBootstrap

fun logout() {
viewModelScope.launch(Dispatchers.IO) {
try {
// First load a refresh token if one exists.
val refreshToken = Storage.tokens.refreshToken
if (refreshToken != null) {
// Revoking the refresh token also revokes the access token.
revokeToken("refresh_token", refreshToken)
} else {
revokeToken("access_token", Storage.tokens.accessToken)
viewModelScope.launch {
// Revoking the refresh token also revokes the access token.
when (val revokeResult = CredentialBootstrap.defaultCredential().revokeToken(RevokeTokenType.REFRESH_TOKEN)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to split kotlin code over two lines? This one runs out of the browser window.

Suggested change
when (val revokeResult = CredentialBootstrap.defaultCredential().revokeToken(RevokeTokenType.REFRESH_TOKEN)) {
when (val revokeResult = CredentialBootstrap.defaultCredential()
.revokeToken(RevokeTokenType.REFRESH_TOKEN)) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did the best I could... in the next commit.

is OidcClientResult.Error -> {
// Sign-out failed, handle the error.
}
is OidcClientResult.Success -> {
// Sign-out successful. Redirect to a sign-in view.
}

// Sign-out successful. Redirect to a sign-in view.
} catch (e: Exception) {
// Sign-out failed, handle the error.
}
}
}


private fun revokeToken(tokenType: String, token: String) {
// Create an API request to revoke the token.
val formBody = FormBody.Builder()
.add("client_id", BuildConfig.CLIENT_ID)
.add("token_type_hint", tokenType)
.add("token", token)
.build()

val request = Request.Builder()
.url("${BuildConfig.ISSUER}/v1/revoke")
.post(formBody)
.build()

// Send the request to revoke the token.
val response = OktaIdxClientConfigurationProvider.get().okHttpCallFactory.newCall(request).execute()

println("Revoke Token Response: $response")
}
```
Loading