diff --git a/library/src/main/java/com/wultra/android/mtokensdk/operation/PACUtils.kt b/library/src/main/java/com/wultra/android/mtokensdk/operation/PACUtils.kt new file mode 100644 index 0000000..abcc7c3 --- /dev/null +++ b/library/src/main/java/com/wultra/android/mtokensdk/operation/PACUtils.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2023 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + */ + +package com.wultra.android.mtokensdk.operation + +import android.net.Uri +import com.google.gson.annotations.SerializedName +import com.wultra.android.mtokensdk.common.Logger + +/** + * Utility class used for handling TOTP + */ +class PACUtils { + + /** Data which is return after parsing PAC code */ + data class PACData( + + /** Time-based one time password used for Proximity antifraud check */ + @SerializedName("oid") + val operationId: String, + + /** The ID of the operation associated with the PAC */ + val totp: String? + ) + + companion object { + + /** Method accepts deeplink URL and returns PAC data */ + fun parseDeeplink(uri: Uri): PACData? { + val components = Uri.parse(uri.toString()) + + components.getQueryParameter("oid")?.let { operationId -> + val totp = components.getQueryParameter("totp") + return PACData(operationId, totp) + } ?: run { + Logger.e("Failed to get operationId from query items keys: ${components.queryParameterNames}") + return null + } + } + + /** Method accepts scanned code as a String and returns PAC data */ + fun parseQRCode(code: String): PACData? { + val encodedURLString = Uri.encode(code) + val uri = Uri.parse(encodedURLString) + + return parseDeeplink(uri) + } + } +} diff --git a/library/src/main/java/com/wultra/android/mtokensdk/operation/TOTPUtils.kt b/library/src/main/java/com/wultra/android/mtokensdk/operation/TOTPUtils.kt deleted file mode 100644 index 4494730..0000000 --- a/library/src/main/java/com/wultra/android/mtokensdk/operation/TOTPUtils.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2023 Wultra s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions - * and limitations under the License. - */ - -package com.wultra.android.mtokensdk.operation - -import android.net.Uri -import android.util.Base64 -import com.google.gson.Gson -import com.google.gson.annotations.SerializedName -import com.wultra.android.mtokensdk.common.Logger - -/** - * Utility class used for handling TOTP - */ -class TOTPUtils { - - /** Data payload which is returned from JWT parser */ - data class OperationTOTPData( - - /** The ID of the operations associated with the TOTP */ - val totp: String, - - /** The actual Time-based one time password */ - @SerializedName("oid") - val operationId: String - ) - - companion object { - - /** Method accepts deeplink Uri and returns payload data or null */ - fun parseDeeplink(uri: Uri?): OperationTOTPData? { - val query = uri?.query ?: return null - val queryItems = query.split("&").associate { - val (key, value) = it.split("=") - key to value - } - - queryItems["code"]?.let { - parseJWT(it) - } ?: run { - Logger.e("Failed to parse deeplink. Key `code` not found") - } - - Logger.e("Failed to parse deeplink from $uri") - return null - } - - /** Method accepts scanned code as a String and returns payload data or null */ - fun parseQRCode(code: String): OperationTOTPData? { - return parseJWT(code) - } - - private fun parseJWT(code: String): OperationTOTPData? { - val jwtParts = code.split(".") - if (jwtParts.size > 1) { - // At this moment we don't care about header, we want only payload which is the second part of JWT - val jwtBase64String = jwtParts[1] - if (jwtBase64String.isNotEmpty()) { - val base64EncodedData = jwtBase64String.toByteArray(Charsets.UTF_8) - return try { - val dataPayload = Base64.decode(base64EncodedData, Base64.DEFAULT) - val json = String(dataPayload, Charsets.UTF_8) - Gson().fromJson(json, OperationTOTPData::class.java) - } catch (e: Exception) { - Logger.e("Failed to decode QR JWT from: $code") - Logger.e("With error: ${e.message}") - null - } - } else { - Logger.e("JWT Payload is empty, jwtParts contain: $jwtParts") - } - } - - Logger.e("Failed to decode QR JWT from: $jwtParts") - return null - } - } -}