Verify with Samsung Wallet
Overview
The following document provides details to Online Relying Party partner to integrate with Samsung Wallet to get Digital Credentials data issued on Samsung wallet devices from trusted issuers. One of the main reasons to introduce Online Relying Party is to enable Samsung Wallet customer to utilize the Digital ID in their wallets to simplify their online interactions which needs to verify their identity. The following are some of the use-cases we expect to satisfy with our Online RP support.
Samsung Online Relying party supports two modalities: Online same-device RPApp-to-WalletApp and Online same-device Web-to-Wallet App. At this time period, we do not support Cross-device functionality.
The identity verification as part of Samsung Wallet is accomplished with the "Verify with Samsung Wallet" button.
Service Flow
Pre-requisites – Technical and System Requirements
- Samsung wallet currently supports same-device RP functionality only. Device and Android OS version limitations are given below:
- Driver's Licenses or State IDs can be added to the following Galaxy devices
- Galaxy S Series - S20 or later
- Galaxy Z Series - Z Flip 5G, Z Fold 5G, Z Flip 2, and Fold 2 or later
- Galaxy A Series - A53, A54
- Mobile Driver's License or State ID is only available on devices running Android 12(S) or above
- Driver's Licenses or State IDs can be added to the following Galaxy devices
- App2App SDK (RpSdk) Requirements
- Minimum Android SDK : RpSdk requires a minimum API level of 26
- Kotlin : 1.7.10 is required
Integration Steps
The following steps describe the integration process
- Step1. Wallet portal on-boarding. Please refer Wallet Portal On-boarding Information
- Review the Wallet portal on-boarding guide
- Register Wallet portal and proceed with on-boarding process
- Create wallet card as 'Relying Party' type in Wallet portal.
- Step2. Select App2App or Web2App
- Select the integration model (App2App or Web2App)
- Follow steps listed in each subsection
- Implement functions
- Implement Verify with Samsung Wallet. Follow steps listed Implementing VWW button
- App2App : Implement partner app with RP SDK. Follow steps listed App2App SDK Integration Specs
- Web2App : Implement server APIs. Follow steps listed Web2App API Integration Specs
- Step3. Test 'Verify with Samsung Wallet' function and release the function to user
- Test overall 'Verify with Samsung Wallet' function.
- Remove test mode in Wallet portal after test is done
- Expose the 'Verify with Samsung Wallet' function to the user in Partner side.
Manage Wallet Cards
To use the onlineRP function, you need to create a card as 'Relying Party' type.
Overall Managing Process
The following image illustrates the process of managing wallet cards:
Create Wallet Cards (Draft status)
Partners can create and manage your wallet cards with this step-by-step guide.
Manage Wallet Cards
- You can manage all registered wallet cards.
- You can edit wallet cards and check the status of cards.
General Information
The general information page allows the partner to enter administrative details to manage their cards, as well as to define common parameters for the wallet.
Item | Description | ||
---|---|---|---|
Testing Mode | All data generated in testing mode is periodically deleted. Be sure to turn off the 'Testing mode' setting after the test is over. |
||
Wallet Card Name | Representative title of the wallet card | ||
Wallet Card ID | Unique wallet card domain name(Automatic generation) | ||
Partner App Package Name | Partner app package name | ||
Wallet Card Template | Pre-defined partner’s wallet card template(Type > Sub Type > Design Type) | ||
Wallet Card custom setting | |||
type | Authentication issuer | Set the Authentication Issuer for the Relying Party service to be provided as this Wallet Card. Please select Authentication Issuers from the identity provider groups. Only Authentication issuers same as the “Service Location” of this Relying Party service are displayed. ※ The Identity provider of the “Authentication issuer” would be supported depending on the "Service Location" set |
|
Partner Get card data | URL which partner receives card data inquiry API call If a partner uses this API, enter the URL. Otherwise, leave it to be blank. ※ The attribute could be activated with the approval of a manager. |
||
Partner Send card state | URL which partner receives card data inquiry API call If a partner uses this API, enter the URL. Otherwise, leave it to be blank. ※ The attribute could be activated with the approval of a manager. |
||
Samsung Server IPs | Samsung Wallet Server IPs which need to be allowed at the partner’s firewall (separately described for inbound and outbound) | ||
Service Location | Select a regional limit for the wallet card. If there was no selected location, that wallet card is shown in all locations. If the specified location was selected, that wallet card is shown only in the selected location. Users can 'Verify with Samsung Wallet' only in Service locations where the wallet service is provided. ※ The Identity provider of the “Authentication issuer” would be supported depending on the "Service Location" set. |
||
Main(Headquarters) Location | Check to set it as a 'Main location'. As the company's main service country(head office) for creating and proceeding with wallet cards, notification e-mails such as wallet card approval requests are sent only to the selected main location. |
||
Wallet Card data save in Server | Set whether to store Wallet Card data in the server to protect personal information. If the card has sensitive information, you can contact the Developer Support Team not to save it. |
||
Description | Description of the wallet card |
Select Template
You can choose from various types of wallet card templates optimized for partners such as boarding pass, ticket, coupon, and digital ID.
※ For RP Partners : Select "Relying Party type > Other sub type" to set the Relying Party Wallet Card.
You can select the type of wallet card you want to register from the 'Select Wallet Card Template' pop-up. First, select the Wallet Card Type and then select the Wallet Card Sub Type to select one of the templates belonging to it.
Wallet Card custom setting
You must set the attributes of the "Wallet Card custom setting" according to the Wallet Card Type you selected.
※ For RP Partners : The "Authentication issuer" attribute is a unique property of the Relying Party card.
The Identity Provider of the “Authentication issuer” would be supported depending on the "Service Location" set.
e.g. If Service Location is the US, the Authentication Issuer field only supports any Identity Provider belonging to the US.
When the parent hierarchy is checked value, the value of the child that is created later would be automatically checked value.
In the United States, the authentication issuer is the state government, and the driver's license can be understood as an mDL (mobile driver's license).
View Wallet Card
You can view all registered information, also edit and delete the wallet card.
Launch Wallet Cards (Verifying status)
You can launch and activate cards.
- You can activate a card by clicking the Launch button.
- Once a card is launched, the button text changes to 'Launched'. The activation cannot be canceled.
- When a card is launched, its status changes to 'Verifying', and then to ‘Active’ after administrator approval.
Launch Wallet Cards (Rejected status)
If the wallet card is rejected after launching, you can modify and re-launch.
- The administrator registers the reason for rejection when rejecting the launched Wallet card
- Partners will receive an email from the system, including the reason for rejection
- Partners can apply for launch again by checking the reason for rejection and modifying the wallet card information
Testing Mode
You can test a card internally to make sure everything works before you officially release it to users.
- By default, the ‘Testing mode’ option is enabled.
- All data generated in testing mode is periodically deleted.
- Card exposure is not affected even when the testing mode is enabled.
- Be sure to turn off the testing mode after the test is over. (Testing mode ON → Testing mode Off)
Admin Approval (Active status)
All launched cards are activated after the administrator's approval.
- When a card is launched, its status changes to 'Verifying' and then to ‘Active’ after administrator approval.
- When the card is activated, it is made visible to the user.
‘Verify with Samsung Wallet’ Integration
For Wallet integration, you need to insert ‘Verify with Samsung Wallet’ script to your system. ‘Verify with Samsung Wallet’ script is available for Web and Android. Each system has different script composition. To implement ‘Verify with Samsung Wallet’ button, follow the below steps in order.
- Create a tokenized card data (Cdata). Card data is the actual content data of wallet card and it has several format based on card type. Please refer to Generate_Cdata Sample Code for detail.
- Copy the sample ‘Verify with Samsung Wallet’ script from Partner portal’s Wallet Card page and replace cdata with the data token created above.
- Apply the script to your system. Please see Web_Integration Sample Code and App_Integration Sample Code for detail.
※ For RP Partners : The guide below is about 'Verify with Samsung Wallet'.
Please note that the 'Verify with Samsung Wallet' guide is provided as sample code.
It will be updated on the portal before service opening.
- Ref. 8. Implementing VWW button
Below are ‘Verify with Samsung Wallet’ script guide in Partner portal.
For ‘Verify with Samsung Wallet’ integration, you may need some base data. You can find those base data and other necessary information on Partner Portal and Wallet API Spec . You can also add image beacon in the script for tracking effect analysis.
Sequence/Flow Diagram
This section describes two currently supported flows: App2App and Web2App. Please select the model of your choice for integration.
Same-device App2App (via Native Wallet SDK)
Diagram with numbered flows
[Explanation of each flow]
2) Load Button resources
'Verify with Samsung Wallet' Integration and the sample code will be supported
3) Check Service Available Devices
This is the process of checking whether the device supports the 'Verify with Samsung Wallet' function. You can implement it by referring to the provided sample code.
4) Show button with web link
You can implement it by referring to the provided sample code.
Ref. Data Transmit Link
6) Tokenize card data as JWT
This step generates cdata, Card Data Token.
Ref. https://developer.samsung.com/wallet/api/implement-the-button.html
Ref. Wallet Card and Relying Party
7) Verify with Samsung Wallet link
The link will invoke WalletApp using AppLink technology. In the meantime, app2app sdk makes direct connection between WalletApp and PartnerApp
10) getMdocRequestData(DeviceEngagementBytes)
WalletApp makes DeviceEngagementBytes according to the ISO-18013-5 and send it to the PartnerApp
11) sendMdocRequestData(sessionEstablishment)
PartnerApp build sessionEstablishmentBytes (ISO-18013-5) and encrypt it with HKDF (ISO-18013-5, 9.1.1.5 Cryptographic operations)
13) sendMdocResponse(encryptedResponse)
WalletApp send encrypted ISO-18013-5 response payload to the PartnerApp
Same-device Web2App (via Wallet API)
Diagram with numbered flows
[Explanation of each flow]
2) Load Button resources
'Verify with Samsung Wallet' Integration and the sample code will be supported
3) Check Service Available Devices
This is the process of checking whether the device supports the 'Verify with Samsung Wallet' function. You can implement it by referring to the provided sample code.
4) Show button with web link
You can implement it by referring to the provided sample code.
Ref. Data Transmit Link
6) Tokenize card data as JWT
This step generates cdata, Card Data Token.
Ref. https://developer.samsung.com/wallet/api/implement-the-button.html
Ref. Wallet Card and Relying Party
7) Verify with Samsung Wallet link
The link will invoke WalletApp using AppLink technology
10) Transfer DeviceEngagement
WalletApp makes DeviceEngagementBytes according to the ISO-18013-5 and send it to the PartnerServer through Wallet Server
11~12) Request key API (Send Key)
The Wallet Backend Server converts the data received from the request and card information of cardId into JWT (JWS + JWE) and delivers it to the partner server.
The partner server must decrypt the JWT (JWS + JWE) data again.
PartnerApp build sessionEstablishmentBytes (ISO-18013-5) and encrypt it with HKDF (ISO-18013-5, 9.1.1.5 Cryptographic operations)
The partner server must create and transmit the data fields required for authentication as JWT (JWS + JWE) in response to the Wallet Backed Server.
Please refer to the code below links.
- Generate the response value JWT(JWS + JWE)
- JWT (JWS + JWE) decryption between Wallet Backed Server and partner server
- Authentication processing for values in data fields requested for authentication
14) Send Mdoc response
WalletApp send encrypted ISO-18013-5 response payload to the PartnerServer through Wallet Server
15) Request auth API (Send authentication data)
The authentication data card information received in step 14 is converted into JWT (JWS+JWE) and transmitted to the partner server.
The partner server must decrypt the JWT (JWS + JWE) data again.
Please refer to the code below links.
- JWT (JWS + JWE) decryption between Wallet Backed Server and partner server
- Authentication processing for values in data fields requested for authentication
API Guidelines
Verifying with Samsung Wallet
Data Transmit Link
This is how you create a Data Transmit Link. It is a method of safely including tokenized data within the (Verify with Samsung Wallet) VWW link.
The format of the VWW link for this method is as follows.
- The name Data Transmit Link has been changed from Typical flow
Item | Value | Description | ||
---|---|---|---|---|
URL | https://a.swallet.link/vww/v1/{cardId}#Clip?cdata={cdata} | |||
Path Parameters | cardId | String(32) | Required | Wallet card identifier issued from Partner portal when the partner manager signs up for partner services and registers the wallet card they want to service. |
Hash Path Parameters | #Clip | String(5) | Required | Parameters for the Hash link * The first letter is capitalized |
Query Parameters | cdata | String | Required | Actual payload data in basic JSON format to communicate between partners and Samsung Wallet. This must be secured in JWT (JSON Web Token) format. * See the chapter Security for more details. |
Example
https://a.swallet.link/vww/v1/1656147182764415319#Clip?cdata=eyJjdHkiOiJKV1QiLCJhbGciOiJsInRpbWVzdGFtcCI6ImNyZWF0ZWQgdGltZSIsInBhcnRuZXJJRCI6InBhcnRuZXIgSUQifQ.
… … … …
Dn0_oZ3xcr0JuQ3mlSzLIUTxFoTewnZ0MQj7kiNjysNm5Xfwqt5vcN20PeebeLgUx8VJXLy4_9G4BHQ-hd4O9POYuTuAWew.YzdlMTFhO -NYCeL3T0YzNzAD2KcK_HrtwIGEErHLGn6ydaq_fpFdSlxsA3ZJtNpg3wcuqEw5cIdpbPFswbQLropqEpNawg5nlm3DKAA4a1dzaZMbSR1BGZHrH_vIKnx3CY5MO0jNBexl_YIZ5_wB379UYSwumQiPiTZVg2IjYvfht17I4
Verifying Identities
App2App SDK Integration Specs
Description & Use
Rp SDK is an App2App SDK for online scenarios of Samsung Wallet driver's license service.
This SDK provides an implementation for direct communication between the Samsung Wallet app and Partner app.
Build setting
RpSdk requires additional dependencies with:
dependencies {
...
implementation("rp-sdk-1.0-release.aar")
implementation("androidx.core:core-ktx:1.3.2")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
implementation("androidx.lifecycle:lifecycle-livedata-core-ktx:2.7.0")
implementation("io.reactivex.rxjava2:rxjava:2.2.21")
implementation("io.reactivex.rxjava2:rxkotlin:2.4.0")
implementation("io.reactivex.rxjava2:rxandroid:2.1.1")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("com.google.code.gson:gson:2.10.1")
implementation("org.bouncycastle:bcprov-jdk15to18:1.66")
implementation("com.nimbusds:nimbus-jose-jwt:9.37.3")
...
}
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
<uses-permission android:name="android.permission.INTERNET" />
<queries>
<package android:name="com.samsung.android.spay" />
</queries>
...
</manifest>
R8 / Proguard
The specific rules are already bundled into the aar which can be interpreted by R8 automatically
SDK Method
App2App SDK supports one method.
- request()
Signature & parameters of request method.
fun request(targetPackageName: String, requestId: String, appLink: String, onResponseListener: OnResponseListener? = null)
Parameter name | Description |
---|---|
targetPackageName | Pakcage name to connect |
requestId | Random string to idendify each request |
appLink | appLink built by Samsung mcs server guide |
onResponseListener | listener to receive each events or requests |
Sample code
binding.button.setOnClickListener {
rpClientApis.request("com.samsung.android.spay", UUID.randomUUID().toString(), appLink, object : RpClientApis.OnResponseListener {
override fun onGetMdocRequestData(deviceEngagementBytes: ByteArray): ByteArray? {
Log.i(TAG, "onGetMdocRequestData($deviceEngagementBytes)")
/**
* 1. prepare mDoc request data (ISO-18013-5)
* 2. build sessionEstablishmentBytes (ISO-18013-5)
* 3. Encrypt it with HKDF (ISO-18013-5, 9.1.1.5 Cryptographic operations)
**/
return "encryptedSessionEstablishmentBytes"
}
override fun onMdocResponse(encryptedResponse: ByteArray) {
Log.i(TAG, "onMdocResponse($encryptedResponse)")
/**
* 1. Decrypt it with HKDF (ISO-18013-5, 9.1.1.5 Cryptographic operations)
* 2. CBOR decode it
**/
}
override fun onMdocResponseFailed(exception: Exception) {
Log.i(TAG, "onMdocResponseFailed($exception)")
}
})
}
Error Code Explanation
Below exceptions might be occured through onMdocResponseFailed callback
Exceptions name | Description |
---|---|
RpCommunicationException | This error occurs when the data requested by the listener is incorrect. |
RpConnectionFailedException | This occurs when app-to-app communication between apps is not working. This usually occurs when the target Package Name is written incorrectly |
Web2App API Integration Specs
The API specification need to be implemented by RP partner are described below:
Called by Samsung to RP partner
Send Key
Send key info of wallet app and return the types of data fields requested to the client for authentication of mDL
[Request]
Type | Value | Description | ||
---|---|---|---|---|
Method | POST | |||
URL | {Partner server URL}/rp/v1.0/{cardId}/{RefId}/key | |||
Headers | Authorization | String (1024) | Required | Credential token. The token can have prefix "Bearer" as an authorization type, e.g., Bearer <credentials>. * Refer to Authorization Token for more details. |
x-request-id | String (32) | Required | Request identifier. Randomly generated UUID string. |
|
Path Parameters | cardId | String (32) | Required | Wallet card identifier. * Refer to "Add to Wallet" Interfaces for more details. |
refId | String (32) | Required | Unique content identifier defined by the content provider | |
Query Parameter | N/A | |||
Payload | data | String(3000) | Required | Data JWT encrypted with public key information and card type. if decrypted this data is decoded, it has the following format information. { “data”: “XXXXXXXXXXX”, “card”: { "type": "relyingparty", "subType": "others", "designType": "us-01" } } |
Example
POST {Partner server URL}/rp/v1.0/{cardId}/{RefId}/key
Content-Type: application/json
{
“data”: “eyJjdHkiOiJBVVRIIiwidmVyIjoiMiIsInBhcnRuZXJJZCI6InRlc3QiLCJ1dGMiOjE3MTYyMDYzNjAxMTAsImFsZyI6IlJTMjU2In0.ZXlKbGJtTWlPaUpCTVRJNFIwTk5JaXdpWVd4bklqb2lVbE5CTFU5QlJWQXRNalUySW4wLlZ5aFAxS0FnMVJHbzBDN2NIX2pYdGtfODdQbnhrRmpfWkpPcnNSUUs4MnN0OWVxTjEyVzVMOEJaX1d5NGVzMzE3VDNad0pncmpwZWdZOEk3aVlCWWRlOGJ5LXFiMjBLU3RUc3JsSzlPSlFnN1FaM2xZaUxscXlTb0VlbERvd0FPaTRMRy1JUkZWdVlrbXRiNTg3UTd1ZWNuQ1lWWGZWalVEcG01YXBFbDV3SzM1UGZ3d0dkREM2TmowZ1AwbTZ3Nk1kdl9mdDBvZWc2MWZjaGdBYnY0emxMZjU2cVYzM0t6ZjdjbWVpbkJRNnpMSGUtYmFWYXhVZk5Ld2htZWVjUzFTV3laRm1NVlJ6MEFsMnBxa0dQLVJkT1Iza3VzaVo0VjFIdy1aQ2IyVWVwYVdZRU9nUEdrVW1mbTFuOWJWT1ZmZ1NUV1F0SE5pVTFJYVRHTG1DWlpVQS5PMzZrd1g4WmJnQ21wd3o2Ll9KZEhFVXNnbm13b1drdDRMcU4xMUNCaUNTSnUtbWpYV2ZrckxoS0ZVenBsS085ckdXbUdPZ0pqUkF1NTFsOTRYc2VIVWdfWU9NS2RGR1VOMWJhMHB3Y0tFNGtJMEt2dkFOWHprODN0azBjQzROT2F6VzlmOVNTT0RhMU9IMEFoaVFzQzdDeVFqNndNLWFlVk8waEJwSEJkMEdURUh1Z3Exc21vVmxRbjBlSnJqWHM4X3FwcnpLekwtaDFPcFk1aEs1ZUg5Q3NiSms0aEhCNGNmWUlKRUJFZ09BcGZxcGFuMGFSVGFmODhhdXlqSGZHdGRMa0tLWDV0Q0RTajIxSE5TT0FhWTJVWlZrR0hxU0wzNGJabTU5aEZMNVdHa0lJcE9BMHlWUE9tQzNWTFlKV2JsMm85LkFoeDBVYTVGeTZudkxKVXVkeTAzSHc.E07YYl7IoR3885VYKsS5_q1IcpX750uU2Ge5suJSedx3Dr_U0x4tSe9_0NxM46dyWnFUXrUAGfjDnjHIBc707Li9VI3XtyiHwnwEiFydgv1QB9oddkYyZuahXQmJHVUqNcdt6DF2CAqzF5QgMVqfMGSE_t7IPU8vQFXE34DO-sKzj8ftdusS2EcdANBqOKCHih3m39NouBPFhcX68PlPcW50diXlupxwEGniU2t3Co24YlIaKLGac669aCcXDQr34utVUqhTJt_FTXkahAlzoA34_Hj_s82FivIXh1ITD74UOjZSe7IBWya_kVysoZavnmzTz2tH9CbwyCvx8wA”
}
[Response]
Type | Value | Description | ||
---|---|---|---|---|
Http Status | 200 OK | |||
Payload | data | String(3000) | Required | Data JWT encrypted with the types of data fields requested to the client for authentication of mDL. |
[Result]
Http status code | Description |
---|---|
200 OK | Success |
400 Bad Request | Requests cannot or will not be processed the request due to something that is perceived to be a client error. |
401 Unauthorized | Authorization token is invalid or expired. |
500 Internal Server Error | Server encountered an unexpected condition that prevented it from fulfilling the request. |
503 Service Unavailable | Server is not ready to handle the request. |
Send authentication data
Data according to the requested data fields is encrypted and then transmitted along with the card information of this data.
[Request]
Type | Value | Description | ||
---|---|---|---|---|
Method | POST | |||
URL | {Partner server URL}/rp/v1.0/{cardId}/{RefId}/auth | |||
Headers | Authorization | String (1024) | Required | Credential token. The token can have prefix "Bearer" as an authorization type, e.g., Bearer <credentials>. * Refer to Authorization Token for more details. |
x-request-id | String (32) | Required | Request identifier. Randomly generated UUID string. |
|
PathParameters | cardId | String (32) | Required | Wallet card identifier. * Refer to "Add to Wallet" Interfaces for more details. |
refId | String (32) | Required | Unique content identifier defined by the content provider | |
QueryParameter | N/A | |||
Payload | data | String(3000) | Required | Data JWT encrypted with public key information and card type. if decrypted this data is decoded, it has the following format information. { “data”: “XXXXXXXXXXX”, “card”: { "type": "idcard", "subType": "drivers", "designType": "us-01" } } |
Example
POST {Partner server URL}/rp/v1.0/{cardId}/{RefId}/key
Content-Type: application/json
{
“data”: “eyJjdHkiOiJBVVRIIiwidmVyIjoiMiIsInBhcnRuZXJJZCI6InRlc3QiLCJ1dGMiOjE3MTYy
MDYzNjAxMTAsImFsZyI6IlJTMjU2In0.ZXlKbGJtTWlPaUpCTVRJNFIwTk5JaXdpWVd
4bklqb2lVbE5CTFU5QlJWQXRNalUySW4wLlZ5aFAxS0FnMVJHbzBDN2NIX2pYdGtf
ODdQbnhrRmpfWkpPcnNSUUs4MnN0OWVxTjEyVzVMOEJaX1d5NGVzMzE3VDNad
0pncmpwZWdZOEk3aVlCWWRlOGJ5LXFiMjBLU3RUc3JsSzlPSlFnN1FaM2xZaUxsc
XlTb0VlbERvd0FPaTRMRy1JUkZWdVlrbXRiNTg3UTd1ZWNuQ1lWWGZWalVEcG01
YXBFbDV3SzM1UGZ3d0dkREM2TmowZ1AwbTZ3Nk1kdl9mdDBvZWc2MWZjaGdBY
nY0emxMZjU2cVYzM0t6ZjdjbWVpbkJRNnpMSGUtYmFWYXhVZk5Ld2htZWVjUzFTV
3laRm1NVlJ6MEFsMnBxa0dQLVJkT1Iza3VzaVo0VjFIdy1aQ2IyVWVwYVdZRU9nUEd
rVW1mbTFuOWJWT1ZmZ1NUV1F0SE5pVTFJYVRHTG1DWlpVQS5PMzZrd1g4WmJ
nQ21wd3o2Ll9KZEhFVXNnbm13b1drdDRMcU4xMUNCaUNTSnUtbWpYV2ZrckxoS0Z
VenBsS085ckdXbUdPZ0pqUkF1NTFsOTRYc2VIVWdfWU9NS2RGR1VOMWJhMHB3Y
0tFNGtJMEt2dkFOWHprODN0azBjQzROT2F6VzlmOVNTT0RhMU9IMEFoaVFzQzdDe
VFqNndNLWFlVk8waEJwSEJkMEdURUh1Z3Exc21vVmxRbjBlSnJqWHM4X3FwcnpLe
kwtaDFPcFk1aEs1ZUg5Q3NiSms0aEhCNGNmWUlKRUJFZ09BcGZxcGFuMGFSVGF
mODhhdXlqSGZHdGRMa0tLWDV0Q0RTajIxSE5TT0FhWTJVWlZrR0hxU0wzNGJabTU
5aEZMNVdHa0lJcE9BMHlWUE9tQzNWTFlKV2JsMm85LkFoeDBVYTVGeTZudkxKVX
VkeTAzSHc.E07YYl7IoR3885VYKsS5_q1IcpX750uU2Ge5suJSedx3Dr_U0x4tSe9_0Nx
M46dyWnFUXrUAGfjDnjHIBc707Li9VI3XtyiHwnwEiFydgv1QB9oddkYyZuahXQmJHVU
qNcdt6DF2CAqzF5QgMVqfMGSE_t7IPU8vQFXE34DO-sKzj8ftdusS2EcdANBqOKCHih
3m39NouBPFhcX68PlPcW50diXlupxwEGniU2t3Co24YlIaKLGac669aCcXDQr34utVUq
hTJt_FTXkahAlzoA34_Hj_s82FivIXh1ITD74UOjZSe7IBWya_kVysoZavnmzTz2tH9Cbw
yCvx8wA”
}
[Response]
Type | Value | Description |
---|---|---|
Http Status | 200 OK 400 Bad Request |
[Result]
Http status code | Description |
---|---|
200 OK | Success |
400 Bad Request | Requests cannot or will not be processed the request due to something that is perceived to be a client error. |
401 Unauthorized | Authorization token is invalid or expired. |
500 Internal Server Error | Server encountered an unexpected condition that prevented it from fulfilling the request. |
503 Service Unavailable | Server is not ready to handle the request. |
Code explanation based on sample code
JWT (JWS + JWE) decryption between Wallet Backed Server and partner server
1. Verify with generate JWS by the body data
Code Block 1 Verify with generate JWS
// Generate JWS by the body data
private static SignedJWT parseJWT(final String data) {
try {
return SignedJWT.parse(data);
} catch (ParseException e) {
log.error("parserJWT error class : {}, error message : {}", e.getClass(), e.getMessage());
throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "parserJWT error");
}
}
Code Block 2 Verify JWS
// Verify JWS using Samsung Public key
public RequestBody getRequestBody(final KeyRing keyRing) {
final SignedJWT signedJWT = JWTUtils.verify(keyRing.getTargetPublicKey(), encryptedData, 60 * 10000); // Verify and generate JWS
try {
final String strBody = JWTUtils.getDecryptedPayloadFrom(keyRing.getSourcePrivateKey(), JWEObject.parse(signedJWT.getPayload().toString())); // Decryption JWE by the JWS
return objectMapper.readValue(strBody, RequestBody.class); // Convert to data format requested by client
} catch (ParseException | JsonProcessingException e) {
log.error("getRequestBody : {}", e.getMessage());
throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "data body parse error");
}
}
2. Decryption JWE by JWS
Code Block 3 Generate JWE by JWS
JWEObject.parse(signedJWT.getPayload().toString())
Code Block 4 Decryption data
public static String getDecryptedPayloadFrom(final Key privateKey, final JWEObject data) {
try {
data.decrypt(new RSADecrypter((PrivateKey) privateKey)); // Decryption JWE using Partner Private Key
return data.getPayload().toString();
} catch (JOSEException e) {
log.error("JOSEException message : {}", e.getMessage());
throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "getDecryptedPayloadFrom error");
}
}
3. Convert to the format send by the client
Code Block 5 Convert to the format send by the client
public RequestBody getRequestBody(final KeyRing keyRing) {
final SignedJWT signedJWT = JWTUtils.verify(keyRing.getTargetPublicKey(), encryptedData, 60 * 10000); // Verify and generate JWS
try {
final String strBody = JWTUtils.getDecryptedPayloadFrom(keyRing.getSourcePrivateKey(), JWEObject.parse(signedJWT.getPayload().toString())); // Decryption JWE by the JWS
return objectMapper.readValue(strBody, RequestBody.class); // Convert to data format requested by client
} catch (ParseException | JsonProcessingException e) {
log.error("getRequestBody : {}", e.getMessage());
throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "data body parse error");
}
}
Generate MdocEstablishment
1. Generate RSA Key per refId
Code Block 6 Manage RSA per refId using cache
public class TransactionContext {
private final KeyPair keyPair; // RSA Key
private final byte[] clientEngagement; // Body data received through key API, base64URL decoded value
@EqualsAndHashCode.Exclude
private int encryptMessageCounter = 0; // Count value when encrypted
@EqualsAndHashCode.Exclude
private int decryptMessageCounter = 0; // Count value when decrypted
}
private Cache<String, TransactionContext> contextCache; // RSA key management per refid with memory cache
// Generate and store RSA Key per refId only once upon first request
public TransactionContext setTransactionContext(final String key, final String base64EncodedClientEngagement) {
log.info("base64EncodedClientPublicKey : {}", base64EncodedClientEngagement);
this.contextCache.put(key, new TransactionContext(KeyUtils.generateKeyPair() , Base64Utils.decode(base64EncodedClientEngagement.getBytes())));
return this.getTransactionContextBy(key);
}
// Part of retrieving RAS Key based on refId
public TransactionContext getTransactionContextBy(final String key) {
return Optional.ofNullable(this.contextCache.getIfPresent(key)).orElseThrow(() -> {
log.info("{} is empty", key);
return new CustomException(HttpStatus.BAD_REQUEST, "No key matching the refId");
});
}
2. Create request field values
Code Block 7 Create request field values
@Override
public Mono<List<String>> createRequest(final PartnerInputDTO inputDTO) {
final String mockData = "{ \"docType\": \"org.iso.18013.5.1.mDL\", \"nameSpaces\": { \"org.iso.18013.5.1\": { \"sex\": false, \"portrait\": false, \"given_name\": false, \"issue_date\": false, \"expiry_date\": false, \"family_name\": false, \"document_number\": false, \"issuing_authority\": false }, \"org.iso.18013.5.1.aamva\": { \"DHS_compliance\": false, \"EDL_credential\": false } } }";
return Mono.just(Collections.singletonList(mockData));
}
3. Generate Establishment
Code Block 8 Establishment
@AllArgsConstructor
public class Establishment {
private final TransactionContext context; // Info of client public key , partner private key, public key
private final List<String> strReqs; // Data field information required for authentication to the client
private final KeyRing keyRing; // RSA Key information for JWT(JWS + JWE) encryption and decryption between Wallet Backed Server and partner server.
}
Code Block 9 Generate Establishment
protected CBORObject generate() {
final CBORObject sessionEstablishment = CBORObject.NewMap();
sessionEstablishment.set(E_READER_KEY, CBORObject.FromObjectAndTag(KeyUtils.getEReaderKey(context), TAG_SIZE)); // Generate oneKey by public key in transactionContext
sessionEstablishment.set(DATA, CBORObject.FromObject(CipherUtils.encrypt(context, generateRequestFormat(getRequestCBORObjectsFrom(strReqs))))); // Add request data field information for authentication.
return sessionEstablishment;
}
Generate the response value JWT(JWS + JWE)
1. Generate establishment with JWE
Code Block 10 Generate establishment with JWE
public static String encryptedStringJWE(final Key publicKey, final String data) { // Please enter Samsung Public Key and establishment data
final JWEObject jwe = new JWEObject(
new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128GCM).build(), new Payload(data));
try {
jwe.encrypt(new RSAEncrypter((RSAPublicKey) publicKey));
return jwe.serialize();
} catch (JOSEException e) {
log.error("encryptedStringJWE Exception message : {}", e.getMessage());
throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "encryptedStringJWE error");
}
}
2. Generate JWS by JWE
Code Block 11 Generate JWS by JWE
public static String generateSignedStringJWS(final Key privateKey, final Key publicKey, final String payload) { // Enter your partner’s public key, private key, and JWE data.
try {
final JWSObject jwsObj = new JWSObject(getDefaultJWSHeader(), new Payload(payload));
JWSSigner signer = new RSASSASigner(new RSAKey.Builder((RSAPublicKey) publicKey).privateKey((RSAPrivateKey) privateKey).build());
jwsObj.sign(signer);
return jwsObj.serialize();
} catch (JOSEException e) {
log.error("encryptedStringJWS Exception message : {}", e.getMessage());
throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "generateSignedStringJWS error");
}
}
3. Generate JWT(JWS + JWE)
Code Block 12 Generate JWT
public PartnerOutputDTO toPartnerOutputDTO() {
final CBORObject generate = this.generate(); // create establishment
final String establishment = Base64Utils.encode(generate.EncodeToBytes()); // base64URL encoding the establishment data.
final String strJWE = JWTUtils.encryptedStringJWE(keyRing.getTargetPublicKey(), establishment); // generate JWE
return new PartnerOutputDTO(JWTUtils.generateSignedStringJWS(keyRing.getSourcePrivateKey(), keyRing.getSourcePublicKey(),strJWE)); // generate JWS by JWE
}
Authentication processing for values in data fields requested for authentication
1. Retrieve transactionContext value stored in cache using refId value
Code Block 13 Retrieve transactionContext value stored in cache using refId value
@Override
public Mono<TransactionContext> getContext(final PartnerInputDTO inputDTO) {
return Mono.just(this.transactionContextManager.getTransactionContextBy(inputDTO.getRefId()));
}
2. Processes the decryption process of the request body data like JWT (JWS + JWE) decryption between Wallet Backed Server and partner server.
3. Generate MdocResponse
Code Block 14 MdocResponse
public class MdocResponse {
private final TransactionContext context; // Managed tranactionContext by refId
private final byte[] data; // Base64URL decoded data after decrypting JWT (JWS + JWE) data.
public MdocResponse(final TransactionContext context, final String inputDTO) {
this.context = context;
this.data = Base64Utils.decode(inputDTO.getBytes(StandardCharsets.UTF_8));
}
}
4. Get the field values requested for authentication from the data in mDocResponse.
Code Block 15 Get the field values requested for authentication from the data in mDocResponse.
public String getData() {
// SessionData = {
// ? "data" : bstr ; Encrypted mdoc response or mdoc request
// ? "status" : uint ; Status code
// }
final CBORObject response = CBORObject.DecodeFromBytes(data);
checkType(response, CBORType.Map);
final CBORObject data = response.get(DATA);
checkType(data, CBORType.ByteString);
return CBORObject.DecodeFromBytes(isEncryptedMode ? CipherUtils.decrypt(this.context, data.GetByteString()) : data.GetByteString()).ToJSONString();
}
5. Create a Session value using the transactionContext value managed by refId and then decrypt it.
Code Block 16 Create a Session value using the transactionContext value managed by refId and then decrypt it.
private static byte[] processCipher(final CipherMode cipherMode, final TransactionContext context, final byte[] bytes) { // CipherMode : encrypt or decrypt, bytes : Data passed by the client
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final int counter = CipherMode.ENCRYPT == cipherMode ? context.getEncryptMessageCounter() : context.getDecryptMessageCounter();
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, getSessionKeyIv(cipherMode.identifier, counter));
cipher.init(cipherMode.cipherMode , getSecretKeySpec(context, cipherMode.info), parameterSpec);
return cipher.doFinal(bytes);
} catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException |
NoSuchAlgorithmException | BadPaddingException | InvalidKeyException e) {
log.error("error type : {}, message :{}", e.getClass(), e.getMessage());
throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "processCipher error");
}
}
6. Examining data received from the client.
Code Block 17 Examining data received from the client.
@Override
public Mono<Void> authentication(final String response) {
log.info("response info : {}", response);
return Mono.empty();
}
Implementing Button
For Samsung Wallet integration, you need to insert an "Verify with Samsung Wallet" script into your system. To implement an "Verify with Samsung Wallet" button, follow the procedure below:
- First, proceed with script composition with the sample script on the [Partners Portal][Partners Portal] or refer to the Integration sample code.
- Create the tokenized card data (Cdata). Card data is the actual content of the wallet card and it has several formats based on the card type. Refer to the CData Generation Sample Code for details.
Important
The card data token expires in 30 seconds after creation, so it needs to be created right after the user clicks the "Verify with Samsung Wallet” button.
For "Verify with Samsung Wallet" integration, you may also need some base data. You can find that and other necessary information on Partners Portal
Implementing VWW button on the Web
This section explains how to implement an "Verify with Samsung Wallet" button using JavaScript in a web view.
Web Button Reference with Importing API JavaScript
If you implement the "Verify with Samsung Wallet" button using this script, the button is shown only on the devices that support Samsung Wallet.
To automatically parse <samsung:wallet> HTML tags when the page is loaded, include the following standard JavaScript:
<script src="https://us-cdn-gpp.mcsvc.samsung.com/lib/wallet-card.js" type="text/javascript"></script>
You can use these tags or JavaScript functions for the web button if you're rendering HTML and you have proper partner permissions. You can also use the script by referring to the various attributes.
samsung:wallet HTML Tag
The ‘samsung:wallet’ namespace tag defines the placement and various attributes of the "Verify with Samsung Wallet" web button for Samsung Wallet.
<samsung:wallet
type="vww" authtoken="AUTH_TOKEN"
cardid="CARD_ID" cdata="CDATA" partnercode="PARTNER_CODE" buttonid="BUTTON_ID" buttontype="btnVWSW"
rdclickurl="RD_CLICK_URL" rdimpressionurl="RD_IMPRESSION_URL"
</samsung:wallet>
Button attributes
Attribute | Type | Required | Description |
---|---|---|---|
type | String | Y | Service type. Default is "atw" • "atw" : 'Add to Samsung Wallet' • "vww" : Verify with Samsung Wallet' |
authtoken | String | Y | API auth token It would be generated on the Partners portal. |
cardid | String | Y | Wallet card identifier * Value granted from the Partners Portal. |
cdata | String | Y | Encrypted card object (JSON). * This field needs to be encrypted. * Refer to Security for more details. |
partnercode | String | Y | Partner code. * Value granted from the Partners portal. |
buttonid | String | Y | DOM element ID for the "Verify with Samsung Wallet" web button for Samsung Wallet |
buttontype | String | N | "Verify with Samsung Wallet" button type. ["btnSW" / "btnVWSW", default : btnSW] |
inline | String | N | Flag to display the "Verify with Samsung Wallet" image button in one-line format. Default: true (one-line). |
locale | String | N | Locale of the "Verify with Samsung Wallet" image button. |
rdclickurl | String | Y | URL for logging a button click event. * Value granted from the Partners Portal. |
rdimpressionurl | String | Y | URL for logging a button impression event. * Value granted from the Partners Portal. |
showforced | String | N | Flag to force the "Verify with Samsung Wallet" button to be displayed. Default: false. |
mediatheme | String | N | Load the button’s resources from the media theme policy. There are 4 themes: default, inversion, lightonly, and darkonly. Default: default. *default: Load the button’s theme according to the prefers-color-scheme policy. *inversion: Load the inverse of the default button’s theme. *lightonly: Load the light theme of the default button. *darkonly: Load the dark theme of the default button. |
style | String(CSSStyleDeclaration) | N | Load the button with custom style |
onshowbutton | Function | N | Callback handler function for the button’s on-show event |
onclickbutton | Function | N | Callback handler function for the button’s on-click event. If you register the handler function, you must return a callback or promise value. * Refer to Usage of onClickButton Handler for more details. |
samsungWallet.addButton Function
This function allows you to explicitly render the Samsung Wallet API for the "Verify with Samsung Wallet" web button.
Button attributes
samsungWallet.addButton({
type: "vww",
authToken: "AUTH_TOKEN",
cardId: "CARD_ID",
cdata: "CDATA",
partnerCode: "PARTNER_CODE",
targetId: "TARGET_ID",
buttonId: "BUTTON_ID",
buttonType: "btnVWSW",
RDClickUrl: "RD_CLICK_URL",
RDImpressionUrl: "RD_IMPRESSION_URL",
})
Unlike the samsung:wallet HTML tag, you must use camelCase in the button attributes in function case.
Attribute | Type | Required | Description |
---|---|---|---|
type | String | Y | Service type. Default is "atw" • "atw" : 'Add to Samsung Wallet' • "vww" : Verify with Samsung Wallet' |
authToken | String | Y | API auth token It would be generated on the Partners portal. |
cardId | String | Y | Wallet card identifier * Value granted from the Partners Portal. |
cdata | String | Y | Encrypted card object (JSON). * This field needs to be encrypted. * Refer to Security for more details. |
partnerCode | String | Y | Partner code. * Value granted from the Partners portal. |
targetId | String | Y | DOM (Document Object Model) Element ID to place the "Verify with Samsung Wallet" web button for Samsung Wallet |
buttonId | String | Y | DOM Element ID for the "Verify with Samsung Wallet" web button for Samsung Wallet |
buttonType | String | N | "Verify with Samsung Wallet" button type. ["btnSW" / "btnVWSW", default : btnSW] |
inline | String | N | Flag to display the "Verify with Samsung Wallet" image button in one-line format. Default: true (one-line). |
locale | String | N | Locale of the "Verify with Samsung Wallet" image button. |
RDClickUrl | String | Y | URL for logging a button click event. * Value granted from the Partners Portal. |
RDimpressionurl | String | Y | URL for logging a button impression event. * Value granted from the Partners Portal. |
showForced | String | N | Flag to force the "Verify with Samsung Wallet" button to be displayed. Default: false. |
mediaTheme | String | N | Load the button’s resources from the media theme policy. There are 4 themes: default, inversion, lightonly, and darkonly. Default: default. *default: Load the button’s theme according to the prefers-color-scheme policy. *inversion: Load the inverse of the default button’s theme. *lightonly: Load the light theme of the default button. *darkonly: Load the dark theme of the default button. |
style | Object(CSSStyleDeclaration) | N | Load the button with custom style |
onShowButton | Function | N | Callback handler function for the button’s on-show event |
onClickButton | Function | N | Callback handler function for the button’s on-click event. If you register the handler function, you must return a callback or promise value. * Refer to Usage of onClickButton Handler for more details. |
Usage of onClickButton Handler
You can choose whether to proceed with the next "Verify with Samsung Wallet" step using a promise or a callback function, if you register a callback handler in onClickButton.
We recommend that you add the process of generating JWT cdata (add cdata to options.cdata) to this handler, because of the cdata expiration time.
The function parameters are defined as follows.
Attribute | Type | Required | Description |
---|---|---|---|
options | Button attributes | N | Attributes of the current button |
callback | Function | N | Callback function to pass the flag to proceed. Default: false. |
(Promise resolve) | Function | N | Promise-resolved value to pass the flag to proceed Default: false. |
- Callback to web button process from callback attributes (for ES5)
By executing a callback function with a flag, you can proceed to the next 'Verify with Samsung Wallet' process.
onClickButton: function (options, callback) {
// TODO partner's process
callback(flag)
}
- Callback to web button process from returning Promise (for ES6)
By returning a promise with a resolving flag, you can proceed to the next ‘Verify with Samsung Wallet’ process.
onClickButton: async (options) => {
return new Promise(async (resolve, reject) => {
// TODO partner's process (await)
resolve(flag)
})
}
Resources
Relying Party & Attributes
Wallet Cards
Ref. https://developer.samsung.com/wallet/api/wallet-cards.html
This chapter defines Wallet Card data fields for the attributes object of each wallet card type.
The structure for configuring wallet cards follows the defined specification. Configuring the card data in the specified formatted JSON structure is required. See the details for each card type.
Type | Value | Description | ||
---|---|---|---|---|
Card object | card | Object | Required | Card information. |
card.type | String(16) | Required | Wallet Card type | |
card.subType | String(16) | Required | allet Card sub type | |
card.data[] | Array of Object | Required | Wallet card data container Allows up to 6 objects at once |
|
data[].refId | String(32) | Required | A unique content identifier defined by the content provider |
|
data[].createdAt | Long(13) | Required | Data creation timestamp. Epoch timestamp in milliseconds. UTC±00:00 |
|
data[].updatedAt | Long(13) | Required | Data creation timestamp. Epoch timestamp in milliseconds. UTC±00:00 |
|
data[].language | String(8) | Required | Default content language code. e.g., en, ko |
|
data[].attributes | Object | Required | Attributes of card data. * Refer to the following chapters for each type |
|
data[].attributes.{fields} | Attribute fields by card type. | |||
data[].localization[] | Array of Object | Optional | Information for multilingual support. | |
localization[].language | String(8) | Required | Multilingual content language code. e.g., en, ko |
|
localization[].attributes.{fields} | For displaying a given language, ‘data[].attributes’ can be replaced by localized versions. * Refer to the following chapters for each type |
Example
Example: Card object { "card": { "type": "ticket", "subType": "movies", "data": [{ "refId": "ref-20230304-001", "createdAt": 1612660039000, "language": "en", "attributes": { "title": "Samsung Wallet", "mainImg": "https://../main.png" *Refer to Wallet Cards for each type }, "localization": [{ "language": "ko", "attributes": { "title": "삼성 월렛" } }] }] } }
To ensure secure transmission of card data, it must be tokenized in JWT format. For this purpose, you will require the certificate obtained using the partner's email account when signing up for the partner portal.
For detailed information on secure data tokenization, please refer to the Security chapter.
*Image resources provided by URLs can be cached. Therefore, in order for the image resource to be replaced immediately, the corresponding URL path must be changed.
Relying Party
‘relyingparty’ cards are used for authentication of verifier.
When you create the "Verify with Samsung Wallet" button, you need to create cdata.
This is the data spec included when creating the cdata of relayingparty type.
Wallet card type | Wallet card subtype |
---|---|
relyingparty | others |
Type | Value | Description | |||
---|---|---|---|---|---|
attributes{fields} | 1 | logoImage | String(256) | Required | URL of the logo image. The file size must not be greater than 256 KB. |
2 | logoImage.darkUrl | String(256) | Required | URL of the logo image. The file size must not be greater than 256 KB. |
|
3 | logoImage.lightUrl | String(256) | Required | URL of the logo image. The file size must not be greater than 256 KB. |
|
4 | fontColor | String(8) | Optional | Display Color of the font during user authentication. | |
5 | providerName | String(32) | Required | Display name during user authentication | |
6 | clientType | String(32) | Required | Information on whether the client operates as an App or Web. | |
7 | clientPackageName | String(32) | Optional | Package name when the client operates as an app. |
Example
{
"card": {
"type": "relyingparty",
"subType": "others",
"data": [
{
"refId": "0613001",
"createdAt": 1686657600000,
"updatedAt ": 1686657600000,
"language": "en",
"attributes": {
"logoImage": "https://samsung.com",
"logoImage.darkUrl": "https://dark.samsung.com",
"logoImage.lightUrl": "https://light.samsung.com",
"fontColor": "black",
"providerName": "Company Name",
"clientType": "app",
"clientPackageName": "com.companyName.app"
}
}
]
}
}