Skip to content

Instantly share code, notes, and snippets.

@FeiChen-okta
Last active April 12, 2022 19:33
Show Gist options
  • Save FeiChen-okta/b636e957a173fdec14f54c27cf3e4e19 to your computer and use it in GitHub Desktop.
Save FeiChen-okta/b636e957a173fdec14f54c27cf3e4e19 to your computer and use it in GitHub Desktop.
oidc suspend coroutines
/**
* Oidc client using okta-oidc-android
* Instantiate this in the same activity for authenticate flow to prevent activity leaking
*
* @param activity
*/
class OktaOidcClientImpl(app: Application, private val activity: AppCompatActivity? = null) {
constructor(app: Application) : this(app, null)
private val config = OIDCConfig.Builder()
.withJsonFile(activity, R.raw.config)
.create()
private val oidcClient = Okta.WebAuthBuilder()
.withConfig(config)
.withContext(activity ?: app)
.setRequireHardwareBackedKeyStore(false)
.create()
suspend fun oidcAuthenticate(payload: Map<String, String>): Result<String> {
return suspendCancellableCoroutine { continuation ->
val callback: ResultCallback<AuthorizationStatus, AuthorizationException> =
object : ResultCallback<AuthorizationStatus, AuthorizationException> {
override fun onSuccess(result: AuthorizationStatus) {
oidcClient?.unregisterCallback()
if (result == AuthorizationStatus.AUTHORIZED) {
oidcClient?.sessionClient?.tokens?.accessToken?.run {
continuation.resume(Result.success(this))
} ?: continuation.resume(Result.failure(OidcError.NoSession))
} else continuation.resume(Result.failure(OidcError.Unauthorized(result.name)))
}
override fun onCancel() {
oidcClient?.unregisterCallback()
continuation.resume(Result.failure(OidcError.Canceled))
}
override fun onError(msg: String?, exception: AuthorizationException?) {
oidcClient?.unregisterCallback()
continuation.resume(Result.failure(OidcError.Error(msg, exception)))
}
}
continuation.invokeOnCancellation { continuation.resume(Result.failure(OidcError.Canceled)) }
activity?.run {
oidcClient?.run {
oidcClient.registerCallback(callback, activity)
val builder = AuthenticationPayload.Builder()
payload.forEach { builder.addParameter(it.key, it.value) }
oidcClient.signIn(activity, builder.build())
} ?: continuation.resume(Result.failure(OidcError.InvalidState))
} ?: continuation.resume(Result.failure(OidcError.InvalidState))
}
}
suspend fun authToken(): Result<String> = runCatching {
oidcClient?.sessionClient?.tokens?.accessToken?.run { Result.success(this) }
?: Result.failure(IllegalStateException("Invalid oidc configuration"))
}.getOrElse { Result.failure(it) }
suspend fun refreshAccessToken(): Result<Tokens> = suspendCancellableCoroutine { continuation ->
val callback = object : RequestCallback<Tokens, AuthorizationException> {
override fun onSuccess(tokens: Tokens) = continuation.resume(Result.success(tokens))
override fun onError(error: String?, exception: AuthorizationException?) {
continuation.resume(Result.failure(OidcError.Error(error, exception)))
}
}
continuation.invokeOnCancellation { continuation.resume(Result.failure(OidcError.Canceled)) }
oidcClient?.sessionClient?.run { refreshToken(callback) } ?: continuation.resume(Result.failure(OidcError.NoSession))
}
suspend fun revokeAccessToken(): Result<Boolean> = suspendCancellableCoroutine { continuation ->
val callback = object : RequestCallback<Boolean, AuthorizationException> {
override fun onSuccess(result: Boolean) = continuation.resume(Result.success(result))
override fun onError(error: String?, exception: AuthorizationException?) {
continuation.resume(Result.failure(exception ?: Exception(error)))
}
}
continuation.invokeOnCancellation { continuation.resume(Result.failure(OidcError.Canceled)) }
oidcClient?.sessionClient?.tokens?.run { oidcClient.sessionClient?.revokeToken(accessToken, callback) }
?: continuation.resume(Result.failure(OidcError.NoSession))
}
fun clearData() {
oidcClient?.sessionClient?.clear()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment