Skip to content

Commit 45724d6

Browse files
committed
Merge branch 'develop'
2 parents dac1818 + 71460dc commit 45724d6

29 files changed

+2158
-59
lines changed

.github/workflows/bits&bytes_develop_debug.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ jobs:
3434
run: |
3535
sed -i '/signingConfigs {/,/}/c\signingConfigs {\n\n getByName("debug") {\n keyAlias = "${{ secrets.BITS_N_BYTES_DEBUG_KEY_ALIAS }}"\n keyPassword = "${{ secrets.BITS_N_BYTES_DEBUG_KEY_PASSWORD }}"\n storeFile = file("keystore/debug")\n storePassword = "${{ secrets.BITS_N_BYTES_DEBUG_STORE_PASSWORD }}"\n }\n' app/build.gradle.kts
3636
37-
- name: Add API key tags to strings.xml resource (demo.variant)
37+
- name: Add API keys/domains tags to strings.xml resource (demo.variant)
3838
run: |
3939
sed -i '/<\/resources>/i \ <string name="com.sap.cxcdc.apikey">${{ secrets.BITS_N_BYTES_DEMO_API_KEY }}</string>' app/src/main/res/values/strings.xml
40+
sed -i '/<\/resources>/i \ <string name="com.sap.cxcdc.domain">us1.gigya.com</string>' app/src/main/res/values/strings.xml
4041
sed -i '/<\/resources>/i \ <string name="com.sap.cxcdc.apikey">${{ secrets.BITS_N_BYTES_VARIANT_API_KEY }}</string>' app/src/variant/res/values/strings.xml
42+
sed -i '/<\/resources>/i \ <string name="com.sap.cxcdc.domain">us1.gigya.com</string>' app/src/variant/res/values/strings.xml
4143
4244
- name: Add Google required resources to strings.xml resource (actual value for demo only)
4345
run: |
@@ -85,4 +87,9 @@ jobs:
8587
uses: actions/upload-artifact@v4
8688
with:
8789
name: variant-debug-apk
88-
path: app/build/outputs/apk/variant/debug/bits-n-bytes-app-variant-debug.apk
90+
path: app/build/outputs/apk/variant/debug/bits-n-bytes-app-variant-debug.apk
91+
92+
- name: Upload to Soucelabs
93+
working-directory: app/build/outputs/apk/demo/debug/
94+
run: |
95+
curl -u '${{ secrets.SAUCE_USERNAME }}:${{ secrets.SAUCE_ACCESS_KEY }}' --location --request POST 'https://api.eu-central-1.saucelabs.com/v1/storage/upload' --form payload=@'bits-n-bytes-app-demo-debug.apk' --form name='bits-n-bytes-app-demo-debug.apk' --form description='e2e Android test app' --silent --show-error
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Build Bits & Bytes demo for Push TFA (Develop)
2+
3+
on:
4+
push:
5+
branches:
6+
- develop
7+
workflow_dispatch:
8+
inputs:
9+
branch:
10+
description: 'Branch to run the workflow on'
11+
default: 'develop'
12+
required: true
13+
14+
jobs:
15+
build:
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v3
21+
with:
22+
ref: ${{ github.event.inputs.branch }}
23+
24+
- name: Set up JDK 17
25+
uses: actions/setup-java@v3
26+
with:
27+
distribution: 'zulu'
28+
java-version: '17'
29+
30+
- name: Update signingConfigs in build.gradle.kts
31+
run: |
32+
sed -i '/signingConfigs {/,/}/c\signingConfigs {\n\n getByName("debug") {\n keyAlias = "${{ secrets.BITS_N_BYTES_DEBUG_KEY_ALIAS }}"\n keyPassword = "${{ secrets.BITS_N_BYTES_DEBUG_KEY_PASSWORD }}"\n storeFile = file("keystore/debug")\n storePassword = "${{ secrets.BITS_N_BYTES_DEBUG_STORE_PASSWORD }}"\n }\n' app/build.gradle.kts
33+
34+
- name: Add API key/domain tags to strings.xml resource (demo only)
35+
run: |
36+
sed -i '/<\/resources>/i \ <string name="com.sap.cxcdc.apikey">${{ secrets.BITS_N_BYTES_DEMO_API_KEY_PUSH_TFA }}</string>' app/src/main/res/values/strings.xml
37+
sed -i '/<\/resources>/i \ <string name="com.sap.cxcdc.domain">us1.gigya.com</string>' app/src/main/res/values/strings.xml
38+
39+
- name: Add Google required resources to strings.xml resource (actual value for demo only)
40+
run: |
41+
sed -i '/<\/resources>/i \ <string name="google_server_client_id">${{ secrets.GOOGLE_WEB_SERVER_CLIENT_ID }}</string>' app/src/main/res/values/strings.xml
42+
43+
- name: Add Facebook required resources to strings.xml resource (actual value for demo only)
44+
run: |
45+
sed -i '/<\/resources>/i \ <string name="facebook_app_id">FB_APP_ID_HERE</string>' app/src/main/res/values/strings.xml
46+
sed -i '/<\/resources>/i \ <string name="fb_login_protocol_scheme">FB_LOGIN_PROTOCOL_SCHEME_HERE</string>' app/src/main/res/values/strings.xml
47+
sed -i '/<\/resources>/i \ <string name="facebook_client_token">FB_CLIENT_TOKEN_HERE</string>' app/src/main/res/values/strings.xml
48+
sed -i '/<\/resources>/i \ <string name="facebook_app_id">FB_APP_ID_HERE</string>' app/src/variant/res/values/strings.xml
49+
sed -i '/<\/resources>/i \ <string name="fb_login_protocol_scheme">FB_LOGIN_PROTOCOL_SCHEME_HERE</string>' app/src/variant/res/values/strings.xml
50+
sed -i '/<\/resources>/i \ <string name="facebook_client_token">FB_CLIENT_TOKEN_HERE</string>' app/src/variant/res/values/strings.xml
51+
52+
- name: Add Line required resources to strings.xml resource (actual value for demo only)
53+
run: |
54+
sed -i '/<\/resources>/i \ <string name="line_channel_id">LINE_CHANNEL_ID_HERE</string>' app/src/main/res/values/strings.xml
55+
56+
- name: Add WeChat required resources to strings.xml resource (actual value for demo only)
57+
run: |
58+
sed -i '/<\/resources>/i \ <string name="wechat_app_id">WECHAT_APP_ID_HERE</string>' app/src/main/res/values/strings.xml
59+
60+
- name: Create file for google-services.json (Demo)
61+
run: echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > app/google-services.json
62+
63+
- name: Build Debug APK
64+
run: ./gradlew assembleDemoDebug
65+
66+
- name: Rename Demo APK file
67+
run: mv app/build/outputs/apk/demo/debug/app-demo-debug.apk app/build/outputs/apk/demo/debug/bits-n-bytes-app-demo-debug.apk
68+
69+
- name: Upload Demo APK
70+
uses: actions/upload-artifact@v4
71+
with:
72+
name: demo-debug-apk
73+
path: app/build/outputs/apk/demo/debug/bits-n-bytes-app-demo-debug.apk

