Skip to content

Commit

Permalink
Merge pull request #110 from stytchauth/feature/Error-Unification
Browse files Browse the repository at this point in the history
Feature/error unification
  • Loading branch information
jhaven-stytch authored Dec 18, 2023
2 parents a08ec8b + 9619553 commit f2a1f17
Show file tree
Hide file tree
Showing 59 changed files with 900 additions and 424 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
_loadingState.value = true
val result = StytchB2BClient.handle(uri = uri, sessionDurationMinutes = 60u)
_currentResponse.value = when (result) {
is DeeplinkHandledStatus.NotHandled -> result.reason
is DeeplinkHandledStatus.NotHandled -> result.reason.message
is DeeplinkHandledStatus.Handled -> {
// Hacking this in for organization discovery stuff
(result.response as? DeeplinkResponse.Discovery)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class SSOViewModel : ViewModel() {
intent?.data?.let {
val result = StytchClient.handle(it, 60U)
when (result) {
is DeeplinkHandledStatus.NotHandled -> result.reason
is DeeplinkHandledStatus.NotHandled -> result.reason.message
is DeeplinkHandledStatus.Handled -> result.response.result.toFriendlyDisplay()
// This only happens for password reset deeplinks
is DeeplinkHandledStatus.ManualHandlingRequired -> ""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.stytch.exampleapp.b2b

import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.errors.StytchAPIError
import java.util.regex.Pattern

private val EMAIL_ADDRESS_PATTERN = Pattern.compile(
Expand All @@ -25,5 +26,11 @@ fun isPhoneNumberValid(str: String): Boolean {

fun <T : Any> StytchResult<T>.toFriendlyDisplay() = when (this) {
is StytchResult.Success<*> -> this.toString()
is StytchResult.Error -> this.exception.reason?.toString() ?: "Unknown exception"
is StytchResult.Error -> {
var message = "Name: ${exception}\nDescription: ${exception.message}"
if (exception is StytchAPIError) {
message += "\nURL: ${(exception as StytchAPIError).url}"
}
message
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
viewModelScope.launch {
_loadingState.value = true
_currentResponse.value = when (val result = StytchClient.handle(uri = uri, sessionDurationMinutes = 60u)) {
is DeeplinkHandledStatus.NotHandled -> result.reason
is DeeplinkHandledStatus.NotHandled -> result.reason.message
is DeeplinkHandledStatus.Handled -> result.response.result.toFriendlyDisplay()
// This only happens for password reset deeplinks
is DeeplinkHandledStatus.ManualHandlingRequired ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class OAuthViewModel(application: Application) : AndroidViewModel(application) {
intent.data?.let {
val result = StytchClient.handle(it, 60U)
_currentResponse.value = when (result) {
is DeeplinkHandledStatus.NotHandled -> result.reason
is DeeplinkHandledStatus.NotHandled -> result.reason.message
is DeeplinkHandledStatus.Handled -> result.response.result.toFriendlyDisplay()
// This only happens for password reset deeplinks
is DeeplinkHandledStatus.ManualHandlingRequired -> ""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.stytch.exampleapp

import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.errors.StytchAPIError
import java.util.regex.Pattern

private val EMAIL_ADDRESS_PATTERN = Pattern.compile(
Expand All @@ -25,5 +26,11 @@ fun isPhoneNumberValid(str: String): Boolean {

fun <T : Any> StytchResult<T>.toFriendlyDisplay() = when (this) {
is StytchResult.Success<*> -> this.toString()
is StytchResult.Error -> this.exception.reason?.toString() ?: "Unknown exception"
is StytchResult.Error -> {
var message = "Name: ${exception}\nDescription: ${exception.message}"
if (exception is StytchAPIError) {
message += "\nURL: ${(exception as StytchAPIError).url}"
}
message
}
}
2 changes: 1 addition & 1 deletion sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {

ext {
PUBLISH_GROUP_ID = 'com.stytch.sdk'
PUBLISH_VERSION = '0.17.0'
PUBLISH_VERSION = '0.18.0'
PUBLISH_ARTIFACT_ID = 'sdk'
}

Expand Down
23 changes: 12 additions & 11 deletions sdk/src/main/java/com/stytch/sdk/b2b/StytchB2BClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,19 @@ import com.stytch.sdk.common.DeeplinkHandledStatus
import com.stytch.sdk.common.DeeplinkResponse
import com.stytch.sdk.common.StorageHelper
import com.stytch.sdk.common.StytchDispatchers
import com.stytch.sdk.common.StytchExceptions
import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.dfp.ActivityProvider
import com.stytch.sdk.common.dfp.CaptchaProviderImpl
import com.stytch.sdk.common.dfp.DFP
import com.stytch.sdk.common.dfp.DFPImpl
import com.stytch.sdk.common.dfp.DFPProvider
import com.stytch.sdk.common.dfp.DFPProviderImpl
import com.stytch.sdk.common.errors.StytchDeeplinkMissingTokenError
import com.stytch.sdk.common.errors.StytchDeeplinkUnkownTokenTypeError
import com.stytch.sdk.common.errors.StytchInternalError
import com.stytch.sdk.common.errors.StytchSDKNotConfiguredError
import com.stytch.sdk.common.extensions.getDeviceInfo
import com.stytch.sdk.common.network.StytchErrorType
import com.stytch.sdk.common.network.models.BootstrapData
import com.stytch.sdk.common.stytchError
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -72,7 +73,7 @@ public object StytchB2BClient {
* @param context The applicationContext of your app
* @param publicToken Available via the Stytch dashboard in the API keys section
* @param callback An optional callback that is triggered after configuration and initialization has completed
* @throws StytchExceptions.Critical - if we failed to generate new encryption keys
* @throws StytchInternalError - if we failed to initialize for any reason
*/
public fun configure(context: Context, publicToken: String, callback: ((Boolean) -> Unit) = {}) {
try {
Expand Down Expand Up @@ -106,16 +107,16 @@ public object StytchB2BClient {
callback(_isInitialized.value)
}
} catch (ex: Exception) {
throw StytchExceptions.Critical(ex)
throw StytchInternalError(
message = "Failed to initialize the SDK",
exception = ex
)
}
}

@Suppress("MaxLineLength")
internal fun assertInitialized() {
if (!StytchB2BApi.isInitialized) {
stytchError(
"StytchB2BClient not configured. You must call 'StytchB2BClient.configure(...)' before using any functionality of the StytchB2BClient." // ktlint-disable max-line-length
)
throw StytchSDKNotConfiguredError("StytchB2BClient")
}
}

Expand Down Expand Up @@ -272,7 +273,7 @@ public object StytchB2BClient {
return withContext(dispatchers.io) {
val token = uri.getQueryParameter(Constants.QUERY_TOKEN)
if (token.isNullOrEmpty()) {
return@withContext DeeplinkHandledStatus.NotHandled(StytchErrorType.DEEPLINK_MISSING_TOKEN.message)
return@withContext DeeplinkHandledStatus.NotHandled(StytchDeeplinkMissingTokenError)
}
when (val tokenType = B2BTokenType.fromString(uri.getQueryParameter(Constants.QUERY_TOKEN_TYPE))) {
B2BTokenType.MULTI_TENANT_MAGIC_LINKS -> {
Expand Down Expand Up @@ -309,7 +310,7 @@ public object StytchB2BClient {
)
}
else -> {
DeeplinkHandledStatus.NotHandled(StytchErrorType.DEEPLINK_UNKNOWN_TOKEN.message)
DeeplinkHandledStatus.NotHandled(StytchDeeplinkUnkownTokenTypeError)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import com.stytch.sdk.b2b.AuthResponse
import com.stytch.sdk.b2b.DiscoveryEMLAuthResponse
import com.stytch.sdk.b2b.extensions.launchSessionUpdater
import com.stytch.sdk.b2b.network.StytchB2BApi
import com.stytch.sdk.b2b.network.StytchB2BApi.MagicLinks.Discovery.send
import com.stytch.sdk.b2b.sessions.B2BSessionStorage
import com.stytch.sdk.common.BaseResponse
import com.stytch.sdk.common.StorageHelper
import com.stytch.sdk.common.StytchDispatchers
import com.stytch.sdk.common.StytchExceptions
import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.errors.StytchFailedToCreateCodeChallengeError
import com.stytch.sdk.common.errors.StytchMissingPKCEError
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -34,7 +34,7 @@ internal class B2BMagicLinksImpl internal constructor(
try {
codeVerifier = storageHelper.retrieveCodeVerifier()!!
} catch (ex: Exception) {
result = StytchResult.Error(StytchExceptions.Critical(ex))
result = StytchResult.Error(StytchMissingPKCEError(ex))
return@withContext
}

Expand Down Expand Up @@ -71,7 +71,7 @@ internal class B2BMagicLinksImpl internal constructor(
try {
codeVerifier = storageHelper.retrieveCodeVerifier()!!
} catch (ex: Exception) {
result = StytchResult.Error(StytchExceptions.Critical(ex))
result = StytchResult.Error(StytchMissingPKCEError(ex))
return@withContext
}
result = discoveryApi.authenticate(
Expand Down Expand Up @@ -104,7 +104,7 @@ internal class B2BMagicLinksImpl internal constructor(
val challengePair = storageHelper.generateHashedCodeChallenge()
challengeCode = challengePair.second
} catch (ex: Exception) {
result = StytchResult.Error(StytchExceptions.Critical(ex))
result = StytchResult.Error(StytchFailedToCreateCodeChallengeError(exception = ex))
return@withContext
}

Expand Down Expand Up @@ -144,7 +144,7 @@ internal class B2BMagicLinksImpl internal constructor(
val challengePair = storageHelper.generateHashedCodeChallenge()
challengeCode = challengePair.second
} catch (ex: Exception) {
result = StytchResult.Error(StytchExceptions.Critical(ex))
result = StytchResult.Error(StytchFailedToCreateCodeChallengeError(exception = ex))
return@withContext
}

Expand Down
9 changes: 2 additions & 7 deletions sdk/src/main/java/com/stytch/sdk/b2b/network/StytchB2BApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ import com.stytch.sdk.b2b.network.models.SsoJitProvisioning
import com.stytch.sdk.b2b.network.models.StrengthCheckResponseData
import com.stytch.sdk.common.Constants
import com.stytch.sdk.common.DeviceInfo
import com.stytch.sdk.common.StytchExceptions
import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.dfp.CaptchaProvider
import com.stytch.sdk.common.dfp.DFPProvider
import com.stytch.sdk.common.errors.StytchSDKNotConfiguredError
import com.stytch.sdk.common.network.ApiService
import com.stytch.sdk.common.network.StytchAuthHeaderInterceptor
import com.stytch.sdk.common.network.StytchDFPInterceptor
Expand All @@ -37,7 +37,6 @@ import com.stytch.sdk.common.network.models.BootstrapData
import com.stytch.sdk.common.network.models.CommonRequests
import com.stytch.sdk.common.network.models.DFPProtectedAuthMode
import com.stytch.sdk.common.network.safeApiCall
import java.lang.RuntimeException

internal object StytchB2BApi {
internal lateinit var publicToken: String
Expand All @@ -49,11 +48,7 @@ internal object StytchB2BApi {
@VisibleForTesting
internal val authHeaderInterceptor: StytchAuthHeaderInterceptor by lazy {
if (!isInitialized) {
throw StytchExceptions.Critical(
RuntimeException(
"StytchB2BApi not configured. You must call 'configure(...)' before using any functionality of the " // ktlint-disable max-line-length
)
)
throw StytchSDKNotConfiguredError("StytchB2BClient")
}
StytchAuthHeaderInterceptor(
deviceInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import com.stytch.sdk.b2b.sessions.B2BSessionStorage
import com.stytch.sdk.common.BaseResponse
import com.stytch.sdk.common.StorageHelper
import com.stytch.sdk.common.StytchDispatchers
import com.stytch.sdk.common.StytchExceptions
import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.errors.StytchFailedToCreateCodeChallengeError
import com.stytch.sdk.common.errors.StytchMissingPKCEError
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -52,7 +53,7 @@ internal class PasswordsImpl internal constructor(
val challengePair = storageHelper.generateHashedCodeChallenge()
challengeCode = challengePair.second
} catch (ex: Exception) {
result = StytchResult.Error(StytchExceptions.Critical(ex))
result = StytchResult.Error(StytchFailedToCreateCodeChallengeError(ex))
return@withContext
}
result = api.resetByEmailStart(
Expand Down Expand Up @@ -85,7 +86,7 @@ internal class PasswordsImpl internal constructor(
try {
codeVerifier = storageHelper.retrieveCodeVerifier()!!
} catch (ex: Exception) {
result = StytchResult.Error(StytchExceptions.Critical(ex))
result = StytchResult.Error(StytchMissingPKCEError(ex))
return@withContext
}
result = api.resetByEmail(
Expand Down
5 changes: 3 additions & 2 deletions sdk/src/main/java/com/stytch/sdk/b2b/sessions/B2BSessions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.stytch.sdk.b2b.sessions

import com.stytch.sdk.b2b.AuthResponse
import com.stytch.sdk.common.BaseResponse
import com.stytch.sdk.common.errors.StytchFailedToDecryptDataError

/**
* The B2BSessions interface provides methods for authenticating, updating, or revoking sessions, and properties to
Expand All @@ -10,12 +11,12 @@ import com.stytch.sdk.common.BaseResponse
public interface B2BSessions {

/**
* @throws StytchExceptions.Critical if failed to decrypt data
* @throws StytchFailedToDecryptDataError if failed to decrypt data
*/
public val sessionToken: String?

/**
* @throws StytchExceptions.Critical if failed to decrypt data
* @throws StytchFailedToDecryptDataError if failed to decrypt data
*/
public val sessionJwt: String?

Expand Down
13 changes: 7 additions & 6 deletions sdk/src/main/java/com/stytch/sdk/b2b/sessions/B2BSessionsImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import com.stytch.sdk.b2b.extensions.launchSessionUpdater
import com.stytch.sdk.b2b.network.StytchB2BApi
import com.stytch.sdk.common.BaseResponse
import com.stytch.sdk.common.StytchDispatchers
import com.stytch.sdk.common.StytchExceptions
import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.errors.StytchFailedToDecryptDataError
import com.stytch.sdk.common.errors.StytchInternalError
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -22,7 +23,7 @@ internal class B2BSessionsImpl internal constructor(
try {
return sessionStorage.sessionToken
} catch (ex: Exception) {
throw StytchExceptions.Critical(ex)
throw StytchFailedToDecryptDataError(ex)
}
}

Expand All @@ -31,7 +32,7 @@ internal class B2BSessionsImpl internal constructor(
try {
return sessionStorage.sessionJwt
} catch (ex: Exception) {
throw StytchExceptions.Critical(ex)
throw StytchFailedToDecryptDataError(ex)
}
}

Expand Down Expand Up @@ -69,7 +70,7 @@ internal class B2BSessionsImpl internal constructor(
sessionStorage.revoke()
}
} catch (ex: Exception) {
result = StytchResult.Error(StytchExceptions.Critical(ex))
result = StytchResult.Error(StytchInternalError(exception = ex))
}
return result
}
Expand All @@ -84,13 +85,13 @@ internal class B2BSessionsImpl internal constructor(
}

/**
* @throws StytchExceptions.Critical if failed to save data
* @throws StytchInternalError if failed to save data
*/
override fun updateSession(sessionToken: String?, sessionJwt: String?) {
try {
sessionStorage.updateSession(sessionToken, sessionJwt)
} catch (ex: Exception) {
throw StytchExceptions.Critical(ex)
throw StytchInternalError(ex)
}
}
}
4 changes: 2 additions & 2 deletions sdk/src/main/java/com/stytch/sdk/b2b/sso/SSOImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import com.stytch.sdk.b2b.sessions.B2BSessionStorage
import com.stytch.sdk.common.Constants
import com.stytch.sdk.common.StorageHelper
import com.stytch.sdk.common.StytchDispatchers
import com.stytch.sdk.common.StytchExceptions
import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.errors.StytchMissingPKCEError
import com.stytch.sdk.common.sso.SSOManagerActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -56,7 +56,7 @@ internal class SSOImpl(
try {
codeVerifier = storageHelper.retrieveCodeVerifier()!!
} catch (ex: Exception) {
result = StytchResult.Error(StytchExceptions.Critical(ex))
result = StytchResult.Error(StytchMissingPKCEError(ex))
return@withContext
}
result = api.authenticate(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.stytch.sdk.common

import com.stytch.sdk.common.errors.StytchDeeplinkError

/**
* A class representing the three states of a deeplink handled by StytchClient.handle() / StytchB2BClient.handle()
*/
Expand All @@ -17,9 +19,9 @@ public sealed interface DeeplinkHandledStatus {
* This could happen if you pass a non-Stytch deeplink intent to the StytchClient.handle() method, or are trying to
* use a Consumer/B2B token in the wrong SDK.
*
* @property reason A String explaining why the deeplink was not handled
* @property reason A StytchDeeplinkError explaining why the deeplink was not handled
*/
public data class NotHandled(val reason: String) : DeeplinkHandledStatus
public data class NotHandled(val reason: StytchDeeplinkError) : DeeplinkHandledStatus

/**
* Indicates that this was a supported Stytch deeplink, but there is something more your application needs to do
Expand Down
Loading

0 comments on commit f2a1f17

Please sign in to comment.