Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vp same device flow #7

Merged
merged 5 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@
<!-- android:path="/oauth2redirect"-->
<!-- android:scheme="https" />-->
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="openid4vp" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="https"
android:host="www.ownd-project.com"
android:path="/wallet" />
</intent-filter>
</activity>
<activity
android:name=".SplashActivity"
Expand Down Expand Up @@ -82,7 +98,7 @@
</intent-filter>
</activity>
<activity
android:name=".IdTokenSharingActivity"
android:name=".TokenSharingActivity"
android:exported="true"
android:theme="@style/Theme.Tw2023walletandroid">
</activity>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
}

}
}

Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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}")
Expand Down Expand Up @@ -506,7 +528,7 @@ fun mergeOAuth2AndOpenIdInRequestPayload(
return createRequestObjectPayloadFromMap(mergedMap)
}

fun sendRequest(redirectUrl: String, formData: Map<String, String>): PostResult {
fun sendRequest(destinationUri: String, formData: Map<String, String>, responseMode: ResponseMode): PostResult {
val client = OkHttpClient.Builder()
.followRedirects(false)
.build()
Expand All @@ -516,11 +538,19 @@ fun sendRequest(redirectUrl: String, formData: Map<String, String>): 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()
Expand All @@ -534,7 +564,7 @@ fun sendRequest(redirectUrl: String, formData: Map<String, String>): 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"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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) // 一つ前の画面でアカウントを選択した場合のインデックス
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<CredentialSharingViewModel>()
private val viewModel: IdTokenSharringViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -286,7 +286,7 @@ class IdTokenSharringViewModel : ViewModel() {
}
}

fun shareCredential(fragment: Fragment, credentials: List<SubmissionCredential>) {
fun shareVpToken(fragment: Fragment, credentials: List<SubmissionCredential>) {
Log.d(TAG, "shareVPToken")
viewModelScope.launch(Dispatchers.IO) {
val result = openIdProvider.respondVPResponse(credentials)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CredentialVerificationViewModel : ViewModel() {
val result: LiveData<Boolean> = _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(
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/navigation/mobile_navigation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
</fragment>
<fragment
android:id="@+id/id_token_sharring"
android:name="com.ownd_project.tw2023_wallet_android.ui.siop.IdTokenSharringFragment"
android:name="com.ownd_project.tw2023_wallet_android.ui.siop_vp.TokenSharingFragment"
tools:layout="@layout/fragment_id_token_sharring">
<argument
android:name="siopRequest"
Expand Down
Loading
Loading