Skip to content

Commit

Permalink
Fix Kotlin AuthAPI
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-quiltt committed Feb 22, 2024
1 parent 893b82f commit b0b8999
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import androidx.activity.viewModels
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.ViewModelProvider
import app.quiltt.connector.AuthResponse
import app.quiltt.connector.PingResponse
import app.quiltt.connector.QuilttAuthApi
import app.quiltt.connector.QuilttConnector
import app.quiltt.connector.QuilttConnectorConnectConfiguration
Expand Down Expand Up @@ -65,7 +64,7 @@ class MainViewModel(private val token:String?) : ViewModel() {
viewModelScope.launch(Dispatchers.IO) {// run background task here
if (token != null) {
val result = QuilttAuthApi(clientId = null).ping(token = token)
if (result is AuthResponse.SessionResponse) {
if (result is PingResponse.SessionResponse) {
_isValidToken.value = true
_loading.value = false
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class QuilttConnectorActivity : ComponentActivity() {
val connectorId = AppConfig.addConnectorId
val oauthRedirectUrl = AppConfig.oauthRedirectUrl
val token = SharedPreferencesHelper(context = this).getData("token")
println("Connection ID: $connectionId")
if (connectionId != null) {
val config = QuilttConnectorReconnectConfiguration(
connectorId = connectorId,
Expand All @@ -37,7 +36,6 @@ class QuilttConnectorActivity : ComponentActivity() {
QuilttConnectContent(config = config, token = token!!)
}
}

}
}

Expand All @@ -49,22 +47,18 @@ fun QuilttConnectContent(config: QuilttConnectorConnectConfiguration, token: Str
val connectorWebView = quilttConnector.connect(
config = config,
onExitSuccess = { metadata ->
println("Exit success!")
println("Metadata: $metadata")
if (context is Activity) {
context.finish()
}
},
onExitAbort = { metadata ->
println("Exit abort!")
println("Metadata: $metadata")
// TODO: Handle abort
if (context is Activity) {
context.finish()
}
},
onExitError = { metadata ->
println("Exit error!")
println("Metadata: $metadata")
// TODO: Handle error
if (context is Activity) {
context.finish()
}
Expand All @@ -80,22 +74,18 @@ fun QuilttReconnectContent(config: QuilttConnectorReconnectConfiguration, token:
val connectorWebView = quilttConnector.reconnect(
config = config,
onExitSuccess = { metadata ->
println("Exit success!")
println("Metadata: $metadata")
if (context is Activity) {
context.finish()
}
},
onExitAbort = { metadata ->
println("Exit abort!")
println("Metadata: $metadata")
// TODO: Handle abort
if (context is Activity) {
context.finish()
}
},
onExitError = { metadata ->
println("Exit error!")
println("Metadata: $metadata")
// TODO: Handle error
if (context is Activity) {
context.finish()
}
Expand Down
146 changes: 91 additions & 55 deletions connector/src/main/java/app/quiltt/connector/QuilttAuthApi.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
package app.quiltt.connector

import org.json.JSONObject
import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL


data class EmailInput(val email: String)
data class PhoneInput(val phone: String)

sealed class UsernamePayload {
data class Email(val email: String) : UsernamePayload()
data class Phone(val phone: String) : UsernamePayload()
Expand All @@ -20,11 +13,29 @@ sealed class UsernamePayload {
data class PasscodePayload(val usernamePayload: UsernamePayload, val passcode: String)

data class SessionData(val token: String)
data class UnauthorizedData(val message: String, val instruction: String)
data class UnprocessableData(val attribute: Map<String, String>)

sealed class AuthResponse {
data class SessionResponse(val status: Int, val data: SessionData) : AuthResponse()
data class UnprocessableResponse(val status: Int, val data: UnprocessableData) : AuthResponse()
sealed class PingResponse {
data class SessionResponse(val status: Int, val data: SessionData) : PingResponse()
data class UnprocessableResponse(val status: Int, val data: UnprocessableData) : PingResponse()
}

sealed class IdentifyResponse {
data class SessionResponse(val status: Int, val data: SessionData) : IdentifyResponse()
data class AcceptedResponse(val status: Int) : IdentifyResponse()
data class UnprocessableResponse(val status: Int, val data: UnprocessableData) : IdentifyResponse()
}

sealed class AuthenticateResponse {
data class SessionResponse(val status: Int, val data: SessionData) : AuthenticateResponse()
data class UnauthorizedResponse(val status: Int, val data: UnauthorizedData) : AuthenticateResponse()
data class UnprocessableResponse(val status: Int, val data: UnprocessableData) : AuthenticateResponse()
}

sealed class RevokeResponse {
data class NoContentResponse(val status: Int) : RevokeResponse()
data class UnauthorizedResponse(val status: Int, val data: UnauthorizedData) : RevokeResponse()
}

class QuilttAuthApi(private val clientId: String?) {
Expand All @@ -35,7 +46,7 @@ class QuilttAuthApi(private val clientId: String?) {
* - 200: OK -> Session is Valid
* - 401: Unauthorized -> Session is Invalid
*/
fun ping(token: String): AuthResponse {
fun ping(token: String): PingResponse {
val url = URL(endpointAuth)
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "GET"
Expand All @@ -45,53 +56,53 @@ class QuilttAuthApi(private val clientId: String?) {
val statusCode = connection.responseCode

if (statusCode == 200) {
return AuthResponse.SessionResponse(statusCode, SessionData(token))
return PingResponse.SessionResponse(statusCode, SessionData(token))
}
val inputStream = connection.errorStream
val errorData = errorMap(inputStream)
return AuthResponse.UnprocessableResponse(statusCode, errorData)
return PingResponse.UnprocessableResponse(statusCode, errorData)
}

private fun errorMap(inputStream: InputStream): UnprocessableData {
println("errorMap inputStream: $inputStream")
val jsonObject = JSONObject(inputStream.bufferedReader().use { it.readText() })
println("errorMap jsonObject: $jsonObject")
val errorMap = mutableMapOf<String, String>()
val keys = jsonObject.keys()
while (keys.hasNext()) {
val key = keys.next()
val value = jsonObject.getString(key)
errorMap[key] = value
}
return UnprocessableData(errorMap)
}

fun identify(payload: UsernamePayload): SessionData {
/**
* Response Statuses:
* - 201: Created -> Profile Created, New Session Returned
* - 202: Accepted -> Profile Found, MFA Code Sent for `authenticate`
* - 422: Unprocessable Entity -> Invalid Payload
*/
fun identify(payload: UsernamePayload): IdentifyResponse {
val url = URL(endpointAuth)
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.setRequestProperty("Content-Type", "application/json; utf-8")
connection.doInput = true
connection.doOutput = true

val jsonPayload = JSONObject()
jsonPayload.put("clientId", clientId)
jsonPayload.put("payload", payload)

val writer = OutputStreamWriter(connection.outputStream)
writer.write(jsonPayload.toString())
writer.flush()
writer.close()

val reader = BufferedReader(InputStreamReader(connection.inputStream))
val response = reader.readLine()
reader.close()
when (payload) {
is UsernamePayload.Email -> jsonPayload.put("email", payload.email)
is UsernamePayload.Phone -> jsonPayload.put("phone", payload.phone)
}

val jsonObject = JSONObject(response)
return SessionData(jsonObject.getString("token"))
if (connection.responseCode == 201) {
val inputStream = connection.inputStream
val jsonObject = JSONObject(inputStream.bufferedReader().use { it.readText() })
return IdentifyResponse.SessionResponse(connection.responseCode, SessionData(jsonObject.getString("token")))
}
if (connection.responseCode == 202) {
return IdentifyResponse.AcceptedResponse(connection.responseCode)
}
val inputStream = connection.errorStream
val errorData = errorMap(inputStream)
return IdentifyResponse.UnprocessableResponse(connection.responseCode, errorData)
}

fun authenticate(payload: PasscodePayload): SessionData {
/**
* Response Statuses:
* - 201: Created -> MFA Validated, New Session Returned
* - 401: Unauthorized -> MFA Invalid
* - 422: Unprocessable Entity -> Invalid Payload
*/
fun authenticate(payload: PasscodePayload): AuthenticateResponse {
val url = URL(endpointAuth)
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "PUT"
Expand All @@ -102,25 +113,50 @@ class QuilttAuthApi(private val clientId: String?) {
jsonPayload.put("clientId", clientId)
jsonPayload.put("payload", payload)

val writer = OutputStreamWriter(connection.outputStream)
writer.write(jsonPayload.toString())
writer.flush()
writer.close()

val reader = BufferedReader(InputStreamReader(connection.inputStream))
val response = reader.readLine()
reader.close()

val jsonObject = JSONObject(response)
return SessionData(jsonObject.getString("token"))
if (connection.responseCode == 201) {
val inputStream = connection.inputStream
val jsonObject = JSONObject(inputStream.bufferedReader().use { it.readText() })
return AuthenticateResponse.SessionResponse(connection.responseCode, SessionData(jsonObject.getString("token")))
}
if (connection.responseCode == 401) {
val inputStream = connection.errorStream
val errorData = errorMap(inputStream) as UnauthorizedData
return AuthenticateResponse.UnauthorizedResponse(connection.responseCode, errorData)
}
val inputStream = connection.errorStream
val errorData = errorMap(inputStream)
return AuthenticateResponse.UnprocessableResponse(connection.responseCode, errorData)
}

fun revoke(token: String) {
/**
* Response Statuses:
* - 204: No Content -> Session Revoked
* - 401: Unauthorized -> Session Not Found
*/
fun revoke(token: String): RevokeResponse {
val url = URL(endpointAuth)
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "DELETE"
connection.setRequestProperty("Authorization", "Bearer $token")
connection.responseCode
println("Revoke response code: ${connection.responseCode}")

if (connection.responseCode == 204) {
return RevokeResponse.NoContentResponse(connection.responseCode)
}
val inputStream = connection.errorStream
val errorData = errorMap(inputStream) as UnauthorizedData
return RevokeResponse.UnauthorizedResponse(connection.responseCode, errorData)
}

private fun errorMap(inputStream: InputStream): UnprocessableData {
val jsonObject = JSONObject(inputStream.bufferedReader().use { it.readText() })
val errorMap = mutableMapOf<String, String>()
val keys = jsonObject.keys()
while (keys.hasNext()) {
val key = keys.next()
val value = jsonObject.getString(key)
errorMap[key] = value
}
return UnprocessableData(errorMap)
}
}

0 comments on commit b0b8999

Please sign in to comment.