diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 133f148..ab50c5c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -55,6 +55,22 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/MainActivity.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/MainActivity.kt index 7559e8a..1d4b27a 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/MainActivity.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/MainActivity.kt @@ -1,6 +1,7 @@ package com.ownd_project.tw2023_wallet_android import android.app.Activity +import android.content.Intent import android.graphics.drawable.ColorDrawable import android.net.Uri import android.os.Bundle @@ -78,20 +79,28 @@ class MainActivity : AppCompatActivity() { } } } - // URIからパラメータを抽出 data?.let { - // ここでパラメータを処理 - val parameterValue = it.getQueryParameter("credential_offer") // クエリパラメータの取得 - - // credential_offerがある場合発行画面に遷移する - if (!parameterValue.isNullOrEmpty()) { - val bundle = Bundle().apply { - putString("parameterValue", parameterValue) + Log.d("MainActivity", "uri: $it") + when (data.scheme) { + "openid4vp" -> { + handleVp(it) + } + "openid-credential-offer" -> { + handleOffer(it, navController) + } + "https" -> { + // App link + if (it.getQueryParameter("credential_offer").isNullOrEmpty()){ + handleVp(it) + }else{ + handleOffer(it, navController) + } + } + else -> { + Log.d("MainActivity", "unknown scheme: ${data.scheme}") } - navController.navigate(R.id.action_to_confirmation, bundle) } - } } @@ -103,6 +112,27 @@ class MainActivity : AppCompatActivity() { } } + private fun handleOffer(uri: Uri, navController: androidx.navigation.NavController){ + // ここでパラメータを処理 + val parameterValue = uri.getQueryParameter("credential_offer") // クエリパラメータの取得 + + // credential_offerがある場合発行画面に遷移する + if (!parameterValue.isNullOrEmpty()) { + val bundle = Bundle().apply { + putString("parameterValue", parameterValue) + } + navController.navigate(R.id.action_to_confirmation, bundle) + } + } + + private fun handleVp(uri: Uri) { + val newIntent = Intent(this, TokenSharingActivity::class.java).apply { + putExtra("siopRequest", uri.toString()) + putExtra("index", -1) + } + startActivity(newIntent) + } + private var shouldLock = false private var isLocking = false diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/IdTokenSharingActivity.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/TokenSharingActivity.kt similarity index 95% rename from app/src/main/java/com/ownd_project/tw2023_wallet_android/IdTokenSharingActivity.kt rename to app/src/main/java/com/ownd_project/tw2023_wallet_android/TokenSharingActivity.kt index 53244cd..e0f7095 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/IdTokenSharingActivity.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/TokenSharingActivity.kt @@ -5,7 +5,7 @@ import androidx.appcompat.app.ActionBar import androidx.appcompat.app.AppCompatActivity import androidx.navigation.fragment.NavHostFragment -class IdTokenSharingActivity : AppCompatActivity() { +class TokenSharingActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/oid/OpenIdProvider.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/oid/OpenIdProvider.kt index e5b3804..2e1c849 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/oid/OpenIdProvider.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/oid/OpenIdProvider.kt @@ -21,6 +21,7 @@ import okhttp3.Request import org.bouncycastle.jce.spec.ECNamedCurveSpec import java.math.BigInteger import java.net.URI +import java.net.URLEncoder import java.security.KeyPair import java.security.PublicKey import java.security.interfaces.ECPublicKey @@ -241,7 +242,11 @@ class OpenIdProvider(val uri: String, val option: ProviderOption = ProviderOptio val redirectUrl = requireNotNull(authRequest.redirectUri) println("send id token to $redirectUrl") - val result = sendRequest(redirectUrl, mapOf("id_token" to idToken)) + + // As a temporary value, give DIRECT_POST a fixed value. + // It needs to be modified when responding to redirect responses. + val result = sendRequest(redirectUrl, mapOf("id_token" to idToken), ResponseMode.DIRECT_POST) + println("Received result: $result") return Either.Right(result) } catch (e: Exception) { @@ -259,6 +264,14 @@ class OpenIdProvider(val uri: String, val option: ProviderOption = ProviderOptio ) val presentationDefinition = this.siopRequest.presentationDefinition ?: throw IllegalArgumentException(SIOPErrors.BAD_PARAMS.message) + + // https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes + // the default Response Mode for the OAuth 2.0 code Response Type is the query encoding + // the default Response Mode for the OAuth 2.0 token Response Type is the fragment encoding + // https://openid.net/specs/openid-4-verifiable-presentations-1_0-ID2.html#section-5 + // If the parameter is not present, the default value is fragment. + val responseMode = authRequest.responseMode ?: ResponseMode.FRAGMENT + // presentationDefinition.inputDescriptors を使って選択項目でフィルター val vpTokens = credentials.mapNotNull { it -> when (it.format) { @@ -309,8 +322,17 @@ class OpenIdProvider(val uri: String, val option: ProviderOption = ProviderOptio } val jsonString = objectMapper.writeValueAsString(presentationSubmission) - // todo fragmentの場合はSame Deviceにリダイレクト - val redirectUrl = requireNotNull(authRequest.responseUri) + // https://openid.net/specs/openid-4-verifiable-presentations-1_0-ID2.html#name-authorization-request + // response_uri parameter is present, the redirect_uri Authorization Request parameter MUST NOT be present + val destinationUri = if (responseMode == ResponseMode.DIRECT_POST) { + authRequest.responseUri + } else { + authRequest.redirectUri + } + if (destinationUri.isNullOrBlank()) { + return Either.Left("Unknown destination for response") + } + val body = mutableMapOf( "vp_token" to vpTokenValue, @@ -321,8 +343,8 @@ class OpenIdProvider(val uri: String, val option: ProviderOption = ProviderOptio body["state"] = state } - println("send vp token to $redirectUrl") - val result = sendRequest(redirectUrl, body) + println("send vp token to $destinationUri") + val result = sendRequest(destinationUri, body, responseMode) print("status code: ${result.statusCode}") print("location: ${result.location}") print("cookies: ${result.cookies}") @@ -506,7 +528,7 @@ fun mergeOAuth2AndOpenIdInRequestPayload( return createRequestObjectPayloadFromMap(mergedMap) } -fun sendRequest(redirectUrl: String, formData: Map): PostResult { +fun sendRequest(destinationUri: String, formData: Map, responseMode: ResponseMode): PostResult { val client = OkHttpClient.Builder() .followRedirects(false) .build() @@ -516,11 +538,19 @@ fun sendRequest(redirectUrl: String, formData: Map): PostResult formBodyBuilder.add(key, value) } val formBody = formBodyBuilder.build() - - val request = Request.Builder() - .url(redirectUrl) - .post(formBody) - .build() + val request: Request + + when (responseMode) { + ResponseMode.DIRECT_POST -> { + request = Request.Builder() + .url(destinationUri) + .post(formBody) + .build() + } + else -> { + throw IllegalArgumentException("Unsupported response mode: $responseMode") + } + } client.newCall(request).execute().use { response -> val statusCode = response.code() @@ -534,7 +564,7 @@ fun sendRequest(redirectUrl: String, formData: Map): PostResult val uri = URI.create(location) if (!uri.isAbsolute) { // 元のURLからホスト情報を抽出して補完 - val originalUri = URI.create(redirectUrl) + val originalUri = URI.create(destinationUri) val portPart = if (originalUri.port != -1) ":${originalUri.port}" else "" location = "${originalUri.scheme}://${originalUri.host}$portPart$location" } diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/test/TestFragmentActivity.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/test/TestFragmentActivity.kt index 66ecb66..78c7f94 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/test/TestFragmentActivity.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/test/TestFragmentActivity.kt @@ -15,7 +15,7 @@ import com.ownd_project.tw2023_wallet_android.datastore.CredentialDataStore import com.ownd_project.tw2023_wallet_android.test.DummyData.generateSdJwt import com.ownd_project.tw2023_wallet_android.ui.credential_detail.CredentialDetailFragment import com.ownd_project.tw2023_wallet_android.ui.shared.Constants -import com.ownd_project.tw2023_wallet_android.ui.siop.IdTokenSharringFragment +import com.ownd_project.tw2023_wallet_android.ui.siop_vp.TokenSharingFragment import com.ownd_project.tw2023_wallet_android.utils.KeyPairUtil import com.ownd_project.tw2023_wallet_android.utils.ZipUtil import kotlinx.coroutines.launch @@ -83,7 +83,7 @@ class TestFragmentActivity : AppCompatActivity() { } // setContentView(R.layout.activity_test_fragment); // val fragment = IdTokenSharringFragment() - val fragment = IdTokenSharringFragment().apply { + val fragment = TokenSharingFragment().apply { arguments = args } val fragment3 = CredentialDetailFragment().apply { diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/backup/BackupViewModel.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/backup/BackupViewModel.kt index c4db2cf..8ce052e 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/backup/BackupViewModel.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/backup/BackupViewModel.kt @@ -16,7 +16,7 @@ import com.ownd_project.tw2023_wallet_android.datastore.CredentialSharingHistory import com.ownd_project.tw2023_wallet_android.datastore.IdTokenSharingHistoryStore import com.ownd_project.tw2023_wallet_android.datastore.PreferencesDataStore import com.ownd_project.tw2023_wallet_android.pairwise.HDKeyRing -import com.ownd_project.tw2023_wallet_android.ui.siop.TAG +import com.ownd_project.tw2023_wallet_android.ui.siop_vp.TAG import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/reader/ReaderFragment.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/reader/ReaderFragment.kt index 109c9d4..8df71ec 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/reader/ReaderFragment.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/reader/ReaderFragment.kt @@ -1,6 +1,6 @@ package com.ownd_project.tw2023_wallet_android.ui.reader -import com.ownd_project.tw2023_wallet_android.IdTokenSharingActivity +import com.ownd_project.tw2023_wallet_android.TokenSharingActivity import android.content.Intent import android.os.Bundle import android.view.LayoutInflater @@ -72,7 +72,7 @@ class ReaderFragment : Fragment() { } else { println(scanned) if (scanned.startsWith("openid4vp://") || scanned.startsWith("siopv2://")) { - val intent = Intent(context, IdTokenSharingActivity::class.java).apply { + val intent = Intent(context, TokenSharingActivity::class.java).apply { putExtra("siopRequest", scanned) putExtra("index", -1) // 一つ前の画面でアカウントを選択した場合のインデックス } diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop/IdTokenSharringFragment.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop_vp/TokenSharingFragment.kt similarity index 95% rename from app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop/IdTokenSharringFragment.kt rename to app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop_vp/TokenSharingFragment.kt index f3f1b62..fba74d0 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop/IdTokenSharringFragment.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop_vp/TokenSharingFragment.kt @@ -1,4 +1,4 @@ -package com.ownd_project.tw2023_wallet_android.ui.siop +package com.ownd_project.tw2023_wallet_android.ui.siop_vp import android.app.AlertDialog import android.net.Uri @@ -30,15 +30,15 @@ import com.ownd_project.tw2023_wallet_android.utils.viewBinding // todo レイアウト調整 // todo SVGをURLから表示 -class IdTokenSharringFragment : Fragment(R.layout.fragment_id_token_sharring) { +class TokenSharingFragment : Fragment(R.layout.fragment_id_token_sharring) { companion object { - private val tag = IdTokenSharringFragment::class.simpleName + private val tag = TokenSharingFragment::class.simpleName } private val binding by viewBinding(FragmentIdTokenSharringBinding::bind) private lateinit var issuerDetailBinding: FragmentIssuerDetailBinding - private val args: IdTokenSharringFragmentArgs by navArgs() + private val args: TokenSharingFragmentArgs by navArgs() private val sharedViewModel by activityViewModels() private val viewModel: IdTokenSharringViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -47,7 +47,7 @@ class IdTokenSharringFragment : Fragment(R.layout.fragment_id_token_sharring) { val activity = requireActivity() - val menuProvider = IdTokenSharingFragmentMenuProvider(this, activity.menuInflater) + val menuProvider = TokenSharingFragmentMenuProvider(this, activity.menuInflater) activity.addMenuProvider(menuProvider, viewLifecycleOwner, Lifecycle.State.RESUMED) viewModel.initDone.observe(viewLifecycleOwner, ::onInitDone) @@ -240,7 +240,7 @@ class IdTokenSharringFragment : Fragment(R.layout.fragment_id_token_sharring) { private fun onOpenSelectCredential(view: View) { Log.d(tag, "on click") - val action = IdTokenSharringFragmentDirections.actionIdTokenSharringToNavigationCertificate() + val action = TokenSharingFragmentDirections.actionIdTokenSharringToNavigationCertificate() findNavController().navigate(action) } @@ -281,7 +281,7 @@ class IdTokenSharringFragment : Fragment(R.layout.fragment_id_token_sharring) { val selectedCredential = sharedViewModel.selectedCredential.value if (selectedCredential != null) { // todo 複数対応 - viewModel.shareCredential(this, listOf(selectedCredential)) + viewModel.shareVpToken(this, listOf(selectedCredential)) } else { viewModel.shareIdToken(this) } @@ -319,7 +319,7 @@ class IdTokenSharringFragment : Fragment(R.layout.fragment_id_token_sharring) { if (postResult.location != null) { val url = postResult.location val cookies = postResult.cookies - val action = IdTokenSharringFragmentDirections.actionIdTokenSharringToWebViewFragment(url, cookies) + val action = TokenSharingFragmentDirections.actionIdTokenSharringToWebViewFragment(url, cookies) findNavController().navigate(action) } } diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop/IdTokenSharingFragmentMenuProvider.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop_vp/TokenSharingFragmentMenuProvider.kt similarity index 91% rename from app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop/IdTokenSharingFragmentMenuProvider.kt rename to app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop_vp/TokenSharingFragmentMenuProvider.kt index 643a240..c85c8d1 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop/IdTokenSharingFragmentMenuProvider.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop_vp/TokenSharingFragmentMenuProvider.kt @@ -1,4 +1,4 @@ -package com.ownd_project.tw2023_wallet_android.ui.siop +package com.ownd_project.tw2023_wallet_android.ui.siop_vp import android.view.Menu import android.view.MenuInflater @@ -7,7 +7,7 @@ import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import com.ownd_project.tw2023_wallet_android.R -class IdTokenSharingFragmentMenuProvider( +class TokenSharingFragmentMenuProvider( private val fragment: Fragment, private val menuInflater: MenuInflater ) : MenuProvider { diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop/IdTokenSharringViewModel.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop_vp/TokenSharingViewModel.kt similarity index 99% rename from app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop/IdTokenSharringViewModel.kt rename to app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop_vp/TokenSharingViewModel.kt index cf84046..2840df8 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop/IdTokenSharringViewModel.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/siop_vp/TokenSharingViewModel.kt @@ -1,4 +1,4 @@ -package com.ownd_project.tw2023_wallet_android.ui.siop +package com.ownd_project.tw2023_wallet_android.ui.siop_vp import android.content.Context import android.util.Log @@ -286,7 +286,7 @@ class IdTokenSharringViewModel : ViewModel() { } } - fun shareCredential(fragment: Fragment, credentials: List) { + fun shareVpToken(fragment: Fragment, credentials: List) { Log.d(TAG, "shareVPToken") viewModelScope.launch(Dispatchers.IO) { val result = openIdProvider.respondVPResponse(credentials) diff --git a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/verification/CredentialVerificationViewModel.kt b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/verification/CredentialVerificationViewModel.kt index 8babbe4..be21374 100644 --- a/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/verification/CredentialVerificationViewModel.kt +++ b/app/src/main/java/com/ownd_project/tw2023_wallet_android/ui/verification/CredentialVerificationViewModel.kt @@ -30,7 +30,7 @@ class CredentialVerificationViewModel : ViewModel() { val result: LiveData = _result fun verifyCredential(format: String, credential: String) { - Log.d(com.ownd_project.tw2023_wallet_android.ui.siop.TAG, "verifyCredential seed successfully") + Log.d(com.ownd_project.tw2023_wallet_android.ui.siop_vp.TAG, "verifyCredential seed successfully") viewModelScope.launch(Dispatchers.IO) { val result = JWT.verifyJwtByX5U(credential) result.fold( diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index b247f44..a0367fe 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -132,7 +132,7 @@