From 8287f74101b20685bf7d6faffcb5c982711ff10c Mon Sep 17 00:00:00 2001 From: David Antoon Date: Fri, 7 Jul 2023 21:24:00 +0300 Subject: [PATCH 01/18] FR-12455 - Add support for logout before login --- .../android/utils/AuthorizeUrlGenerator.kt | 16 +++++++++---- .../com/frontegg/android/utils/Constants.kt | 24 +++---------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/android/src/main/java/com/frontegg/android/utils/AuthorizeUrlGenerator.kt b/android/src/main/java/com/frontegg/android/utils/AuthorizeUrlGenerator.kt index c229615..316922b 100644 --- a/android/src/main/java/com/frontegg/android/utils/AuthorizeUrlGenerator.kt +++ b/android/src/main/java/com/frontegg/android/utils/AuthorizeUrlGenerator.kt @@ -40,15 +40,15 @@ class AuthorizeUrlGenerator { } - fun generate(): String { + fun generate(): Pair { val nonce = createRandomString() val codeVerifier = createRandomString() val codeChallenge = generateCodeChallenge(codeVerifier) val credentialManager = FronteggApp.getInstance().credentialManager credentialManager.save(CredentialKeys.CODE_VERIFIER, codeVerifier); - val redirectUrl = Constants.oauthCallbackUrl(baseUrl) + val authorizeUrlBuilder = Uri.Builder() .encodedPath(baseUrl) .appendEncodedPath("oauth/authorize") @@ -61,8 +61,16 @@ class AuthorizeUrlGenerator { .appendQueryParameter("nonce", nonce) - val authorizeUrl = authorizeUrlBuilder.build() - return authorizeUrl.toString() + val url = authorizeUrlBuilder.build().toString() + Log.d(TAG, "Generated url: $url") + + val authorizeUrl = Uri.Builder() + .encodedPath(baseUrl) + .appendEncodedPath("frontegg/oauth/logout") + .appendQueryParameter("post_logout_redirect_uri", url) + .build().toString() + + return Pair(authorizeUrl, codeVerifier) } diff --git a/android/src/main/java/com/frontegg/android/utils/Constants.kt b/android/src/main/java/com/frontegg/android/utils/Constants.kt index 97cb375..7013f84 100644 --- a/android/src/main/java/com/frontegg/android/utils/Constants.kt +++ b/android/src/main/java/com/frontegg/android/utils/Constants.kt @@ -1,5 +1,7 @@ package com.frontegg.android.utils +import com.frontegg.android.FronteggApp + class ApiConstants { companion object { const val me: String = "identity/resources/users/v2/me" @@ -12,30 +14,10 @@ class ApiConstants { class Constants { companion object { - val oauthUrls = listOf( - "https://www.facebook.com", - "https://accounts.google.com", - "https://github.com/login/oauth/authorize", - "https://login.microsoftonline.com", - "https://slack.com/openid/connect/authorize", - "https://appleid.apple.com", - "https://www.linkedin.com/oauth/" - ) - - - val successLoginRoutes = listOf( - "/oauth/account/social/success", - ) - val loginRoutes = listOf( - "/oauth/account/", - ) fun oauthCallbackUrl(baseUrl: String): String { - return "$baseUrl/oauth/mobile/callback" + return "$baseUrl/android/${FronteggApp.getInstance().packageName}/callback" } - fun socialLoginRedirectUrl(baseUrl: String): String { - return "$baseUrl/oauth/account/social/success" - } } } \ No newline at end of file From 8142573f304dec55cf14a8e195faac4ccbcc503f Mon Sep 17 00:00:00 2001 From: David Antoon Date: Fri, 7 Jul 2023 21:25:06 +0300 Subject: [PATCH 02/18] FR-12453 - Remove unused code --- .../android/AbstractFronteggActivity.kt | 96 -------- .../java/com/frontegg/android/FronteggAuth.kt | 7 +- .../com/frontegg/android/FronteggWebClient.kt | 224 ------------------ .../com/frontegg/android/FronteggWebView.kt | 40 ---- .../res/layout/activity_frontegg_login.xml | 20 +- .../res/layout/activity_frontegg_logout.xml | 3 +- app/src/main/AndroidManifest.xml | 30 +-- .../com/frontegg/demo/FronteggActivity.kt | 15 +- 8 files changed, 35 insertions(+), 400 deletions(-) delete mode 100644 android/src/main/java/com/frontegg/android/AbstractFronteggActivity.kt delete mode 100644 android/src/main/java/com/frontegg/android/FronteggWebClient.kt delete mode 100644 android/src/main/java/com/frontegg/android/FronteggWebView.kt diff --git a/android/src/main/java/com/frontegg/android/AbstractFronteggActivity.kt b/android/src/main/java/com/frontegg/android/AbstractFronteggActivity.kt deleted file mode 100644 index cfc4daa..0000000 --- a/android/src/main/java/com/frontegg/android/AbstractFronteggActivity.kt +++ /dev/null @@ -1,96 +0,0 @@ -package com.frontegg.android - -import android.app.Activity -import android.os.Bundle -import android.util.Log -import android.view.View -import android.widget.LinearLayout -import com.frontegg.android.utils.AuthorizeUrlGenerator -import com.frontegg.android.utils.NullableObject -import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.functions.Consumer - -abstract class AbstractFronteggActivity : Activity() { - - companion object { - private val TAG = AbstractFronteggActivity::class.java.simpleName - } - - lateinit var webView: FronteggWebView - var webViewUrl: String? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_frontegg_login) - overridePendingTransition(R.anim.fadein, R.anim.fadeout) - - webView = findViewById(R.id.custom_webview) - - val authorizeUrl = AuthorizeUrlGenerator() - val url = authorizeUrl.generate() - - webViewUrl = intent.data?.toString() ?: url - - loaderLayout = findViewById(R.id.loaderView) - val loaderView = layoutInflater.inflate(FronteggApp.getInstance().loaderId, null) - - loaderLayout!!.addView(loaderView) - - - if (!FronteggAuth.instance.initializing.value - && !FronteggAuth.instance.isAuthenticated.value - ) { - webView.loadUrl(webViewUrl!!) - webViewUrl = null - } - - } - - private val disposables: ArrayList = arrayListOf() - private var loaderLayout: LinearLayout? = null - - private val showLoaderConsumer: Consumer> = Consumer { - runOnUiThread { - loaderLayout?.visibility = if (it.value) View.VISIBLE else View.GONE - } - } - - private val isAuthenticatedConsumer: Consumer> = Consumer { - Log.d(TAG, "isAuthenticatedConsumer: ${it.value}") - if (it.value) { - runOnUiThread { - navigateToAuthenticated() - } - } - } - - - private val isInitializingConsumer: Consumer> = Consumer { - if (!it.value && !FronteggAuth.instance.isAuthenticated.value) { - runOnUiThread { - if(webViewUrl != null) { - webView.loadUrl(webViewUrl!!) - } - } - } - } - - - override fun onResume() { - super.onResume() - disposables.add(FronteggAuth.instance.showLoader.subscribe(this.showLoaderConsumer)) - disposables.add(FronteggAuth.instance.isAuthenticated.subscribe(this.isAuthenticatedConsumer)) - disposables.add(FronteggAuth.instance.initializing.subscribe(this.isInitializingConsumer)) - - } - - override fun onPause() { - super.onPause() - disposables.forEach { - it.dispose() - } - } - - - abstract fun navigateToAuthenticated() -} \ No newline at end of file diff --git a/android/src/main/java/com/frontegg/android/FronteggAuth.kt b/android/src/main/java/com/frontegg/android/FronteggAuth.kt index 70dc406..2183b74 100644 --- a/android/src/main/java/com/frontegg/android/FronteggAuth.kt +++ b/android/src/main/java/com/frontegg/android/FronteggAuth.kt @@ -46,7 +46,6 @@ class FronteggAuth( val isLoading: ObservableValue = ObservableValue(true) val initializing: ObservableValue = ObservableValue(true) val showLoader: ObservableValue = ObservableValue(true) - val externalLink: ObservableValue = ObservableValue(false) var pendingAppLink: String? = null var timer: Timer = Timer() var refreshTaskRunner: TimerTask? = null @@ -131,7 +130,7 @@ class FronteggAuth( this.initializing.value = false } - fun handleHostedLoginCallback(webView: FronteggWebView, code: String): Boolean { + fun handleHostedLoginCallback( code: String): Boolean { val codeVerifier = credentialManager.get(CredentialKeys.CODE_VERIFIER) val redirectUrl = Constants.oauthCallbackUrl(baseUrl) @@ -141,9 +140,7 @@ class FronteggAuth( if (data != null) { setCredentials(data.access_token, data.refresh_token) } else { - launch(Dispatchers.Main) { - webView.loadOauthAuthorize() - } + // TODO: handle error } } diff --git a/android/src/main/java/com/frontegg/android/FronteggWebClient.kt b/android/src/main/java/com/frontegg/android/FronteggWebClient.kt deleted file mode 100644 index 67850f4..0000000 --- a/android/src/main/java/com/frontegg/android/FronteggWebClient.kt +++ /dev/null @@ -1,224 +0,0 @@ -package com.frontegg.android - -import android.content.Context -import android.content.Intent -import android.graphics.Bitmap -import android.net.Uri -import android.net.UrlQuerySanitizer -import android.util.Log -import android.webkit.* -import com.frontegg.android.utils.AuthorizeUrlGenerator -import com.frontegg.android.utils.Constants.Companion.loginRoutes -import com.frontegg.android.utils.Constants.Companion.oauthUrls -import com.frontegg.android.utils.Constants.Companion.socialLoginRedirectUrl -import com.frontegg.android.utils.Constants.Companion.successLoginRoutes - - -class FronteggWebClient(val context: Context) : WebViewClient() { - companion object { - private val TAG = FronteggWebClient::class.java.simpleName - } - - override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { - super.onPageStarted(view, url, favicon) - Log.d(TAG, "onPageStarted $url") - FronteggAuth.instance.isLoading.value = true - - } - - override fun onPageFinished(view: WebView?, url: String?) { - super.onPageFinished(view, url) - Log.d(TAG, "onPageFinished $url") - when (getOverrideUrlType(Uri.parse(url))) { - OverrideUrlType.HostedLoginCallback -> - FronteggAuth.instance.isLoading.value = true - OverrideUrlType.Unknown, - OverrideUrlType.loginRoutes -> - FronteggAuth.instance.isLoading.value = false - else -> - FronteggAuth.instance.isLoading.value = true - } - } - - override fun onReceivedError( - view: WebView?, - request: WebResourceRequest?, - error: WebResourceError? - ) { - super.onReceivedError(view, request, error) - } - - override fun onReceivedHttpError( - view: WebView?, - request: WebResourceRequest?, - errorResponse: WebResourceResponse? - ) { - if(view!!.url == request?.url.toString()){ - Log.d(TAG, "onReceivedHttpError: Direct load url, ${request?.url?.path}") - }else { - Log.d(TAG, "onReceivedHttpError: HTTP api call, ${request?.url?.path}") - } - super.onReceivedHttpError(view, request, errorResponse) - } - - private fun setUriParameter(uri: Uri, key: String, newValue: String): Uri? { - val params: Set = uri.queryParameterNames - val newUri: Uri.Builder = uri.buildUpon().clearQuery() - var paramExist = false - for (param in params) { - if (param == key) { - paramExist = true - newUri.appendQueryParameter(param, newValue) - } else { - newUri.appendQueryParameter(param, uri.getQueryParameter(param)) - } - } - if (!paramExist) { - newUri.appendQueryParameter(key, newValue) - } - return newUri.build() - } - - enum class OverrideUrlType { - HostedLoginCallback, - SocialLoginRedirectToBrowser, - SocialOauthPreLogin, - loginRoutes, - internalRoutes, - Unknown - } - - private fun getOverrideUrlType(url: Uri): OverrideUrlType { - val urlPath = url.path - if (urlPath != null && url.toString().startsWith(FronteggApp.getInstance().baseUrl)) { - - return when (urlPath) { - "/oauth/mobile/callback" -> OverrideUrlType.HostedLoginCallback - else -> { - - if (urlPath.startsWith("/frontegg/identity/resources/auth/v2/user/sso/default") - && urlPath.endsWith("/prelogin") - ) { - return OverrideUrlType.SocialOauthPreLogin - } - if (successLoginRoutes.find { u -> urlPath.startsWith(u) } != null) { - OverrideUrlType.internalRoutes - } else if (loginRoutes.find { u -> urlPath.startsWith(u) } != null) { - OverrideUrlType.loginRoutes - } else { - OverrideUrlType.internalRoutes - } - } - } - } - - if (oauthUrls.find { u -> url.toString().startsWith(u) } != null) { - return OverrideUrlType.SocialLoginRedirectToBrowser - } - - - return OverrideUrlType.Unknown - } - - override fun shouldOverrideUrlLoading( - view: WebView?, - request: WebResourceRequest? - ): Boolean { - val url = request?.url - - if (url != null) { - when (getOverrideUrlType(request.url)) { - OverrideUrlType.HostedLoginCallback -> { - return handleHostedLoginCallback(view, request.url?.query) - } - OverrideUrlType.SocialLoginRedirectToBrowser -> { - FronteggAuth.instance.isLoading.value = true - val browserIntent = Intent(Intent.ACTION_VIEW, url) - context.startActivity(browserIntent) - return true - } - else -> { - return super.shouldOverrideUrlLoading(view, request) - } - } - } else { - return super.shouldOverrideUrlLoading(view, request) - } - - } - - - override fun onLoadResource(view: WebView?, url: String?) { - if (url == null || view == null) { - super.onLoadResource(view, url) - return - } - - val uri = Uri.parse(url) - val urlType = getOverrideUrlType(uri) - val query = uri.query - when (urlType) { - OverrideUrlType.HostedLoginCallback -> { - if (handleHostedLoginCallback(view, query)) { - return - } - super.onLoadResource(view, url) - } - OverrideUrlType.SocialOauthPreLogin -> { - if (setSocialLoginRedirectUri(view, uri)) { - return - } - super.onLoadResource(view, url) - } - else -> { - super.onLoadResource(view, url) - } - } - } - - private fun setSocialLoginRedirectUri(webView: WebView, uri: Uri): Boolean { - Log.d(TAG, "setSocialLoginRedirectUri setting redirect uri for social login") - - if (uri.getQueryParameter("redirectUri") != null) { - Log.d(TAG, "redirectUri exist, forward navigation to webView") - return false - } - val baseUrl = FronteggApp.getInstance().baseUrl - val oauthRedirectUri = socialLoginRedirectUrl(baseUrl) - val newUri = setUriParameter(uri, "redirectUri", oauthRedirectUri) - webView.loadUrl(newUri.toString()) - return true - } - - - private fun handleHostedLoginCallback(webView: WebView?, query: String?): Boolean { - Log.d(TAG, "handleHostedLoginCallback received query: $query") - if (query == null || webView == null) { - Log.d( - TAG, - "handleHostedLoginCallback failed of nullable value, query: $query, webView: $webView" - ) - return false - } - - val querySanitizer = UrlQuerySanitizer() - querySanitizer.allowUnregisteredParamaters = true - querySanitizer.parseQuery(query) - val code = querySanitizer.getValue("code") - - if (code == null) { - Log.d(TAG, "handleHostedLoginCallback failed of nullable code value, query: $query") - return false - } - - if (FronteggAuth.instance.handleHostedLoginCallback(webView as FronteggWebView, code)) { - return true - } - - val authorizeUrl = AuthorizeUrlGenerator() - val url = authorizeUrl.generate() - webView.loadUrl(url) - return false - } - -} \ No newline at end of file diff --git a/android/src/main/java/com/frontegg/android/FronteggWebView.kt b/android/src/main/java/com/frontegg/android/FronteggWebView.kt deleted file mode 100644 index 0f894a8..0000000 --- a/android/src/main/java/com/frontegg/android/FronteggWebView.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.frontegg.android - -import android.annotation.SuppressLint -import android.content.Context -import android.util.AttributeSet -import android.webkit.WebView -import com.frontegg.android.utils.AuthorizeUrlGenerator -import java.util.* - - -open class FronteggWebView : WebView { - - constructor(context: Context) : super(context) { - initView(context) - } - - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { - initView(context) - } - - @SuppressLint("SetJavaScriptEnabled") - fun initView(context: Context) { - settings.javaScriptEnabled = true - settings.useWideViewPort = true - settings.loadWithOverviewMode = true - settings.domStorageEnabled = true - - webViewClient = FronteggWebClient(context) - } - - fun loadOauthAuthorize() { - - val authorizeUrl = AuthorizeUrlGenerator() - val url = authorizeUrl.generate() - this.loadUrl(url) - } -} - - - diff --git a/android/src/main/res/layout/activity_frontegg_login.xml b/android/src/main/res/layout/activity_frontegg_login.xml index a6efc94..f8de77b 100644 --- a/android/src/main/res/layout/activity_frontegg_login.xml +++ b/android/src/main/res/layout/activity_frontegg_login.xml @@ -2,14 +2,12 @@ + android:layout_height="match_parent"> - - + + + + - - - - + + + + \ No newline at end of file diff --git a/android/src/main/res/layout/activity_frontegg_logout.xml b/android/src/main/res/layout/activity_frontegg_logout.xml index 4d364e3..6b98a4b 100644 --- a/android/src/main/res/layout/activity_frontegg_logout.xml +++ b/android/src/main/res/layout/activity_frontegg_logout.xml @@ -2,8 +2,7 @@ + android:layout_height="match_parent"> - @@ -34,22 +32,26 @@ - - - + + - - - + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/frontegg/demo/FronteggActivity.kt b/app/src/main/java/com/frontegg/demo/FronteggActivity.kt index 90ad0c6..8dd6ed4 100644 --- a/app/src/main/java/com/frontegg/demo/FronteggActivity.kt +++ b/app/src/main/java/com/frontegg/demo/FronteggActivity.kt @@ -1,12 +1,11 @@ package com.frontegg.demo -import android.content.Intent -import com.frontegg.android.AbstractFronteggActivity -class FronteggActivity: AbstractFronteggActivity() { - override fun navigateToAuthenticated() { - val intent = Intent(this, MainActivity::class.java) - startActivity(intent) - finish() - } +class FronteggActivity() { +// fun navigateToAuthenticated() { +// val intent = Intent(this, MainActivity::class.java) +// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) +//// startActivity(intent) +//// finish() +// } } \ No newline at end of file From dc0dfb56fd7abf1621f8d91b3e59e57ac1aa6b85 Mon Sep 17 00:00:00 2001 From: David Antoon Date: Fri, 7 Jul 2023 21:26:40 +0300 Subject: [PATCH 03/18] FR-12456 - save code verifier in credential manager --- android/build.gradle | 1 + .../java/com/frontegg/android/FronteggApp.kt | 3 ++ .../android/services/Authentication.kt | 7 ++++ .../android/utils/AuthorizeUrlGenerator.kt | 8 ++-- app/build.gradle | 41 ++++++++++++++----- 5 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 android/src/main/java/com/frontegg/android/services/Authentication.kt diff --git a/android/build.gradle b/android/build.gradle index 538af44..6b2faba 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -54,6 +54,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.4' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + implementation "androidx.browser:browser:1.5.0" } afterEvaluate { diff --git a/android/src/main/java/com/frontegg/android/FronteggApp.kt b/android/src/main/java/com/frontegg/android/FronteggApp.kt index 3d86cd3..9dd1361 100644 --- a/android/src/main/java/com/frontegg/android/FronteggApp.kt +++ b/android/src/main/java/com/frontegg/android/FronteggApp.kt @@ -18,6 +18,7 @@ class FronteggApp private constructor( val auth: FronteggAuth val api: Api val credentialManager: CredentialManager + val packageName: String; @LayoutRes var loaderId: Int @@ -28,6 +29,8 @@ class FronteggApp private constructor( credentialManager = CredentialManager(context) api = Api(baseUrl, clientId, credentialManager) auth = FronteggAuth(baseUrl, clientId, api, credentialManager) + + packageName = context.packageName } companion object { diff --git a/android/src/main/java/com/frontegg/android/services/Authentication.kt b/android/src/main/java/com/frontegg/android/services/Authentication.kt new file mode 100644 index 0000000..b3f231a --- /dev/null +++ b/android/src/main/java/com/frontegg/android/services/Authentication.kt @@ -0,0 +1,7 @@ +package com.frontegg.android.services + + +class Authentication { + + +} \ No newline at end of file diff --git a/android/src/main/java/com/frontegg/android/utils/AuthorizeUrlGenerator.kt b/android/src/main/java/com/frontegg/android/utils/AuthorizeUrlGenerator.kt index 316922b..46f765c 100644 --- a/android/src/main/java/com/frontegg/android/utils/AuthorizeUrlGenerator.kt +++ b/android/src/main/java/com/frontegg/android/utils/AuthorizeUrlGenerator.kt @@ -8,7 +8,7 @@ import java.util.* class AuthorizeUrlGenerator { companion object { - private val TAG = AuthorizeUrlGenerator::class.java.simpleName + private val TAG = AuthorizeUrlGenerator::class.java.simpleName } private var clientId: String @@ -26,11 +26,11 @@ class AuthorizeUrlGenerator { .joinToString("") } - private fun generateCodeChallenge(codeVerifier: String):String { + private fun generateCodeChallenge(codeVerifier: String): String { val md = MessageDigest.getInstance("SHA-256") val input = codeVerifier.toByteArray() val bytes = md.digest(input) - val digest = Base64.getEncoder().encodeToString(bytes) + val digest = Base64.getEncoder().encodeToString(bytes) return digest .replace("=", "") @@ -46,7 +46,7 @@ class AuthorizeUrlGenerator { val codeChallenge = generateCodeChallenge(codeVerifier) val credentialManager = FronteggApp.getInstance().credentialManager - credentialManager.save(CredentialKeys.CODE_VERIFIER, codeVerifier); + credentialManager.save(CredentialKeys.CODE_VERIFIER, codeVerifier) val redirectUrl = Constants.oauthCallbackUrl(baseUrl) val authorizeUrlBuilder = Uri.Builder() diff --git a/app/build.gradle b/app/build.gradle index bcac581..f17db66 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,8 +3,9 @@ plugins { id 'org.jetbrains.kotlin.android' } -def fronteggDomain = "https://test.frontegg.com" -def fronteggClientId = "f7a3f13d-4cb5-4078-b785-e0189c287d4b" +def fronteggDomain = "auth.davidantoon.me" +def fronteggClientId = "b6adfe4c-d695-4c04-b95f-3ec9fd0c6cca" + android { namespace 'com.frontegg.demo' compileSdk 33 @@ -19,11 +20,11 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders = [ - "frontegg_domain":fronteggDomain, - frontegg_client_id: fronteggClientId + "frontegg_domain" : fronteggDomain, + "frontegg_client_id": fronteggClientId ] - buildConfigField "String", 'FRONTEGG_DOMAIN', "\"$fronteggDomain\"" + buildConfigField "String", 'FRONTEGG_DOMAIN', "\"https://$fronteggDomain\"" buildConfigField "String", 'FRONTEGG_CLIENT_ID', "\"$fronteggClientId\"" } @@ -43,20 +44,38 @@ android { buildFeatures { viewBinding true } - buildToolsVersion '30.0.3' + buildToolsVersion buildToolsVersion + } + dependencies { - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'androidx.core:core-ktx:1.10.0' + implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'io.reactivex.rxjava3:rxkotlin:3.0.1' - implementation 'com.google.android.material:material:1.7.0' + implementation 'com.google.android.material:material:1.8.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3' implementation 'androidx.navigation:navigation-ui-ktx:2.5.3' implementation project(path: ':android') + implementation 'androidx.browser:browser:1.5.0' + + androidTestImplementation 'org.jetbrains.kotlin:kotlin-stdlib:' + rootProject.kotlinVersion + androidTestImplementation 'androidx.test:core:' + rootProject.coreVersion + androidTestImplementation 'androidx.test:core-ktx:' + rootProject.coreVersion + androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion + androidTestImplementation 'androidx.test.ext:junit-ktx:' + rootProject.extJUnitVersion + androidTestImplementation 'androidx.test:rules:1.6.0-alpha01' + androidTestImplementation 'androidx.test:runner:1.6.0-alpha02' + androidTestImplementation 'androidx.test.espresso:espresso-core:' + rootProject.espressoVersion + androidTestImplementation 'androidx.test.espresso:espresso-intents:' + rootProject.espressoVersion + androidTestImplementation 'androidx.test.espresso:espresso-web:' + rootProject.espressoVersion + androidTestImplementation 'androidx.test:monitor:1.7.0-alpha01' + + + testImplementation 'androidx.test:core:' + rootProject.coreVersion; + testImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.4' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + } \ No newline at end of file From 54d6c9c4ba723831113911f0f2ecf44fdc0fca17 Mon Sep 17 00:00:00 2001 From: David Antoon Date: Fri, 7 Jul 2023 21:31:23 +0300 Subject: [PATCH 04/18] algin dependencies --- android/build.gradle | 4 --- app/build.gradle | 19 ++----------- .../java/com/frontegg/demo/MainActivity.kt | 27 +++++++++++++++---- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 6b2faba..48b6666 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -15,7 +15,6 @@ android { defaultConfig { minSdk 26 targetSdk 33 - versionName "1.0.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } @@ -51,9 +50,6 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.9' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1" - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.4' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' implementation "androidx.browser:browser:1.5.0" } diff --git a/app/build.gradle b/app/build.gradle index f17db66..206dc3b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -58,24 +58,9 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3' implementation 'androidx.navigation:navigation-ui-ktx:2.5.3' - implementation project(path: ':android') + implementation 'androidx.tracing:tracing:1.1.0' implementation 'androidx.browser:browser:1.5.0' - - androidTestImplementation 'org.jetbrains.kotlin:kotlin-stdlib:' + rootProject.kotlinVersion - androidTestImplementation 'androidx.test:core:' + rootProject.coreVersion - androidTestImplementation 'androidx.test:core-ktx:' + rootProject.coreVersion - androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion - androidTestImplementation 'androidx.test.ext:junit-ktx:' + rootProject.extJUnitVersion - androidTestImplementation 'androidx.test:rules:1.6.0-alpha01' - androidTestImplementation 'androidx.test:runner:1.6.0-alpha02' - androidTestImplementation 'androidx.test.espresso:espresso-core:' + rootProject.espressoVersion - androidTestImplementation 'androidx.test.espresso:espresso-intents:' + rootProject.espressoVersion - androidTestImplementation 'androidx.test.espresso:espresso-web:' + rootProject.espressoVersion - androidTestImplementation 'androidx.test:monitor:1.7.0-alpha01' - - - testImplementation 'androidx.test:core:' + rootProject.coreVersion; - testImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion + implementation project(path: ':android') testImplementation 'junit:junit:4.13.2' } \ No newline at end of file diff --git a/app/src/main/java/com/frontegg/demo/MainActivity.kt b/app/src/main/java/com/frontegg/demo/MainActivity.kt index a627509..ac68e84 100644 --- a/app/src/main/java/com/frontegg/demo/MainActivity.kt +++ b/app/src/main/java/com/frontegg/demo/MainActivity.kt @@ -1,15 +1,21 @@ package com.frontegg.demo +import android.content.Intent +import android.net.Uri import android.os.Bundle -import com.google.android.material.snackbar.Snackbar +import android.util.Log +import android.view.Menu +import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.content.ContextCompat import androidx.navigation.findNavController import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.navigateUp import androidx.navigation.ui.setupActionBarWithNavController -import android.view.Menu -import android.view.MenuItem +import com.frontegg.android.utils.AuthorizeUrlGenerator import com.frontegg.demo.databinding.ActivityMainBinding +import com.google.android.material.snackbar.Snackbar class MainActivity : AppCompatActivity() { @@ -22,15 +28,25 @@ class MainActivity : AppCompatActivity() { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + + val data = intent.data setSupportActionBar(binding.toolbar) val navController = findNavController(R.id.nav_host_fragment_content_main) appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration) + val builder = CustomTabsIntent.Builder() + builder.setShowTitle(false) + val customTabsIntent = builder.build() + + var url =AuthorizeUrlGenerator().generate().first + Log.d("TEST", url) + binding.fab.setOnClickListener { view -> - Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) - .setAction("Action", null).show() + Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).show() + + customTabsIntent.launchUrl(this, Uri.parse(url)) } } @@ -55,4 +71,5 @@ class MainActivity : AppCompatActivity() { return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() } + } \ No newline at end of file From be5c6997bcdb39fe5275dbd09387bb7c84a50c8a Mon Sep 17 00:00:00 2001 From: David Antoon Date: Fri, 7 Jul 2023 21:34:15 +0300 Subject: [PATCH 05/18] FR-12461 - add option to use credential manager outside the library --- .../java/com/frontegg/android/services/CredentialManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/frontegg/android/services/CredentialManager.kt b/android/src/main/java/com/frontegg/android/services/CredentialManager.kt index 72dddce..2b7955a 100644 --- a/android/src/main/java/com/frontegg/android/services/CredentialManager.kt +++ b/android/src/main/java/com/frontegg/android/services/CredentialManager.kt @@ -7,7 +7,7 @@ import android.util.Log import com.frontegg.android.exceptions.KeyNotFoundException import com.frontegg.android.utils.CredentialKeys -class CredentialManager(context: Context) { +open class CredentialManager(context: Context) { companion object { private const val SHARED_PREFERENCES_NAME: String = "com.frontegg.services.CredentialManager" @@ -51,7 +51,7 @@ class CredentialManager(context: Context) { * Remove all keys from shared preferences */ @SuppressLint("ApplySharedPref") - fun clear(){ + fun clear() { Log.d(TAG, "clear Frontegg shared preference ") sp.edit().clear().commit() } From 8947329629d3ebd0b9ca2a888f0d9d90f8179a1c Mon Sep 17 00:00:00 2001 From: David Antoon Date: Fri, 7 Jul 2023 21:41:40 +0300 Subject: [PATCH 06/18] - update dependency - fix gralde file --- app/build.gradle | 4 ++-- app/src/main/AndroidManifest.xml | 1 - .../main/java/com/frontegg/demo/MainActivity.kt | 11 ----------- build.gradle | 15 ++++++++++++--- gradle/wrapper/gradle-wrapper.properties | 2 +- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 206dc3b..e19990b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ android { "frontegg_client_id": fronteggClientId ] - buildConfigField "String", 'FRONTEGG_DOMAIN', "\"https://$fronteggDomain\"" + buildConfigField "String", 'FRONTEGG_DOMAIN', "\"$fronteggDomain\"" buildConfigField "String", 'FRONTEGG_CLIENT_ID', "\"$fronteggClientId\"" } @@ -59,8 +59,8 @@ dependencies { implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3' implementation 'androidx.navigation:navigation-ui-ktx:2.5.3' implementation 'androidx.tracing:tracing:1.1.0' - implementation 'androidx.browser:browser:1.5.0' implementation project(path: ':android') + testImplementation 'junit:junit:4.13.2' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4916b53..dadb9b4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,6 @@ android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyApplication" diff --git a/app/src/main/java/com/frontegg/demo/MainActivity.kt b/app/src/main/java/com/frontegg/demo/MainActivity.kt index ac68e84..e79ba01 100644 --- a/app/src/main/java/com/frontegg/demo/MainActivity.kt +++ b/app/src/main/java/com/frontegg/demo/MainActivity.kt @@ -7,8 +7,6 @@ import android.util.Log import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity -import androidx.browser.customtabs.CustomTabsIntent -import androidx.core.content.ContextCompat import androidx.navigation.findNavController import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.navigateUp @@ -29,24 +27,15 @@ class MainActivity : AppCompatActivity() { setContentView(binding.root) - val data = intent.data setSupportActionBar(binding.toolbar) val navController = findNavController(R.id.nav_host_fragment_content_main) appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration) - val builder = CustomTabsIntent.Builder() - builder.setShowTitle(false) - val customTabsIntent = builder.build() - - var url =AuthorizeUrlGenerator().generate().first - Log.d("TEST", url) - binding.fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).show() - customTabsIntent.launchUrl(this, Uri.parse(url)) } } diff --git a/build.gradle b/build.gradle index 81e7f2a..0ddbbbf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,15 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.3.1' apply false - id 'com.android.library' version '7.3.1' apply false - id 'org.jetbrains.kotlin.android' version '1.7.10' apply false + id 'com.android.application' version '7.4.2' apply false + id 'com.android.library' version '7.4.2' apply false + id 'org.jetbrains.kotlin.android' version '1.8.0' apply false } +ext { + kotlinVersion = "1.7.10" + buildToolsVersion = "32.0.0" + androidxAnnotationVersion = "1.5.0" + extTruthVersion = "1.6.0-alpha01" + coreVersion = "1.6.0-alpha01" + extJUnitVersion = "1.2.0-alpha01" + espressoVersion = "3.6.0-alpha01" +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ca3475a..51ecfd9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Nov 21 02:35:38 IST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From bebd3d3526355abea3c920093b1b08ecedccf36f Mon Sep 17 00:00:00 2001 From: David Antoon Date: Fri, 7 Jul 2023 21:47:38 +0300 Subject: [PATCH 07/18] fix build step --- .github/workflows/build.yml | 79 +++++++++++++++++++++++++++++++------ app/build.gradle | 18 ++++++++- 2 files changed, 83 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab56093..f3115c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,28 +15,81 @@ concurrency: jobs: build-and-test: name: Build And Test - runs-on: ubuntu-latest + runs-on: macos-latest steps: - - name: Checkout repo + - name: Checkout uses: actions/checkout@v3 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 +# - name: Clone Mock Server +# uses: actions/checkout@v3 +# with: +# repository: frontegg/frontegg-mock-server +# ssh-key: ${{ secrets.MOCK_SERVER_SSH_KEY }} +# ref: "master" +# path: mocker +# - name: Install Mock Server +# working-directory: mocker +# run: yarn install +# - name: Run Mock Server +# working-directory: mocker +# env: +# ANDROID_ASSOCIATED_DOMAIN_GRADLE_PATH: "${{ github.workspace }}/app/build.gradle" +# SERVER_HOSTNAME: "10.0.2.2" +# NGROCK_AUTH_TOKEN: "${{ secrets.NGROCK_AUTH_TOKEN }}" +# NGROCK_SUBDOMAIN: "frontegg-test" +# run: | +# echo "ANDROID_ASSOCIATED_DOMAIN_GRADLE_PATH: $ANDROID_ASSOCIATED_DOMAIN_GRADLE_PATH" +# echo "SERVER_HOSTNAME: $SERVER_HOSTNAME" +# echo "NGROCK_SUBDOMAIN: $NGROCK_SUBDOMAIN" +# (yarn start:mobile-mock&) +# sleep 40 + - name: Set git config run: | git config --global user.name 'github-actions' git config --global user.email 'github-actions@github.com' + - name: Setup JDK 11 uses: actions/setup-java@v3 with: java-version: 11 settings-path: ${{ github.workspace }} # location for the settings.xml file - server-id: ossrh - server-username: NEXUS_USERNAME - server-password: NEXUS_PASSWORD - gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} - gpg-passphrase: GPG_PASSPHRASE - cache: 'gradle' distribution: temurin - - name: Build Libraries - run: ./gradlew build --no-daemon + + - name: Gradle cache + uses: gradle/gradle-build-action@v2 + + - name: AVD cache + uses: actions/cache@v3 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-${{env.API_LEVEL}} + + - name: Create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + avd-name: "AndroidEmulator" + api-level: ${{env.API_LEVEL}} + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: true + script: echo "Generated AVD snapshot for caching." + + - name: Run tests + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{env.API_LEVEL}} + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: true + script: ./run-tests.sh + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-results + path: app/build/reports/androidTests/connected/ diff --git a/app/build.gradle b/app/build.gradle index e19990b..4783d66 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -61,6 +61,22 @@ dependencies { implementation 'androidx.tracing:tracing:1.1.0' implementation project(path: ':android') - testImplementation 'junit:junit:4.13.2' + + + androidTestImplementation 'org.jetbrains.kotlin:kotlin-stdlib:' + rootProject.kotlinVersion + androidTestImplementation 'androidx.test:core:' + rootProject.coreVersion + androidTestImplementation 'androidx.test:core-ktx:' + rootProject.coreVersion + androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion + androidTestImplementation 'androidx.test.ext:junit-ktx:' + rootProject.extJUnitVersion + androidTestImplementation 'androidx.test:rules:1.6.0-alpha01' + androidTestImplementation 'androidx.test:runner:1.6.0-alpha02' + androidTestImplementation 'androidx.test.espresso:espresso-core:' + rootProject.espressoVersion + androidTestImplementation 'androidx.test.espresso:espresso-intents:' + rootProject.espressoVersion + androidTestImplementation 'androidx.test.espresso:espresso-web:' + rootProject.espressoVersion + androidTestImplementation 'androidx.test:monitor:1.7.0-alpha01' + + testImplementation 'androidx.test:core:' + rootProject.coreVersion; + testImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion + testImplementation 'junit:junit:4.13.2' } \ No newline at end of file From c092e14e52dc27465628c14c93493e30e51a036d Mon Sep 17 00:00:00 2001 From: David Antoon Date: Fri, 7 Jul 2023 21:50:13 +0300 Subject: [PATCH 08/18] add API_LEVEL --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3115c9..90c74e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,7 @@ on: env: CI: true LANG: en_US.UTF-8 + API_LEVEL: 29 concurrency: group: ci-push-${{ github.ref }} From e68f6d01bf09602b32dec18108d373b236da522b Mon Sep 17 00:00:00 2001 From: David Antoon Date: Sat, 8 Jul 2023 00:28:02 +0300 Subject: [PATCH 09/18] disable tests --- .github/workflows/build.yml | 70 +++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 90c74e4..da2fa27 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,38 +59,40 @@ jobs: - name: Gradle cache uses: gradle/gradle-build-action@v2 - - name: AVD cache - uses: actions/cache@v3 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{env.API_LEVEL}} - - - name: Create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@v2 - with: - avd-name: "AndroidEmulator" - api-level: ${{env.API_LEVEL}} - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: true - script: echo "Generated AVD snapshot for caching." - - - name: Run tests - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{env.API_LEVEL}} - force-avd-creation: false - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: true - script: ./run-tests.sh + - name: Build Libraries + run: ./gradlew build --no-daemon +# - name: AVD cache +# uses: actions/cache@v3 +# id: avd-cache +# with: +# path: | +# ~/.android/avd/* +# ~/.android/adb* +# key: avd-${{env.API_LEVEL}} +# +# - name: Create AVD and generate snapshot for caching +# if: steps.avd-cache.outputs.cache-hit != 'true' +# uses: reactivecircus/android-emulator-runner@v2 +# with: +# avd-name: "AndroidEmulator" +# api-level: ${{env.API_LEVEL}} +# force-avd-creation: false +# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none +# disable-animations: true +# script: echo "Generated AVD snapshot for caching." - - name: Upload test results - if: always() - uses: actions/upload-artifact@v2 - with: - name: test-results - path: app/build/reports/androidTests/connected/ +# - name: Run tests +# uses: reactivecircus/android-emulator-runner@v2 +# with: +# api-level: ${{env.API_LEVEL}} +# force-avd-creation: false +# emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none +# disable-animations: true +# script: ./run-tests.sh +# +# - name: Upload test results +# if: always() +# uses: actions/upload-artifact@v2 +# with: +# name: test-results +# path: app/build/reports/androidTests/connected/ From ca686e6c47c80d595f935eaeed6a0448349705c3 Mon Sep 17 00:00:00 2001 From: David Antoon Date: Sat, 8 Jul 2023 01:15:24 +0300 Subject: [PATCH 10/18] FR-12453 - Remove abstract activities --- .../android/AbstractFronteggLogoutActivity.kt | 37 ------------------- .../java/com/frontegg/android/FronteggApp.kt | 28 +++----------- .../java/com/frontegg/android/FronteggAuth.kt | 21 ++++++----- .../android/services/Authentication.kt | 15 +++++++- .../frontegg/android/utils/ObservableValue.kt | 7 ---- .../com/frontegg/android/ExampleUnitTest.kt | 17 --------- app/src/main/AndroidManifest.xml | 15 -------- app/src/main/java/com/frontegg/demo/App.kt | 3 +- .../java/com/frontegg/demo/FirstFragment.kt | 5 ++- .../frontegg/demo/FronteggLogoutActivity.kt | 12 ------ 10 files changed, 35 insertions(+), 125 deletions(-) delete mode 100644 android/src/main/java/com/frontegg/android/AbstractFronteggLogoutActivity.kt delete mode 100644 android/src/test/java/com/frontegg/android/ExampleUnitTest.kt delete mode 100644 app/src/main/java/com/frontegg/demo/FronteggLogoutActivity.kt diff --git a/android/src/main/java/com/frontegg/android/AbstractFronteggLogoutActivity.kt b/android/src/main/java/com/frontegg/android/AbstractFronteggLogoutActivity.kt deleted file mode 100644 index 12394b1..0000000 --- a/android/src/main/java/com/frontegg/android/AbstractFronteggLogoutActivity.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.frontegg.android - -import android.app.Activity -import android.os.Bundle -import android.widget.LinearLayout -import com.frontegg.android.FronteggApp -import com.frontegg.android.FronteggAuth -import com.frontegg.android.R - -abstract class AbstractFronteggLogoutActivity : Activity() { - - companion object { - private val TAG = AbstractFronteggLogoutActivity::class.java.simpleName - } - private var loaderLayout: LinearLayout? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_frontegg_logout) - overridePendingTransition(R.anim.fadein, R.anim.fadeout) - - loaderLayout = findViewById(R.id.loaderView) - val loaderView = layoutInflater.inflate(FronteggApp.getInstance().loaderId, null) - - loaderLayout!!.addView(loaderView) - - } - - override fun onResume() { - super.onResume() - FronteggAuth.instance.logout(this) { - navigateToFronteggLogin() - } - } - - abstract fun navigateToFronteggLogin() -} \ No newline at end of file diff --git a/android/src/main/java/com/frontegg/android/FronteggApp.kt b/android/src/main/java/com/frontegg/android/FronteggApp.kt index 9dd1361..8696f1a 100644 --- a/android/src/main/java/com/frontegg/android/FronteggApp.kt +++ b/android/src/main/java/com/frontegg/android/FronteggApp.kt @@ -2,7 +2,6 @@ package com.frontegg.android import android.annotation.SuppressLint import android.content.Context -import androidx.annotation.LayoutRes import com.frontegg.android.exceptions.FronteggException import com.frontegg.android.exceptions.FronteggException.Companion.FRONTEGG_APP_MUST_BE_INITIALIZED import com.frontegg.android.exceptions.FronteggException.Companion.FRONTEGG_DOMAIN_MUST_NOT_START_WITH_HTTPS @@ -11,27 +10,13 @@ import com.frontegg.android.services.* class FronteggApp private constructor( val context: Context, val baseUrl: String, - val clientId: String, - loaderId: Int? + val clientId: String ) { - val auth: FronteggAuth - val api: Api - val credentialManager: CredentialManager - val packageName: String; - - @LayoutRes - var loaderId: Int - - init { - this.loaderId = loaderId ?: R.layout.default_frontegg_loader - - credentialManager = CredentialManager(context) - api = Api(baseUrl, clientId, credentialManager) - auth = FronteggAuth(baseUrl, clientId, api, credentialManager) - - packageName = context.packageName - } + val credentialManager: CredentialManager = CredentialManager(context) + val api: Api = Api(baseUrl, clientId, credentialManager) + val auth: FronteggAuth = FronteggAuth(baseUrl, clientId, api, credentialManager) + val packageName: String = context.packageName companion object { @SuppressLint("StaticFieldLeak") @@ -48,7 +33,6 @@ class FronteggApp private constructor( fronteggDomain: String, clientId: String, context: Context, - loaderId: Int? ) { val baseUrl: String = if (fronteggDomain.startsWith("https")) { throw FronteggException(FRONTEGG_DOMAIN_MUST_NOT_START_WITH_HTTPS) @@ -56,7 +40,7 @@ class FronteggApp private constructor( "https://$fronteggDomain" } - instance = FronteggApp(context, baseUrl, clientId, loaderId) + instance = FronteggApp(context, baseUrl, clientId) } } diff --git a/android/src/main/java/com/frontegg/android/FronteggAuth.kt b/android/src/main/java/com/frontegg/android/FronteggAuth.kt index 2183b74..f096053 100644 --- a/android/src/main/java/com/frontegg/android/FronteggAuth.kt +++ b/android/src/main/java/com/frontegg/android/FronteggAuth.kt @@ -1,7 +1,8 @@ package com.frontegg.android -import android.app.Activity -import android.content.Context +import android.annotation.SuppressLint +import android.os.Handler +import android.os.Looper import android.util.Log import android.webkit.CookieManager import com.frontegg.android.models.User @@ -21,6 +22,7 @@ import java.util.* import kotlin.concurrent.schedule +@SuppressLint("CheckResult") @OptIn(DelicateCoroutinesApi::class) class FronteggAuth( val baseUrl: String, @@ -100,7 +102,7 @@ class FronteggAuth( && credentialManager.save(CredentialKeys.ACCESS_TOKEN, accessToken) ) { - @Suppress("UNUSED_VARIABLE") val decoded = JWTHelper.decode(accessToken) + val decoded = JWTHelper.decode(accessToken) val user = api.me(accessToken) this.refreshToken.value = refreshToken @@ -111,7 +113,7 @@ class FronteggAuth( refreshTaskRunner?.cancel() - if(decoded.exp > 0) { + if (decoded.exp > 0) { val now: Long = Instant.now().toEpochMilli() val offset = (((decoded.exp * 1000) - now) * 0.80).toLong() Log.d(TAG, "setCredentials, schedule for $offset") @@ -130,7 +132,7 @@ class FronteggAuth( this.initializing.value = false } - fun handleHostedLoginCallback( code: String): Boolean { + fun handleHostedLoginCallback(code: String): Boolean { val codeVerifier = credentialManager.get(CredentialKeys.CODE_VERIFIER) val redirectUrl = Constants.oauthCallbackUrl(baseUrl) @@ -145,13 +147,11 @@ class FronteggAuth( } return true - } - - fun logout(context: Context, callback: () -> Unit) { + fun logout(callback: () -> Unit = {}) { CookieManager.getInstance().removeAllCookies { - CookieManager.getInstance().flush(); + CookieManager.getInstance().flush() GlobalScope.launch(Dispatchers.IO) { refreshTaskRunner?.cancel() isLoading.value = true @@ -161,7 +161,8 @@ class FronteggAuth( user.value = null api.logout() credentialManager.clear() - (context as Activity).runOnUiThread { + val handler = Handler(Looper.getMainLooper()) + handler.post { callback() } } diff --git a/android/src/main/java/com/frontegg/android/services/Authentication.kt b/android/src/main/java/com/frontegg/android/services/Authentication.kt index b3f231a..6b65494 100644 --- a/android/src/main/java/com/frontegg/android/services/Authentication.kt +++ b/android/src/main/java/com/frontegg/android/services/Authentication.kt @@ -1,7 +1,20 @@ package com.frontegg.android.services +import android.content.Context +import android.net.Uri +import androidx.browser.customtabs.CustomTabsIntent +import com.frontegg.android.FronteggApp +import com.frontegg.android.utils.AuthorizeUrlGenerator -class Authentication { +class Authentication { + private val context: Context = FronteggApp.getInstance().context + public fun start() { + val url = AuthorizeUrlGenerator().generate().first + val builder = CustomTabsIntent.Builder() + builder.setShowTitle(true) + val customTabsIntent = builder.build() + customTabsIntent.launchUrl(this.context, Uri.parse(url)) + } } \ No newline at end of file diff --git a/android/src/main/java/com/frontegg/android/utils/ObservableValue.kt b/android/src/main/java/com/frontegg/android/utils/ObservableValue.kt index db0c849..2fa81c9 100644 --- a/android/src/main/java/com/frontegg/android/utils/ObservableValue.kt +++ b/android/src/main/java/com/frontegg/android/utils/ObservableValue.kt @@ -29,11 +29,4 @@ class ObservableValue(value: T) { onNext.accept(nullableObject) return disposable } - - fun subscribe2(onNext: (NullableObject) -> Unit) { - observable.subscribe { - onNext(it) - } - onNext(nullableObject) - } } \ No newline at end of file diff --git a/android/src/test/java/com/frontegg/android/ExampleUnitTest.kt b/android/src/test/java/com/frontegg/android/ExampleUnitTest.kt deleted file mode 100644 index 11ab49c..0000000 --- a/android/src/test/java/com/frontegg/android/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.frontegg.android - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dadb9b4..949681c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -36,21 +36,6 @@ android:scheme="https" /> - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/java/com/frontegg/demo/App.kt b/app/src/main/java/com/frontegg/demo/App.kt index bea8f16..3dbdf9a 100644 --- a/app/src/main/java/com/frontegg/demo/App.kt +++ b/app/src/main/java/com/frontegg/demo/App.kt @@ -10,8 +10,7 @@ class App : Application() { FronteggApp.init( BuildConfig.FRONTEGG_DOMAIN, BuildConfig.FRONTEGG_CLIENT_ID, - this, - R.layout.loader + this ) } } \ No newline at end of file diff --git a/app/src/main/java/com/frontegg/demo/FirstFragment.kt b/app/src/main/java/com/frontegg/demo/FirstFragment.kt index 18b76a0..8985115 100644 --- a/app/src/main/java/com/frontegg/demo/FirstFragment.kt +++ b/app/src/main/java/com/frontegg/demo/FirstFragment.kt @@ -7,6 +7,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.navigation.fragment.findNavController +import com.frontegg.android.FronteggApp import com.frontegg.android.FronteggAuth import com.frontegg.demo.databinding.FragmentFirstBinding import io.reactivex.rxjava3.disposables.Disposable @@ -41,8 +42,8 @@ class FirstFragment : Fragment() { } binding.logoutButton.setOnClickListener { - activity?.startActivity(Intent(activity, FronteggLogoutActivity::class.java)) - activity?.finish() + + FronteggAuth.instance.logout() } disposables.add(FronteggAuth.instance.user.subscribe { diff --git a/app/src/main/java/com/frontegg/demo/FronteggLogoutActivity.kt b/app/src/main/java/com/frontegg/demo/FronteggLogoutActivity.kt deleted file mode 100644 index f418cf1..0000000 --- a/app/src/main/java/com/frontegg/demo/FronteggLogoutActivity.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.frontegg.demo - -import com.frontegg.android.AbstractFronteggLogoutActivity -import android.content.Intent - -class FronteggLogoutActivity: AbstractFronteggLogoutActivity() { - override fun navigateToFronteggLogin() { - val intent = Intent(this, FronteggActivity::class.java) - startActivity(intent) - finish() - } -} \ No newline at end of file From 2d540fc373b5ab36aecfdd150ea3e72498b618d9 Mon Sep 17 00:00:00 2001 From: David Antoon Date: Sat, 8 Jul 2023 01:23:49 +0300 Subject: [PATCH 11/18] react native version --- .github/workflows/release.yaml | 3 +++ android/build.gradle | 2 +- .../android/ExampleInstrumentedTest.kt | 24 ------------------- 3 files changed, 4 insertions(+), 25 deletions(-) delete mode 100644 android/src/androidTest/java/com/frontegg/android/ExampleInstrumentedTest.kt diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 640da1c..a2800fd 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,6 +4,9 @@ on: types: [ closed ] branches: - master + push: + branches: + - FR-12452-support-react-native concurrency: group: ci-release-${{ github.ref }} diff --git a/android/build.gradle b/android/build.gradle index 48b6666..865952d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,7 +6,7 @@ plugins { } group 'com.frontegg.android' -version '1.0.2' +version '1.0.3' android { namespace 'com.frontegg.android' diff --git a/android/src/androidTest/java/com/frontegg/android/ExampleInstrumentedTest.kt b/android/src/androidTest/java/com/frontegg/android/ExampleInstrumentedTest.kt deleted file mode 100644 index 73e9853..0000000 --- a/android/src/androidTest/java/com/frontegg/android/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.frontegg.android - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.frontegg.android.test", appContext.packageName) - } -} \ No newline at end of file From 3c5450d126736e7b8638517020a1b746c0746dc8 Mon Sep 17 00:00:00 2001 From: David Antoon Date: Sat, 8 Jul 2023 01:24:15 +0300 Subject: [PATCH 12/18] react native version --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a2800fd..18e1037 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -14,7 +14,7 @@ concurrency: jobs: publish-packages: - if: "github.event.pull_request.merged == true" +# if: "github.event.pull_request.merged == true" name: "Publish Libraries" runs-on: ubuntu-latest steps: From 9b098555e16e566b8f1f0f82edfe0cf1867588e7 Mon Sep 17 00:00:00 2001 From: David Antoon Date: Sat, 8 Jul 2023 01:40:48 +0300 Subject: [PATCH 13/18] upgrade gralde and fix publish --- .github/workflows/release.yaml | 6 +----- android/build.gradle | 6 +++--- android/src/main/java/com/frontegg/android/services/Api.kt | 2 +- .../main/java/com/frontegg/android/utils/CredentialKeys.kt | 1 + gradle/wrapper/gradle-wrapper.properties | 6 +++--- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 18e1037..d4881b5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,14 +19,10 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout" - uses: actions/checkout@v3 - with: - fetch-depth: "0" - - name: Checkout repo uses: actions/checkout@v3 with: token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 + fetch-depth: "0" - name: Set git config run: | git config --global user.name 'github-actions' diff --git a/android/build.gradle b/android/build.gradle index 865952d..32fcf44 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -17,6 +17,7 @@ android { targetSdk 33 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" + versionName "$version" } buildTypes { @@ -93,13 +94,12 @@ afterEvaluate { } repositories { maven { - + name = "OSSRH" + url = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' credentials { username = System.getenv('NEXUS_USERNAME') password = System.getenv('NEXUS_PASSWORD') } - name = "OSSRH" - url = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' } } } diff --git a/android/src/main/java/com/frontegg/android/services/Api.kt b/android/src/main/java/com/frontegg/android/services/Api.kt index 297e6a7..21163d0 100644 --- a/android/src/main/java/com/frontegg/android/services/Api.kt +++ b/android/src/main/java/com/frontegg/android/services/Api.kt @@ -81,7 +81,7 @@ open class Api( } @Throws(IllegalArgumentException::class, IOException::class) - public fun me(accessToken: String): User? { + public fun me(): User? { val call = buildGetRequest(ApiConstants.me) val response = call.execute() if (response.isSuccessful) { diff --git a/android/src/main/java/com/frontegg/android/utils/CredentialKeys.kt b/android/src/main/java/com/frontegg/android/utils/CredentialKeys.kt index b17a363..653a2d4 100644 --- a/android/src/main/java/com/frontegg/android/utils/CredentialKeys.kt +++ b/android/src/main/java/com/frontegg/android/utils/CredentialKeys.kt @@ -1,5 +1,6 @@ package com.frontegg.android.utils +@Suppress("UNUSED_PARAMETER") public enum class CredentialKeys(key: String) { ACCESS_TOKEN("access_token"), REFRESH_TOKEN("refresh_token"), diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 51ecfd9..df250ba 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Nov 21 02:35:38 IST 2022 +#Sat Jul 08 01:29:18 IDT 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists From f3df3ef8baa96afffa5a24789b0e99da48d93ff5 Mon Sep 17 00:00:00 2001 From: David Antoon Date: Sat, 8 Jul 2023 01:53:35 +0300 Subject: [PATCH 14/18] fix code --- android/src/main/java/com/frontegg/android/FronteggAuth.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/frontegg/android/FronteggAuth.kt b/android/src/main/java/com/frontegg/android/FronteggAuth.kt index f096053..c1e69e9 100644 --- a/android/src/main/java/com/frontegg/android/FronteggAuth.kt +++ b/android/src/main/java/com/frontegg/android/FronteggAuth.kt @@ -103,7 +103,7 @@ class FronteggAuth( ) { val decoded = JWTHelper.decode(accessToken) - val user = api.me(accessToken) + val user = api.me() this.refreshToken.value = refreshToken this.accessToken.value = accessToken From 1640ac46fd2eecb570cecacb853cddc368fc39ee Mon Sep 17 00:00:00 2001 From: David Antoon Date: Tue, 11 Jul 2023 15:37:52 +0300 Subject: [PATCH 15/18] add authentication activity to be able to launch a frontegg oauth from react native applicaiton --- .../android/AuthenticationActivity.kt | 90 +++++++++++++++++++ .../java/com/frontegg/android/FronteggAuth.kt | 2 + .../android/services/Authentication.kt | 20 ----- .../com/frontegg/android/utils/JWTHelper.kt | 1 - .../java/com/frontegg/demo/FirstFragment.kt | 1 - 5 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 android/src/main/java/com/frontegg/android/AuthenticationActivity.kt delete mode 100644 android/src/main/java/com/frontegg/android/services/Authentication.kt diff --git a/android/src/main/java/com/frontegg/android/AuthenticationActivity.kt b/android/src/main/java/com/frontegg/android/AuthenticationActivity.kt new file mode 100644 index 0000000..c832662 --- /dev/null +++ b/android/src/main/java/com/frontegg/android/AuthenticationActivity.kt @@ -0,0 +1,90 @@ +package com.frontegg.reactnative + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.util.Log +import androidx.browser.customtabs.CustomTabsIntent +import com.frontegg.android.FronteggAuth +import com.frontegg.android.utils.AuthorizeUrlGenerator + +class AuthenticationActivity : Activity() { + + + fun startAuth(url: String) { + val builder = CustomTabsIntent.Builder() + builder.setShowTitle(true) + val customTabsIntent = builder.build() + customTabsIntent.launchUrl(this, Uri.parse(url)) + } + + override fun onResume() { + super.onResume() + + val intentLaunched = intent.extras?.getBoolean(AUTH_LAUNCHED, false) ?: false + Log.d("TEST", "onResume | intentLaunched: $intentLaunched") + + if (intentLaunched) { + // check for intent url + val url = intent.extras!!.getString(AUTHORIZE_URI) + if (url != null) { + startAuth(url) + } else { + setResult(RESULT_CANCELED) + } + } else { + Log.d("TEST", "Got intent with data, ${intent.data.toString()}") + val code = intent.data?.getQueryParameter("code") + if(code != null) { + FronteggAuth.instance.handleHostedLoginCallback(code) + setResult(RESULT_OK) + }else { + setResult(RESULT_CANCELED) + } + } + +// val resultMissing = authIntent.data == null +// if (resultMissing) setResult(RESULT_CANCELED) else setResult( +// RESULT_OK, +// authenticationIntent +// ) + } + + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + setIntent(intent) + } + + @SuppressLint("QueryPermissionsNeeded") + @Suppress("DEPRECATION") + fun getMainActivity() { + val pm: PackageManager = packageManager + val mainIntent = Intent(Intent.ACTION_MAIN, null) + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER) + mainIntent.setPackage(applicationContext.packageName) + val appList = pm.queryIntentActivities(mainIntent, 0) + for (resolveInfo in appList) { + val packageName = resolveInfo.activityInfo.packageName + val activityName = resolveInfo.activityInfo.name + Log.d("ActivityNames", "Package Name: $packageName Activity Name: $activityName") + } + } + + companion object { + const val OAUTH_LOGIN_REQUEST = 100001 + const val AUTHORIZE_URI = "com.frontegg.android.AUTHORIZE_URI" + private const val AUTH_LAUNCHED = "com.frontegg.android.AUTH_LAUNCHED" + + fun authenticateUsingBrowser(activity: Activity) { + val intent = Intent(activity, AuthenticationActivity::class.java) + val authorizeUri = AuthorizeUrlGenerator().generate() + intent.putExtra(AUTH_LAUNCHED, true) + intent.putExtra(AUTHORIZE_URI, authorizeUri.first) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + activity.startActivityForResult(intent, OAUTH_LOGIN_REQUEST) + } + } +} diff --git a/android/src/main/java/com/frontegg/android/FronteggAuth.kt b/android/src/main/java/com/frontegg/android/FronteggAuth.kt index c1e69e9..9a4a554 100644 --- a/android/src/main/java/com/frontegg/android/FronteggAuth.kt +++ b/android/src/main/java/com/frontegg/android/FronteggAuth.kt @@ -113,6 +113,8 @@ class FronteggAuth( refreshTaskRunner?.cancel() + + if (decoded.exp > 0) { val now: Long = Instant.now().toEpochMilli() val offset = (((decoded.exp * 1000) - now) * 0.80).toLong() diff --git a/android/src/main/java/com/frontegg/android/services/Authentication.kt b/android/src/main/java/com/frontegg/android/services/Authentication.kt deleted file mode 100644 index 6b65494..0000000 --- a/android/src/main/java/com/frontegg/android/services/Authentication.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.frontegg.android.services - -import android.content.Context -import android.net.Uri -import androidx.browser.customtabs.CustomTabsIntent -import com.frontegg.android.FronteggApp -import com.frontegg.android.utils.AuthorizeUrlGenerator - - -class Authentication { - private val context: Context = FronteggApp.getInstance().context - - public fun start() { - val url = AuthorizeUrlGenerator().generate().first - val builder = CustomTabsIntent.Builder() - builder.setShowTitle(true) - val customTabsIntent = builder.build() - customTabsIntent.launchUrl(this.context, Uri.parse(url)) - } -} \ No newline at end of file diff --git a/android/src/main/java/com/frontegg/android/utils/JWTHelper.kt b/android/src/main/java/com/frontegg/android/utils/JWTHelper.kt index a4fcd9e..c65d450 100644 --- a/android/src/main/java/com/frontegg/android/utils/JWTHelper.kt +++ b/android/src/main/java/com/frontegg/android/utils/JWTHelper.kt @@ -20,7 +20,6 @@ class JWTHelper { companion object { fun decode(token: String): JWT { - val chunks: List = token.split(Regex("\\."), 0) val decoder: Base64.Decoder = Base64.getUrlDecoder() val payload = String(decoder.decode(chunks[1])) diff --git a/app/src/main/java/com/frontegg/demo/FirstFragment.kt b/app/src/main/java/com/frontegg/demo/FirstFragment.kt index 8985115..a902398 100644 --- a/app/src/main/java/com/frontegg/demo/FirstFragment.kt +++ b/app/src/main/java/com/frontegg/demo/FirstFragment.kt @@ -7,7 +7,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.navigation.fragment.findNavController -import com.frontegg.android.FronteggApp import com.frontegg.android.FronteggAuth import com.frontegg.demo.databinding.FragmentFirstBinding import io.reactivex.rxjava3.disposables.Disposable From 9969606a0d4677f29e20868379a69d0caefaa55c Mon Sep 17 00:00:00 2001 From: David Antoon Date: Wed, 12 Jul 2023 13:32:21 +0300 Subject: [PATCH 16/18] add android intent filter to the package maniest --- android/src/main/AndroidManifest.xml | 29 +++++++- .../android/AuthenticationActivity.kt | 71 ++++++++++++++----- .../java/com/frontegg/android/FronteggAuth.kt | 5 ++ app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 9 --- .../java/com/frontegg/demo/FirstFragment.kt | 8 ++- app/src/main/res/layout/fragment_first.xml | 18 ++--- 7 files changed, 103 insertions(+), 38 deletions(-) diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index a5918e6..f7f6f74 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,4 +1,31 @@ - + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/src/main/java/com/frontegg/android/AuthenticationActivity.kt b/android/src/main/java/com/frontegg/android/AuthenticationActivity.kt index c832662..ce8a424 100644 --- a/android/src/main/java/com/frontegg/android/AuthenticationActivity.kt +++ b/android/src/main/java/com/frontegg/android/AuthenticationActivity.kt @@ -1,55 +1,90 @@ -package com.frontegg.reactnative +package com.frontegg.android import android.annotation.SuppressLint import android.app.Activity import android.content.Intent import android.content.pm.PackageManager import android.net.Uri +import android.os.Bundle import android.util.Log import androidx.browser.customtabs.CustomTabsIntent -import com.frontegg.android.FronteggAuth import com.frontegg.android.utils.AuthorizeUrlGenerator class AuthenticationActivity : Activity() { - - fun startAuth(url: String) { + var customTabLaunched = false + private fun startAuth(url: String) { val builder = CustomTabsIntent.Builder() builder.setShowTitle(true) + builder.setToolbarCornerRadiusDp(14) val customTabsIntent = builder.build() customTabsIntent.launchUrl(this, Uri.parse(url)) + customTabLaunched = true + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + customTabLaunched = savedInstanceState?.getBoolean(CUSTOM_TAB_LAUNCHED, false) ?: false + } + + override fun onSaveInstanceState(outState: Bundle) { + outState.putBoolean(CUSTOM_TAB_LAUNCHED, customTabLaunched) + super.onSaveInstanceState(outState) + } override fun onResume() { super.onResume() val intentLaunched = intent.extras?.getBoolean(AUTH_LAUNCHED, false) ?: false - Log.d("TEST", "onResume | intentLaunched: $intentLaunched") + Log.d(TAG, "onResume | intentLaunched: $intentLaunched") - if (intentLaunched) { - // check for intent url + if (intentLaunched && !customTabLaunched) { val url = intent.extras!!.getString(AUTHORIZE_URI) if (url != null) { startAuth(url) } else { setResult(RESULT_CANCELED) + finish() } } else { - Log.d("TEST", "Got intent with data, ${intent.data.toString()}") + val intentUrl = intent.data + if(intentUrl == null){ + setResult(RESULT_CANCELED) + finish() + return + } + val code = intent.data?.getQueryParameter("code") - if(code != null) { + if (code != null) { + Log.d(TAG, "Got intent with oauth callback") FronteggAuth.instance.handleHostedLoginCallback(code) setResult(RESULT_OK) - }else { - setResult(RESULT_CANCELED) + finish() + return } - } -// val resultMissing = authIntent.data == null -// if (resultMissing) setResult(RESULT_CANCELED) else setResult( -// RESULT_OK, -// authenticationIntent -// ) + /** + * due to lake to support from android chrome tab + * we are sharing magic link cookie with the chrome + * instance to give the ability to login via magic link + * in the native chrome browser, then the hosted login + * will redirect the user directly to the application + * with the exchange code to finalize the authorization process + */ +// val path = intentUrl.path +// if(path != null && path.startsWith("/oauth/account/login/magic-link?token=")){ +// Log.d(TAG, "Got intent with magic link") +// var url = intentUrl.toString() +// url = url.replace("magic-link?token=", "magic-link?chromeTab=true&token=") +// startAuth(url) +// return +// } + + Log.d(TAG, "Got intent with unknown data") + setResult(RESULT_CANCELED) + finish() + } } @@ -77,6 +112,8 @@ class AuthenticationActivity : Activity() { const val OAUTH_LOGIN_REQUEST = 100001 const val AUTHORIZE_URI = "com.frontegg.android.AUTHORIZE_URI" private const val AUTH_LAUNCHED = "com.frontegg.android.AUTH_LAUNCHED" + private const val CUSTOM_TAB_LAUNCHED = "com.frontegg.android.CUSTOM_TAB_LAUNCHED" + private val TAG = AuthenticationActivity::class.java.simpleName fun authenticateUsingBrowser(activity: Activity) { val intent = Intent(activity, AuthenticationActivity::class.java) diff --git a/android/src/main/java/com/frontegg/android/FronteggAuth.kt b/android/src/main/java/com/frontegg/android/FronteggAuth.kt index 9a4a554..31a2f4b 100644 --- a/android/src/main/java/com/frontegg/android/FronteggAuth.kt +++ b/android/src/main/java/com/frontegg/android/FronteggAuth.kt @@ -1,6 +1,7 @@ package com.frontegg.android import android.annotation.SuppressLint +import android.app.Activity import android.os.Handler import android.os.Looper import android.util.Log @@ -171,4 +172,8 @@ class FronteggAuth( } } + + fun login(activity: Activity) { + AuthenticationActivity.authenticateUsingBrowser(activity) + } } diff --git a/app/build.gradle b/app/build.gradle index 4783d66..538a4b1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,6 +20,7 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders = [ + "package_name" : applicationId, "frontegg_domain" : fronteggDomain, "frontegg_client_id": fronteggClientId ] diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 949681c..373c830 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,15 +26,6 @@ - - - - - - - diff --git a/app/src/main/java/com/frontegg/demo/FirstFragment.kt b/app/src/main/java/com/frontegg/demo/FirstFragment.kt index a902398..1918665 100644 --- a/app/src/main/java/com/frontegg/demo/FirstFragment.kt +++ b/app/src/main/java/com/frontegg/demo/FirstFragment.kt @@ -36,15 +36,17 @@ class FirstFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.buttonFirst.setOnClickListener { - findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment) - } binding.logoutButton.setOnClickListener { FronteggAuth.instance.logout() } + binding.loginButton.setOnClickListener { + + FronteggAuth.instance.login(requireActivity()) + } + disposables.add(FronteggAuth.instance.user.subscribe { activity?.runOnUiThread { binding.textviewFirst.text = it.value?.email diff --git a/app/src/main/res/layout/fragment_first.xml b/app/src/main/res/layout/fragment_first.xml index cb0eb4b..84c2921 100644 --- a/app/src/main/res/layout/fragment_first.xml +++ b/app/src/main/res/layout/fragment_first.xml @@ -11,28 +11,30 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_first_fragment" - app:layout_constraintBottom_toTopOf="@id/button_first" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent"/>