app/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,6 @@ dependencies {
116116
implementation(libs.googleid)
117117

118118
implementation(platform(libs.firebase.bom))
119-
119+
implementation(libs.firebase.messaging)
120+
implementation(libs.accompanist.permissions)
120121
}

app/src/main/AndroidManifest.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@
3434

3535
</activity>
3636

37+
<service
38+
android:name=".cdc.AppMessagingService"
39+
android:exported="false">
40+
<intent-filter>
41+
<action android:name="com.google.firebase.MESSAGING_EVENT" />
42+
</intent-filter>
43+
</service>
44+
45+
<receiver
46+
android:name="com.sap.cdc.android.sdk.auth.notifications.CDCNotificationReceiver"
47+
android:exported="false">
48+
<intent-filter>
49+
<action android:name="approve" />
50+
<action android:name="deny" />
51+
</intent-filter>
52+
</receiver>
53+
3754
<activity
3855
android:name="com.sap.cdc.android.sdk.auth.provider.activity.WebLoginActivity"
3956
android:allowTaskReparenting="true"

app/src/main/java/com/sap/cdc/bitsnbytes/MainApplication.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class MainApplication : Application() {
1515

1616
// Allow WebView debugging.
1717
CDCDebuggable.debugLogging(true)
18-
CDCDebuggable.httpLogging(false)
18+
CDCDebuggable.httpLogging(true)
1919
CDCDebuggable.setWebViewDebuggable(true)
2020
}
2121
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.sap.cdc.bitsnbytes.cdc
2+
3+
import com.google.firebase.messaging.FirebaseMessagingService
4+
import com.google.firebase.messaging.RemoteMessage
5+
import com.sap.cdc.android.sdk.CDCMessageEventBus
6+
import com.sap.cdc.android.sdk.MessageEvent
7+
8+
9+
class AppMessagingService() : FirebaseMessagingService() {
10+
11+
override fun onNewToken(token: String) {
12+
CDCMessageEventBus.emitMessageEvent(MessageEvent.EventWithToken(token))
13+
14+
}
15+
16+
override fun onMessageReceived(message: RemoteMessage) {
17+
CDCMessageEventBus.emitMessageEvent(MessageEvent.EventWithRemoteMessageData(message.data))
18+
}
19+
20+
}

app/src/main/java/com/sap/cdc/bitsnbytes/cdc/IdentityServiceRepository.kt

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@ package com.sap.cdc.bitsnbytes.cdc
33
import android.content.Context
44
import android.util.Log
55
import androidx.activity.ComponentActivity
6+
import com.google.android.gms.tasks.OnCompleteListener
7+
import com.google.firebase.messaging.FirebaseMessaging
8+
import com.sap.cdc.android.sdk.CDCMessageEventBus
9+
import com.sap.cdc.android.sdk.MessageEvent
610
import com.sap.cdc.android.sdk.auth.AuthenticationService
711
import com.sap.cdc.android.sdk.auth.IAuthResponse
812
import com.sap.cdc.android.sdk.auth.ResolvableContext
13+
import com.sap.cdc.android.sdk.auth.notifications.IFCMTokenRequest
914
import com.sap.cdc.android.sdk.auth.provider.IAuthenticationProvider
15+
import com.sap.cdc.android.sdk.auth.provider.IPasskeysAuthenticationProvider
1016
import com.sap.cdc.android.sdk.auth.provider.SSOAuthenticationProvider
1117
import com.sap.cdc.android.sdk.auth.provider.WebAuthenticationProvider
1218
import com.sap.cdc.android.sdk.auth.session.Session
@@ -47,7 +53,21 @@ class IdentityServiceRepository private constructor(context: Context) {
4753
/**
4854
* Initialize authentication service.
4955
*/
50-
var authenticationService = AuthenticationService(siteConfig)
56+
var authenticationService = AuthenticationService(siteConfig).registerForPushAuthentication(
57+
object : IFCMTokenRequest {
58+
override fun requestFCMToken() {
59+
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
60+
if (!task.isSuccessful) {
61+
return@OnCompleteListener
62+
}
63+
64+
// Get new FCM registration token
65+
val token = task.result
66+
CDCMessageEventBus.emitMessageEvent(MessageEvent.EventWithToken(token))
67+
})
68+
}
69+
}
70+
)
5171

5272
/**
5373
* Authentication providers map.
@@ -56,7 +76,6 @@ class IdentityServiceRepository private constructor(context: Context) {
5676
private var authenticationProviderMap: MutableMap<String, IAuthenticationProvider> =
5777
mutableMapOf()
5878

59-
6079
init {
6180
// Using session migrator to try and migrate an existing session in an application using old versions
6281
// of the gigya-android-sdk library.
@@ -219,6 +238,37 @@ class IdentityServiceRepository private constructor(context: Context) {
219238
parameters: MutableMap<String, String>
220239
): IAuthResponse = authenticationService.authenticate().otpSendCode(parameters)
221240

241+
suspend fun createPasskey(
242+
passkeysAuthenticationProvider: IPasskeysAuthenticationProvider
243+
): IAuthResponse {
244+
return authenticationService.authenticate()
245+
.createPasskey(passkeysAuthenticationProvider)
246+
}
247+
248+
suspend fun passkeySignIn(
249+
passkeysAuthenticationProvider: IPasskeysAuthenticationProvider
250+
): IAuthResponse {
251+
return authenticationService.authenticate()
252+
.passkeySignIn(passkeysAuthenticationProvider)
253+
}
254+
255+
suspend fun clearPasskey(
256+
passkeysAuthenticationProvider: IPasskeysAuthenticationProvider
257+
): IAuthResponse {
258+
return authenticationService.authenticate()
259+
.clearPasskey(passkeysAuthenticationProvider)
260+
}
261+
262+
//region PUSH
263+
264+
suspend fun optInForPushTFA(): IAuthResponse {
265+
return authenticationService.tfa().optInForPushAuthentication()
266+
}
267+
268+
suspend fun optInForPushAuth(): IAuthResponse {
269+
return authenticationService.authenticate().registerForAuthPushNotifications()
270+
}
271+
222272
//endregion
223273

224274
//region RESOLVE INTERRUPTIONS
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.sap.cdc.bitsnbytes.cdc
2+
3+
import android.annotation.SuppressLint
4+
import androidx.activity.ComponentActivity
5+
import androidx.credentials.CreateCredentialResponse
6+
import androidx.credentials.CreatePublicKeyCredentialRequest
7+
import androidx.credentials.CreatePublicKeyCredentialResponse
8+
import androidx.credentials.CredentialManager
9+
import androidx.credentials.GetCredentialRequest
10+
import androidx.credentials.GetCredentialResponse
11+
import androidx.credentials.GetPublicKeyCredentialOption
12+
import androidx.credentials.PublicKeyCredential
13+
import com.sap.cdc.android.sdk.CDCDebuggable
14+
import com.sap.cdc.android.sdk.auth.provider.IPasskeysAuthenticationProvider
15+
import java.lang.ref.WeakReference
16+
17+
class PasskeysAuthenticationProvider(
18+
private val weakActivity: WeakReference<ComponentActivity>? = null
19+
) : IPasskeysAuthenticationProvider {
20+
21+
companion object {
22+
23+
const val LOG_TAG = "PasskeysAuthenticationProvider"
24+
}
25+
26+
private val credentialManager by lazy(LazyThreadSafetyMode.PUBLICATION) {
27+
weakActivity?.get()?.let {
28+
CredentialManager.create(it)
29+
}
30+
}
31+
32+
@SuppressLint("PublicKeyCredential")
33+
override suspend fun createPasskey(requestJson: String): String? {
34+
if (weakActivity?.get() == null) {
35+
return null
36+
}
37+
try {
38+
val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
39+
// Contains the request in JSON format. Uses the standard WebAuthn
40+
// web JSON spec.
41+
requestJson = requestJson,
42+
// Defines whether you prefer to use only immediately available credentials,
43+
// not hybrid credentials, to fulfill this request. This value is false
44+
// by default.
45+
preferImmediatelyAvailableCredentials = false,
46+
)
47+
// Use the createCredential method to create the passkey.
48+
val result: CreateCredentialResponse? = credentialManager?.createCredential(
49+
request = createPublicKeyCredentialRequest,
50+
context = weakActivity.get()!!,
51+
)
52+
return if (result is CreatePublicKeyCredentialResponse) {
53+
result.registrationResponseJson
54+
} else {
55+
null
56+
}
57+
} catch (e: Exception) {
58+
CDCDebuggable.log(LOG_TAG, e.message ?: "Error creating passkey")
59+
return null
60+
}
61+
}
62+
63+
override suspend fun getPasskey(requestJson: String): String? {
64+
if (weakActivity?.get() == null) {
65+
return null
66+
}
67+
try {
68+
// Get passkeys from the user's public key credential provider.
69+
val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
70+
requestJson = requestJson,
71+
)
72+
73+
val getCredRequest = GetCredentialRequest(
74+
listOf(getPublicKeyCredentialOption)
75+
)
76+
77+
val result: GetCredentialResponse? = credentialManager?.getCredential(
78+
// Use an activity-based context to avoid undefined system UI
79+
// launching behavior.
80+
context = weakActivity.get()!!,
81+
request = getCredRequest
82+
)
83+
84+
val credential = result?.credential
85+
return if (credential is PublicKeyCredential) {
86+
credential.authenticationResponseJson
87+
} else {
88+
null
89+
}
90+
} catch (e: Exception) {
91+
CDCDebuggable.log(LOG_TAG, e.message ?: "Error getting passkey")
92+
return null
93+
}
94+
}
95+
96+
}

0 commit comments

Comments
 (0)