From 68d8bb70e61258a0f27743705817bff124edc84f Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Mon, 19 Sep 2022 11:52:04 -0700 Subject: [PATCH 01/21] Update kotlin and build tools deps --- app/build.gradle | 2 +- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 6 +++--- mobileauthentication/build.gradle | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 39c173c..61a733d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,7 +26,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':mobileauthentication') - implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "com.android.support:appcompat-v7:26.1.0" implementation "com.android.support.constraint:constraint-layout:1.0.2" testImplementation "junit:junit:4.12" diff --git a/build.gradle b/build.gradle index 2354353..dea9173 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.2.21' + ext.kotlin_version = '1.7.10' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:7.2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index eae11e6..081467e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Feb 07 13:43:17 PST 2018 +#Mon Sep 19 11:37:24 PDT 2022 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/mobileauthentication/build.gradle b/mobileauthentication/build.gradle index b95f3e8..cf2e5b2 100644 --- a/mobileauthentication/build.gradle +++ b/mobileauthentication/build.gradle @@ -40,7 +40,7 @@ dependencies { implementation "com.android.support:appcompat-v7:26.1.0" implementation "com.android.support:customtabs:26.1.0" implementation "com.android.support.constraint:constraint-layout:1.0.2" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.21" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "io.reactivex.rxjava2:rxandroid:2.0.1" implementation "io.reactivex.rxjava2:rxjava:2.1.7" From be432fd0e6fb85e83ca29d383bd3a6a24ca78c92 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Mon, 19 Sep 2022 11:52:50 -0700 Subject: [PATCH 02/21] Move namespace per gradle update recommendation --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 3 +-- mobileauthentication/build.gradle | 3 +-- mobileauthentication/src/main/AndroidManifest.xml | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 61a733d..9feb9b7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,6 +20,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + namespace 'ca.bc.gov.mobileauthenticationandroidexample' } dependencies { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6656cb8..3510310 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/mobileauthentication/build.gradle b/mobileauthentication/build.gradle index cf2e5b2..49ba63c 100644 --- a/mobileauthentication/build.gradle +++ b/mobileauthentication/build.gradle @@ -13,8 +13,6 @@ android { targetSdkVersion 26 compileSdkVersion 26 buildToolsVersion '26.0.2' - versionCode 1 - versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -31,6 +29,7 @@ android { } } } + namespace 'ca.bc.gov.mobileauthentication' } diff --git a/mobileauthentication/src/main/AndroidManifest.xml b/mobileauthentication/src/main/AndroidManifest.xml index 02b8573..743a525 100644 --- a/mobileauthentication/src/main/AndroidManifest.xml +++ b/mobileauthentication/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + From 8146bbbbe461ca0bddc78dc47072b725fc626d53 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Mon, 19 Sep 2022 12:15:40 -0700 Subject: [PATCH 03/21] Update gitignore and untrack files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added the entire .idea/ directory to gitignore instead of singular files and removed tracked files from git. These files are automatically generated by IDEA – there's no need to keep them around in source control. Even worse, it can cause weird build issues depending on different dev environments. --- .gitignore | 12 ++++++------ .idea/gradle.xml | 19 ------------------- .idea/misc.xml | 33 --------------------------------- .idea/modules.xml | 10 ---------- .idea/runConfigurations.xml | 12 ------------ .idea/vcs.xml | 6 ------ 6 files changed, 6 insertions(+), 86 deletions(-) delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/runConfigurations.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 39fb081..6259ca0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ -*.iml -.gradle +.gradle/ /local.properties -/.idea/workspace.xml -/.idea/libraries + +.idea/ + .DS_Store -/build -/captures +/build/ +/captures/ .externalNativeBuild diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 5b05804..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 3963879..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index ba113a8..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From b38b647e9cfb3c1dc6a2432240e91288c73ae5fe Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Mon, 19 Sep 2022 12:36:03 -0700 Subject: [PATCH 04/21] Change to camelCase --- app/build.gradle | 2 +- build.gradle | 5 +++-- mobileauthentication/build.gradle | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9feb9b7..ce865a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,7 +27,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':mobileauthentication') - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" implementation "com.android.support:appcompat-v7:26.1.0" implementation "com.android.support.constraint:constraint-layout:1.0.2" testImplementation "junit:junit:4.12" diff --git a/build.gradle b/build.gradle index dea9173..dbdd8f0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,15 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlinVersion = '1.7.10' + repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:7.2.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/mobileauthentication/build.gradle b/mobileauthentication/build.gradle index 49ba63c..b3a1c58 100644 --- a/mobileauthentication/build.gradle +++ b/mobileauthentication/build.gradle @@ -39,7 +39,7 @@ dependencies { implementation "com.android.support:appcompat-v7:26.1.0" implementation "com.android.support:customtabs:26.1.0" implementation "com.android.support.constraint:constraint-layout:1.0.2" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" implementation "io.reactivex.rxjava2:rxandroid:2.0.1" implementation "io.reactivex.rxjava2:rxjava:2.1.7" From 23d4a4879089cceba73f835f0661a948d037b65a Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Mon, 19 Sep 2022 12:36:55 -0700 Subject: [PATCH 05/21] Add variables for Android versioning --- app/build.gradle | 7 ++++--- build.gradle | 4 ++++ mobileauthentication/build.gradle | 7 +++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ce865a7..56f9c9b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,11 +5,12 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 26 + defaultConfig { + compileSdkVersion rootProject.ext.compileSdkVersion applicationId "ca.bc.gov.mobileauthenticationandroidexample" - minSdkVersion 23 - targetSdkVersion 26 + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/build.gradle b/build.gradle index dbdd8f0..c390551 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,10 @@ buildscript { ext.kotlinVersion = '1.7.10' + ext.compileSdkVersion = 26 + ext.minSdkVersion = 23 + ext.targetSdkVersion = 26 + repositories { google() jcenter() diff --git a/mobileauthentication/build.gradle b/mobileauthentication/build.gradle index b3a1c58..e0c9c13 100644 --- a/mobileauthentication/build.gradle +++ b/mobileauthentication/build.gradle @@ -6,12 +6,11 @@ apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 26 defaultConfig { - minSdkVersion 23 - targetSdkVersion 26 - compileSdkVersion 26 + compileSdkVersion rootProject.ext.compileSdkVersion + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion buildToolsVersion '26.0.2' testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" From 2e685c1cbe398ac1db54d20bbe9296ff1d92581e Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Mon, 19 Sep 2022 12:40:53 -0700 Subject: [PATCH 06/21] Migrate from legacy support libraries to AndroidX Had to increase compileSdkVersion from 26 to 28 for this to work. --- app/build.gradle | 10 +++++----- .../mobileauthenticationandroidexample/MainActivity.kt | 2 +- app/src/main/res/layout/activity_main.xml | 4 ++-- build.gradle | 2 +- gradle.properties | 2 ++ mobileauthentication/build.gradle | 8 ++++---- .../screens/redirect/RedirectActivity.kt | 6 +++--- .../src/main/res/layout/activity_login.xml | 4 ++-- 8 files changed, 20 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 56f9c9b..b6b672a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { release { @@ -29,10 +29,10 @@ dependencies { implementation project(':mobileauthentication') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" - implementation "com.android.support:appcompat-v7:26.1.0" - implementation "com.android.support.constraint:constraint-layout:1.0.2" + implementation 'androidx.appcompat:appcompat:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' testImplementation "junit:junit:4.12" - androidTestImplementation "com.android.support.test:runner:1.0.1" - androidTestImplementation "com.android.support.test.espresso:espresso-core:3.0.1" + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' } diff --git a/app/src/main/java/ca/bc/gov/mobileauthenticationandroidexample/MainActivity.kt b/app/src/main/java/ca/bc/gov/mobileauthenticationandroidexample/MainActivity.kt index 398cd7a..ec9d5d5 100644 --- a/app/src/main/java/ca/bc/gov/mobileauthenticationandroidexample/MainActivity.kt +++ b/app/src/main/java/ca/bc/gov/mobileauthenticationandroidexample/MainActivity.kt @@ -1,7 +1,7 @@ package ca.bc.gov.mobileauthenticationandroidexample import android.content.Intent -import android.support.v7.app.AppCompatActivity +import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import ca.bc.gov.mobileauthentication.MobileAuthenticationClient diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 092ef3c..4cc4bf4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,9 +1,9 @@ - - + diff --git a/build.gradle b/build.gradle index c390551..33fd42b 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { ext.kotlinVersion = '1.7.10' - ext.compileSdkVersion = 26 + ext.compileSdkVersion = 28 ext.minSdkVersion = 23 ext.targetSdkVersion = 26 diff --git a/gradle.properties b/gradle.properties index aac7c9b..9e6fce1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/mobileauthentication/build.gradle b/mobileauthentication/build.gradle index e0c9c13..06e92da 100644 --- a/mobileauthentication/build.gradle +++ b/mobileauthentication/build.gradle @@ -13,7 +13,7 @@ android { targetSdkVersion rootProject.ext.targetSdkVersion buildToolsVersion '26.0.2' - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } @@ -35,9 +35,9 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "com.android.support:appcompat-v7:26.1.0" - implementation "com.android.support:customtabs:26.1.0" - implementation "com.android.support.constraint:constraint-layout:1.0.2" + implementation 'androidx.appcompat:appcompat:1.0.0' + implementation 'androidx.browser:browser:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" implementation "io.reactivex.rxjava2:rxandroid:2.0.1" diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt index af1fdb4..ac6896b 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt @@ -8,9 +8,9 @@ import android.preference.PreferenceManager import android.widget.Toast import ca.bc.gov.mobileauthentication.R import ca.bc.gov.mobileauthentication.di.InjectionUtils -import android.support.customtabs.CustomTabsIntent -import android.support.v4.content.ContextCompat -import android.support.v7.app.AppCompatActivity +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.content.ContextCompat +import androidx.appcompat.app.AppCompatActivity import android.view.View import ca.bc.gov.mobileauthentication.MobileAuthenticationClient import ca.bc.gov.mobileauthentication.common.Constants diff --git a/mobileauthentication/src/main/res/layout/activity_login.xml b/mobileauthentication/src/main/res/layout/activity_login.xml index 4d41546..0466c09 100644 --- a/mobileauthentication/src/main/res/layout/activity_login.xml +++ b/mobileauthentication/src/main/res/layout/activity_login.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file From 8c77b2930e4ed82ec7829e0c6b3c21208b2f4ab1 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Mon, 19 Sep 2022 12:49:51 -0700 Subject: [PATCH 07/21] Add AppAuth dependency --- mobileauthentication/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mobileauthentication/build.gradle b/mobileauthentication/build.gradle index 06e92da..3eedbfc 100644 --- a/mobileauthentication/build.gradle +++ b/mobileauthentication/build.gradle @@ -35,6 +35,8 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'net.openid:appauth:0.11.1' + implementation 'androidx.appcompat:appcompat:1.0.0' implementation 'androidx.browser:browser:1.0.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' From 740c4fcbfec0729f3205fd6e5063039b4a20a13c Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Mon, 19 Sep 2022 12:50:09 -0700 Subject: [PATCH 08/21] Add manifestPlaceholder for demo module --- app/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index b6b672a..e917cec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,10 @@ android { versionCode 1 versionName "1.0" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + + manifestPlaceholders = [ + 'appAuthRedirectScheme': 'secure-image' + ] } buildTypes { release { From 4bca7fe1148197ec21c74462e6cf7ffcbb0d7ca8 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Mon, 19 Sep 2022 14:16:14 -0700 Subject: [PATCH 09/21] Use appauth redirect build variable --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e917cec..513af8f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,7 @@ android { testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' manifestPlaceholders = [ - 'appAuthRedirectScheme': 'secure-image' + 'appAuthRedirectScheme': 'secure-image://client' ] } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3510310..f0f7650 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,7 +27,7 @@ - + From cb2ca8f78c4fed4f3a14a026c2bceab6649efcc3 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 10:47:32 -0700 Subject: [PATCH 10/21] Add AppAuthApi class to replace AuthApi interface Replaces AuthApi and its retrofit implementation using the AppAuth library to handle token requests. --- .../mobileauthentication/data/AppAuthApi.kt | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt new file mode 100644 index 0000000..28c64d6 --- /dev/null +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt @@ -0,0 +1,118 @@ +package ca.bc.gov.mobileauthentication.data + +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.browser.customtabs.CustomTabsIntent +import ca.bc.gov.mobileauthentication.common.utils.UrlUtils +import ca.bc.gov.mobileauthentication.data.models.Token +import io.reactivex.Observable +import net.openid.appauth.* + + +class AppAuthApi(private val context: Context, + baseUrl: String, + private val realmName: String, + authEndpoint: String, + redirectUri: String, + private val clientId: String, + hint: String = "") { + + companion object { + const val REFRESH_EXPIRES_IN = "refresh_expires_in" + const val NOT_BEFORE_POLICY = "not-before-policy" + const val SESSION_STATE = "session_state" + } + + private fun buildTokenUrl(baseUrl: String): String { + return UrlUtils.cleanBaseUrl(baseUrl) + "auth/realms/$realmName/protocol/openid-connect/token" + } + + private val authorizationConfig: AuthorizationServiceConfiguration + private val authorizationRequest: AuthorizationRequest + + init { + authorizationConfig = AuthorizationServiceConfiguration( + Uri.parse(authEndpoint), + Uri.parse(buildTokenUrl(baseUrl))) + + val reqBuilder = AuthorizationRequest.Builder( + authorizationConfig, + clientId, + ResponseTypeValues.CODE, + Uri.parse(redirectUri)) + + if (hint.isNotEmpty()) + reqBuilder.setLoginHint(hint) + + authorizationRequest = reqBuilder.build() + } + + private fun buildRefreshTokenRequest(token: Token): TokenRequest { + return TokenRequest.Builder(authorizationConfig, clientId) + .setGrantType(GrantTypeValues.REFRESH_TOKEN) + .setRefreshToken(token.refreshToken) + .build() + } + + private fun convertToToken(tokenResponse: TokenResponse): Token { + val accessExpirationTime = tokenResponse.accessTokenExpirationTime + val accessExpirationUnixTime = accessExpirationTime?.div(1000) + val accessExpiresInMillis = accessExpirationUnixTime?.minus(System.currentTimeMillis()) + + val refreshExpiresIn = tokenResponse.additionalParameters[REFRESH_EXPIRES_IN]?.toLong() + val refreshExpiresInMillis = refreshExpiresIn?.times(1000) + val refreshExpirationUnixTime = refreshExpiresInMillis?.plus(System.currentTimeMillis()) + + return Token( + tokenResponse.accessToken, + accessExpiresInMillis, + refreshExpiresIn, + tokenResponse.refreshToken, + tokenResponse.tokenType, + tokenResponse.idToken, + tokenResponse.additionalParameters[NOT_BEFORE_POLICY]?.toLong(), + tokenResponse.additionalParameters[SESSION_STATE], + accessExpirationUnixTime, + refreshExpirationUnixTime + ) + } + + /** + * Using configuration provided to the Constructor: builds an Intent that will be used to + * initiate the OAuth authentication flow (using startActivityForResult) + */ + fun getAuthRequestIntent(customTabsIntent: CustomTabsIntent? = null): Intent { + val service = AuthorizationService(context) + + return if (customTabsIntent != null) + service.getAuthorizationRequestIntent(authorizationRequest, customTabsIntent) + else + service.getAuthorizationRequestIntent(authorizationRequest) + } + + private fun performTokenRequest(tokenReq: TokenRequest): Observable { + return Observable.create { + AuthorizationService(context).performTokenRequest(tokenReq) { response, ex -> + if (response != null) + it.onNext(convertToToken(response)) + else if (ex != null) + it.onError(ex) + } + } + } + + /** + * Makes an OAuth request to exchange an Authorization code for an access token. + */ + fun getToken(authResponse: AuthorizationResponse): Observable { + return performTokenRequest(authResponse.createTokenExchangeRequest()) + } + + /** + * Using the provided refresh token: makes an OAuth request to exchange for a new access token. + */ + fun refreshToken(token: Token): Observable { + return performTokenRequest(buildRefreshTokenRequest(token)) + } +} \ No newline at end of file From 06db7b5113c14f40f58292d4c501ef5d2aa43cb9 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 10:49:48 -0700 Subject: [PATCH 11/21] Modify TokenRepo and subtypes to use AppAuthApi --- .../MobileAuthenticationClient.kt | 13 ++++---- .../data/repos/token/TokenDataSource.kt | 3 +- .../data/repos/token/TokenLocalDataSource.kt | 3 +- .../data/repos/token/TokenRemoteDataSource.kt | 31 ++++++++----------- .../data/repos/token/TokenRepo.kt | 9 +++--- 5 files changed, 28 insertions(+), 31 deletions(-) diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt index 985bedc..368cf49 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt @@ -6,8 +6,7 @@ import android.content.Intent import android.preference.PreferenceManager import ca.bc.gov.mobileauthentication.common.Constants import ca.bc.gov.mobileauthentication.common.exceptions.TokenNotFoundException -import ca.bc.gov.mobileauthentication.common.utils.UrlUtils -import ca.bc.gov.mobileauthentication.data.AuthApi +import ca.bc.gov.mobileauthentication.data.AppAuthApi import ca.bc.gov.mobileauthentication.data.models.Token import ca.bc.gov.mobileauthentication.data.repos.token.TokenRepo import ca.bc.gov.mobileauthentication.di.Injection @@ -52,10 +51,10 @@ class MobileAuthenticationClient( private val gson: Gson = Injection.provideGson() private val grantType: String = Constants.GRANT_TYPE_AUTH_CODE - private val authApi: AuthApi = InjectionUtils.getAuthApi(UrlUtils.cleanBaseUrl(baseUrl)) + private val appauthApi: AppAuthApi = AppAuthApi(context, baseUrl, realmName, authEndpoint, redirectUri, clientId, hint) private val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context.applicationContext) private val tokenRepo: TokenRepo = InjectionUtils.getTokenRepo( - authApi, realmName, grantType, redirectUri, clientId, sharedPrefs) + appauthApi, realmName, grantType, redirectUri, clientId, sharedPrefs) private var passedRequestCode: Int = DEFAULT_REQUEST_CODE @@ -104,7 +103,7 @@ class MobileAuthenticationClient( * If a token does not exist a @see ca.bc.gov.mobileauthentication.common.exceptions.TokenNotFoundException will be thrown */ override fun getToken(tokenCallback: TokenCallback) { - tokenRepo.getToken() + tokenRepo.getToken(null) .firstElement() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).subscribeBy( @@ -123,7 +122,7 @@ class MobileAuthenticationClient( /** * Gets Token from local storage as a RxJava2 Observable */ - override fun getTokenAsObservable(): Observable = tokenRepo.getToken() + override fun getTokenAsObservable(): Observable = tokenRepo.getToken(null) /** * Refreshes token @@ -148,7 +147,7 @@ class MobileAuthenticationClient( /** * Refreshes Token that is stored in local storage as a RxJava2 Observable */ - override fun refreshTokenAsObservable(): Observable = tokenRepo.getToken() + override fun refreshTokenAsObservable(): Observable = tokenRepo.getToken(null) .flatMap { token -> tokenRepo.refreshToken(token) } /** diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenDataSource.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenDataSource.kt index 1876049..4d754ed 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenDataSource.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenDataSource.kt @@ -2,6 +2,7 @@ package ca.bc.gov.mobileauthentication.data.repos.token import ca.bc.gov.mobileauthentication.data.models.Token import io.reactivex.Observable +import net.openid.appauth.AuthorizationResponse /** * @@ -22,7 +23,7 @@ import io.reactivex.Observable */ interface TokenDataSource { - fun getToken(code: String? = null): Observable + fun getToken(authResponse: AuthorizationResponse? = null): Observable fun saveToken(token: Token): Observable diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenLocalDataSource.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenLocalDataSource.kt index aec6933..fcf1d61 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenLocalDataSource.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenLocalDataSource.kt @@ -4,6 +4,7 @@ import ca.bc.gov.mobileauthentication.common.exceptions.InvalidOperationExceptio import ca.bc.gov.mobileauthentication.data.models.Token import com.google.gson.Gson import io.reactivex.Observable +import net.openid.appauth.AuthorizationResponse /** * @@ -45,7 +46,7 @@ private constructor( /** * Gets token from local db and returns */ - override fun getToken(code: String?): Observable { + override fun getToken(authResponse: AuthorizationResponse?): Observable { return Observable.create { emitter -> val tokenJson = secureSharedPrefs.getString(TOKEN_KEY) if (tokenJson.isNotBlank()) { diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRemoteDataSource.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRemoteDataSource.kt index 1aaf13e..710860b 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRemoteDataSource.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRemoteDataSource.kt @@ -3,9 +3,10 @@ package ca.bc.gov.mobileauthentication.data.repos.token import ca.bc.gov.mobileauthentication.common.exceptions.InvalidOperationException import ca.bc.gov.mobileauthentication.common.exceptions.NoCodeException import ca.bc.gov.mobileauthentication.common.exceptions.NoRefreshTokenException -import ca.bc.gov.mobileauthentication.data.AuthApi +import ca.bc.gov.mobileauthentication.data.AppAuthApi import ca.bc.gov.mobileauthentication.data.models.Token import io.reactivex.Observable +import net.openid.appauth.AuthorizationResponse /** * @@ -26,7 +27,7 @@ import io.reactivex.Observable */ class TokenRemoteDataSource private constructor( - private val authApi: AuthApi, + private val authApi: AppAuthApi, private val realmName: String, private val grantType: String, private val redirectUri: String, @@ -39,7 +40,7 @@ private constructor( private var INSTANCE: TokenRemoteDataSource? = null fun getInstance( - authApi: AuthApi, + authApi: AppAuthApi, realmName: String, grantType: String, redirectUri: String, @@ -53,14 +54,11 @@ private constructor( * Exchanges code for token using authentication api * Returns error if code is null */ - override fun getToken(code: String?): Observable { - if (code == null) return Observable.error(NoCodeException()) - return authApi.getToken(realmName, grantType, redirectUri, clientId, code) - .map { token -> - token.expiresAt = System.currentTimeMillis() + ((token.expiresIn ?: 0) * 1000) - token.refreshExpiresAt = System.currentTimeMillis() + ((token.refreshExpiresIn ?: 0) * 1000) - token - } + override fun getToken(authResponse: AuthorizationResponse?): Observable { + if (authResponse == null) + return Observable.error(NoCodeException()) + + return authApi.getToken(authResponse) } /** @@ -75,13 +73,10 @@ private constructor( * Returns error if there is no refresh token */ override fun refreshToken(token: Token): Observable { - val refreshToken = token.refreshToken ?: return Observable.error(NoRefreshTokenException()) - return authApi.refreshToken(realmName, redirectUri, clientId, refreshToken) - .map { refreshedToken -> - refreshedToken.expiresAt = System.currentTimeMillis() + ((refreshedToken.expiresIn ?: 0) * 1000) - refreshedToken.refreshExpiresAt = System.currentTimeMillis() + ((refreshedToken.refreshExpiresIn ?: 0) * 1000) - refreshedToken - } + if (token.refreshToken == null) + return Observable.error(NoRefreshTokenException()) + + return authApi.refreshToken(token) } /** * Invalid operation for remote data source diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRepo.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRepo.kt index ef6c902..e40683a 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRepo.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRepo.kt @@ -3,6 +3,7 @@ package ca.bc.gov.mobileauthentication.data.repos.token import ca.bc.gov.mobileauthentication.common.exceptions.RefreshExpiredException import ca.bc.gov.mobileauthentication.data.models.Token import io.reactivex.Observable +import net.openid.appauth.AuthorizationResponse /** * @@ -52,12 +53,12 @@ private constructor( * If token from local db refresh is expired then @see ca.bc.gov.mobileauthentication.common.exceptions.RefreshExpiredException will be thrown. * If refresh token is not expired and token is expired then token will be refreshed and returned */ - override fun getToken(code: String?): Observable { - return if (code != null) { - remoteDataSource.getToken(code) + override fun getToken(authResponse: AuthorizationResponse?): Observable { + return if (authResponse != null) { + remoteDataSource.getToken(authResponse) .flatMap { localDataSource.saveToken(it) } } else { - localDataSource.getToken() + localDataSource.getToken(null) .flatMap { when { it.isRefreshExpired() -> Observable.error(RefreshExpiredException()) From 600e13610379cdcb933a2f866276d40359f9c89b Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 10:54:16 -0700 Subject: [PATCH 12/21] Refactor and add error handling --- .../MobileAuthenticationClient.kt | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt index 368cf49..8778ed9 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt @@ -63,6 +63,7 @@ class MobileAuthenticationClient( */ override fun authenticate(requestCode: Int) { this.passedRequestCode = requestCode + Intent(context, RedirectActivity::class.java) .putExtra(RedirectActivity.BASE_URL, baseUrl) .putExtra(RedirectActivity.REALM_NAME, realmName) @@ -77,21 +78,34 @@ class MobileAuthenticationClient( * Handles on activity result and determines if the authentication was successful * or an error occurred. */ - override fun handleAuthResult( - requestCode: Int, resultCode: Int, data: Intent?, - tokenCallback: TokenCallback) { - - if (resultCode == Activity.RESULT_OK && requestCode == passedRequestCode && data != null) { - val success = data.getBooleanExtra(MobileAuthenticationClient.SUCCESS, false) - if (success) { - val tokenJson = data.getStringExtra(MobileAuthenticationClient.TOKEN_JSON) - val token: Token = gson.fromJson(tokenJson, Token::class.java) - tokenCallback.onSuccess(token) - } - else { - val errorMessage = data.getStringExtra(MobileAuthenticationClient.ERROR_MESSAGE) - tokenCallback.onError(Throwable(errorMessage)) - } + override fun handleAuthResult(requestCode: Int, resultCode: Int, data: Intent?, + tokenCallback: TokenCallback) { + if (requestCode != passedRequestCode) + return + + if (resultCode != Activity.RESULT_OK) { + val message = data?.getStringExtra(ERROR_MESSAGE) ?: "Unknown authentication error" + tokenCallback.onError(Throwable(message)) + return + } + + if (data == null) { + tokenCallback.onError(Throwable("Result OK but authentication response is malformed")) + return + } + + handleConsumerResult(data, tokenCallback) + } + + private fun handleConsumerResult(data: Intent, tokenCallback: TokenCallback) { + val success = data.getBooleanExtra(SUCCESS, false) + if (success) { + val tokenJson = data.getStringExtra(TOKEN_JSON) + val token: Token = gson.fromJson(tokenJson, Token::class.java) + tokenCallback.onSuccess(token) + } else { + val errorMessage = data.getStringExtra(ERROR_MESSAGE) + tokenCallback.onError(Throwable(errorMessage)) } } From 75b2ca8856a796b91cfa2ca4995163e67da3c0e1 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 11:01:45 -0700 Subject: [PATCH 13/21] Implement new AppAuthApi flow in RedirectActivity - RedirectActivity now uses activity results for OAuth flow - Reworked CustomTabsIntent to use non-deprecated features - RedirectPresenter error checks Intent from result and initiates the Token exchange inside redirectReceived() --- .../MobileAuthenticationClient.kt | 1 + .../gov/mobileauthentication/di/Injection.kt | 3 +- .../mobileauthentication/di/InjectionUtils.kt | 4 +-- .../screens/redirect/RedirectActivity.kt | 33 +++++++++++++------ .../screens/redirect/RedirectContract.kt | 3 +- .../screens/redirect/RedirectPresenter.kt | 19 +++++++---- 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt index 8778ed9..3b9e867 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/MobileAuthenticationClient.kt @@ -203,6 +203,7 @@ class MobileAuthenticationClient( companion object { const val DEFAULT_REQUEST_CODE = 1012 + const val APPAUTH_REQUEST_CODE = 2024 const val SUCCESS = "SUCCESS" const val ERROR_MESSAGE = "ERROR_MESSAGE" const val TOKEN_JSON = "TOKEN_JSON" diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/Injection.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/Injection.kt index b0d47c2..61cb8f5 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/Injection.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/Injection.kt @@ -1,6 +1,7 @@ package ca.bc.gov.mobileauthentication.di import android.content.SharedPreferences +import ca.bc.gov.mobileauthentication.data.AppAuthApi import ca.bc.gov.mobileauthentication.data.AuthApi import ca.bc.gov.mobileauthentication.data.repos.token.SecureSharedPrefs import ca.bc.gov.mobileauthentication.data.repos.token.TokenLocalDataSource @@ -99,7 +100,7 @@ object Injection { // Token Repo @JvmStatic fun provideTokenRepo( - authApi: AuthApi, + authApi: AppAuthApi, realmName: String, grantType: String, redirectUri: String, diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/InjectionUtils.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/InjectionUtils.kt index 8eb5904..181bafe 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/InjectionUtils.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/InjectionUtils.kt @@ -3,6 +3,7 @@ package ca.bc.gov.mobileauthentication.di import android.content.SharedPreferences import ca.bc.gov.mobileauthentication.data.AuthApi import ca.bc.gov.mobileauthentication.common.Constants +import ca.bc.gov.mobileauthentication.data.AppAuthApi import ca.bc.gov.mobileauthentication.data.repos.token.SecureSharedPrefs import ca.bc.gov.mobileauthentication.data.repos.token.TokenLocalDataSource import ca.bc.gov.mobileauthentication.data.repos.token.TokenRemoteDataSource @@ -57,7 +58,7 @@ object InjectionUtils { * Gets Token Repo with standard params */ fun getTokenRepo( - authApi: AuthApi, + authApi: AppAuthApi, realmName: String, grantType: String, redirectUri: String, @@ -70,5 +71,4 @@ object InjectionUtils { TokenRemoteDataSource.getInstance(authApi, realmName, grantType, redirectUri, clientId), TokenLocalDataSource.getInstance(gson, secureSharedPrefs) ) - } \ No newline at end of file diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt index ac6896b..63862c5 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt @@ -2,7 +2,6 @@ package ca.bc.gov.mobileauthentication.screens.redirect import android.app.Activity import android.content.Intent -import android.net.Uri import android.os.Bundle import android.preference.PreferenceManager import android.widget.Toast @@ -12,9 +11,10 @@ import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat import androidx.appcompat.app.AppCompatActivity import android.view.View +import androidx.browser.customtabs.CustomTabColorSchemeParams import ca.bc.gov.mobileauthentication.MobileAuthenticationClient import ca.bc.gov.mobileauthentication.common.Constants -import ca.bc.gov.mobileauthentication.common.utils.UrlUtils +import ca.bc.gov.mobileauthentication.data.AppAuthApi import ca.bc.gov.mobileauthentication.di.Injection import kotlinx.android.synthetic.main.activity_login.* @@ -41,6 +41,8 @@ class RedirectActivity : AppCompatActivity(), RedirectContract.View { override var loading: Boolean = false + private lateinit var appauthApi: AppAuthApi + companion object { const val BASE_URL = "BASE_URL" const val REALM_NAME = "REALM_NAME" @@ -97,10 +99,10 @@ class RedirectActivity : AppCompatActivity(), RedirectContract.View { val grantType = Constants.GRANT_TYPE_AUTH_CODE val responseType = Constants.RESPONSE_TYPE_CODE - val authApi = InjectionUtils.getAuthApi(UrlUtils.cleanBaseUrl(baseUrl)) + appauthApi = AppAuthApi(this, baseUrl, realmName, authEndpoint, redirectUri, clientId, hint) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext) val tokenRepo = InjectionUtils.getTokenRepo( - authApi, realmName, grantType, redirectUri, clientId, sharedPreferences) + appauthApi, realmName, grantType, redirectUri, clientId, sharedPreferences) val gson = Injection.provideGson() @@ -128,10 +130,15 @@ class RedirectActivity : AppCompatActivity(), RedirectContract.View { checkIntentForRedirect(intent) } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + if (requestCode == MobileAuthenticationClient.APPAUTH_REQUEST_CODE) + checkIntentForRedirect(data) + } + private fun checkIntentForRedirect(intent: Intent?) { - if (intent != null && intent.action == Intent.ACTION_VIEW && intent.data != null) { - presenter?.redirectReceived(intent.data.toString()) - } + presenter?.redirectReceived(intent) } // Loading @@ -162,12 +169,18 @@ class RedirectActivity : AppCompatActivity(), RedirectContract.View { * Goes to Chrome custom tab */ override fun loadWithChrome(url: String) { - CustomTabsIntent.Builder() - .addDefaultShareMenuItem() + val colorScheme = CustomTabColorSchemeParams.Builder() .setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary)) + .build() + + val customTabsIntent = CustomTabsIntent.Builder() + .setShareState(CustomTabsIntent.SHARE_STATE_OFF) + .setDefaultColorSchemeParams(colorScheme) .setShowTitle(true) .build() - .launchUrl(this, Uri.parse(url)) + + val authIntent = appauthApi.getAuthRequestIntent(customTabsIntent) + startActivityForResult(authIntent, MobileAuthenticationClient.APPAUTH_REQUEST_CODE) } // Results diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectContract.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectContract.kt index 5ae4acf..a151248 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectContract.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectContract.kt @@ -1,5 +1,6 @@ package ca.bc.gov.mobileauthentication.screens.redirect +import android.content.Intent import ca.bc.gov.mobileauthentication.common.BasePresenter import ca.bc.gov.mobileauthentication.common.BaseView @@ -43,7 +44,7 @@ interface RedirectContract { interface Presenter: BasePresenter { fun loginClicked() - fun redirectReceived(redirectUrl: String) + fun redirectReceived(redirectIntent: Intent?) } } \ No newline at end of file diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectPresenter.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectPresenter.kt index 276fcf1..f01dc96 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectPresenter.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectPresenter.kt @@ -1,5 +1,6 @@ package ca.bc.gov.mobileauthentication.screens.redirect +import android.content.Intent import ca.bc.gov.mobileauthentication.common.utils.UrlUtils import ca.bc.gov.mobileauthentication.data.repos.token.TokenRepo import com.google.gson.Gson @@ -8,6 +9,7 @@ import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.addTo import io.reactivex.rxkotlin.subscribeBy import io.reactivex.schedulers.Schedulers +import net.openid.appauth.AuthorizationResponse /** * @@ -61,20 +63,23 @@ class RedirectPresenter( "$authEndpoint?response_type=$responseType&client_id=$clientId&redirect_uri=$redirectUri&kc_idp_hint=$hint" // Redirect - override fun redirectReceived(redirectUrl: String) { - if (!redirectUrl.contains("code=".toRegex())) { + override fun redirectReceived(redirectIntent: Intent?) { + if (redirectIntent == null) return - } - val code = UrlUtils.extractCode(redirectUrl) - getToken(code) + val response = AuthorizationResponse.fromIntent(redirectIntent) ?: return + + if (response.authorizationCode == null || response.authorizationCode!!.isEmpty()) + return + + getToken(response) } /** * Gets token remotely using Authorization Code and saves locally */ - fun getToken(code: String) { - tokenRepo.getToken(code) + fun getToken(authResponse: AuthorizationResponse) { + tokenRepo.getToken(authResponse) .firstOrError() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) From 2a88ef38ee5d1ade28e9285e61a249c52809624f Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 11:22:49 -0700 Subject: [PATCH 14/21] Make RedirectActivity a single instance activity --- mobileauthentication/src/main/AndroidManifest.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mobileauthentication/src/main/AndroidManifest.xml b/mobileauthentication/src/main/AndroidManifest.xml index 743a525..22a4fd5 100644 --- a/mobileauthentication/src/main/AndroidManifest.xml +++ b/mobileauthentication/src/main/AndroidManifest.xml @@ -4,8 +4,9 @@ - + android:theme="@style/AppTheme" + android:launchMode="singleInstance" /> + From 7cc89303f34c8325da14246b0e387ed35ddd72a6 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 11:23:58 -0700 Subject: [PATCH 15/21] Remove RedirectActivity from demo module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No longer needed – appAuthRedirectScheme in build.gradle replaces it --- app/src/main/AndroidManifest.xml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f0f7650..fdd0973 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,24 +13,14 @@ android:supportsRtl="true" android:theme="@style/AppTheme"> - + - - - - - - - - - \ No newline at end of file From 46fb278d5272ac4016d11ab9ede84b585e6b14f6 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 11:29:37 -0700 Subject: [PATCH 16/21] Add explanatory comment to demo module's build.gradle --- app/build.gradle | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 513af8f..c15782b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,17 @@ android { testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' manifestPlaceholders = [ - 'appAuthRedirectScheme': 'secure-image://client' + /* + Instead of an in AndroidManifest.xml the redirectUri scheme is + defined here. Only give the scheme itself: + + 'appAuthRedirectScheme': 'custom-scheme' <- happiness + + Adding any more isn't valid: + + 'appAuthRedirectScheme': 'custom-scheme://somePath' <- runtime issues + */ + 'appAuthRedirectScheme': 'secure-image' ] } buildTypes { From 2a7e17e04802ae30a728b1ad985a761db549272e Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 13:05:11 -0700 Subject: [PATCH 17/21] Clean codebase - Remove unused items - Fix typos - Fix kotlin lint items --- README.md | 6 +- app/src/main/res/layout/activity_main.xml | 1 - mobileauthentication/build.gradle | 3 - .../MobileAuthenticationClient.kt | 5 +- .../mobileauthentication/common/Constants.kt | 3 - .../exceptions/InvalidOperationException.kt | 2 +- .../common/exceptions/NoCodeException.kt | 2 +- .../exceptions/NoRefreshTokenException.kt | 2 +- .../exceptions/RefreshExpiredException.kt | 2 +- .../exceptions/TokenNotFoundException.kt | 2 +- .../gov/mobileauthentication/data/AuthApi.kt | 55 ------------- .../data/repos/token/SecureSharedPrefs.kt | 2 +- .../data/repos/token/TokenRemoteDataSource.kt | 18 +---- .../gov/mobileauthentication/di/Injection.kt | 77 ------------------- .../mobileauthentication/di/InjectionUtils.kt | 32 +------- .../screens/redirect/RedirectActivity.kt | 14 ++-- .../screens/redirect/RedirectPresenter.kt | 1 - 17 files changed, 20 insertions(+), 207 deletions(-) delete mode 100644 mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AuthApi.kt diff --git a/README.md b/README.md index e619035..e256d5a 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ The parameters needed for the client can all be found on your Red Hat Single Sig The hint param is optional and can be used to directly send the user to the specified login. -We recommened you use a custom application schema for your redirectUri such as ://android +We recommend you use a custom application schema for your redirectUri such as ://android Please use the context of the activity in which you are going to call authenticate to create the client. @@ -100,7 +100,7 @@ If the token is expired and the refresh token is NOT expired the token will auto Exceptions: 1. If there is no token locally a `TokenNotFoundException` will be thrown in the onError of the TokenCallback. This means the user has not yet been authenticated so no token exists locally. -2. If the token's refresh token is expired a `RefreshExpiredException` will be thrown in the onError of the TokenCallback. In this case the user will need to reauthenticate. +2. If the token's refresh token is expired a `RefreshExpiredException` will be thrown in the onError of the TokenCallback. In this case the user will need to re-authenticate. 3. If the token does not have a refresh token then a `NoRefreshTokenException` will be thrown in the onError of the TokenCallback. This means the Token data being returned does not contain the required refreshToken for this lib to work. Calling getToken: @@ -130,7 +130,7 @@ client?.getToken(object: MobileAuthenticationClient.TokenCallback { Refresh token will refresh the locally stored token. Exceptions: -1. If the token's refresh token is expired a `RefreshExpiredException` will be thrown in the onError of the TokenCallback. In this case the user will need to reauthenticate. +1. If the token's refresh token is expired a `RefreshExpiredException` will be thrown in the onError of the TokenCallback. In this case the user will need to re-authenticate. 2. If the token does not have a refresh token then a `NoRefreshTokenException` will be thrown in the onError of the TokenCallback. This means the Token data being returned does not contain the required refreshToken for this lib to work. Calling refreshToken: diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4cc4bf4..70be574 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,5 @@ - - /** - * OAuth2 refresh Token call - */ - @POST("/auth/realms/{realm_name}/protocol/openid-connect/token") - @FormUrlEncoded - fun refreshToken( - @Path("realm_name") realmName: String, - @Field("redirect_uri") redirectUri: String, - @Field("client_id") client_id: String, - @Field("refresh_token") refreshToken: String, - @Field("grant_type") grantType: String = "refresh_token" - ): Observable - -} \ No newline at end of file diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/SecureSharedPrefs.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/SecureSharedPrefs.kt index 887c526..7eb2410 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/SecureSharedPrefs.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/SecureSharedPrefs.kt @@ -154,7 +154,7 @@ class SecureSharedPrefs( } companion object { - private val AES_TRANSFORMATION = "AES/GCM/NoPadding" + private const val AES_TRANSFORMATION = "AES/GCM/NoPadding" } } \ No newline at end of file diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRemoteDataSource.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRemoteDataSource.kt index 710860b..16fff34 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRemoteDataSource.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/repos/token/TokenRemoteDataSource.kt @@ -26,27 +26,15 @@ import net.openid.appauth.AuthorizationResponse * */ class TokenRemoteDataSource -private constructor( - private val authApi: AppAuthApi, - private val realmName: String, - private val grantType: String, - private val redirectUri: String, - private val clientId: String -) : TokenDataSource { +private constructor(private val authApi: AppAuthApi) : TokenDataSource { companion object { @Volatile private var INSTANCE: TokenRemoteDataSource? = null - fun getInstance( - authApi: AppAuthApi, - realmName: String, - grantType: String, - redirectUri: String, - clientId: String): TokenRemoteDataSource = INSTANCE ?: synchronized(this) { - INSTANCE ?: TokenRemoteDataSource( - authApi, realmName, grantType, redirectUri, clientId).also { INSTANCE = it } + fun getInstance(authApi: AppAuthApi): TokenRemoteDataSource = INSTANCE ?: synchronized(this) { + INSTANCE ?: TokenRemoteDataSource(authApi).also { INSTANCE = it } } } diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/Injection.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/Injection.kt index 61cb8f5..9cbd201 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/Injection.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/Injection.kt @@ -1,23 +1,10 @@ package ca.bc.gov.mobileauthentication.di import android.content.SharedPreferences -import ca.bc.gov.mobileauthentication.data.AppAuthApi -import ca.bc.gov.mobileauthentication.data.AuthApi import ca.bc.gov.mobileauthentication.data.repos.token.SecureSharedPrefs -import ca.bc.gov.mobileauthentication.data.repos.token.TokenLocalDataSource -import ca.bc.gov.mobileauthentication.data.repos.token.TokenRemoteDataSource -import ca.bc.gov.mobileauthentication.data.repos.token.TokenRepo import com.google.gson.Gson import com.google.gson.GsonBuilder -import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.CallAdapter -import retrofit2.Converter -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory import java.security.KeyStore -import java.util.concurrent.TimeUnit /** * @@ -38,25 +25,6 @@ import java.util.concurrent.TimeUnit */ object Injection { - // OkHttpClient - @JvmStatic - fun provideOkHttpClient( - readTimeOut: Long, - connectTimeOut: Long, - interceptor: HttpLoggingInterceptor - ): OkHttpClient = OkHttpClient.Builder() - .readTimeout(readTimeOut, TimeUnit.SECONDS) - .connectTimeout(connectTimeOut, TimeUnit.SECONDS) - .addInterceptor(interceptor) - .build() - - // Logging interceptor - @JvmStatic - fun provideHttpLoggingInterceptor( - loggingLevel: HttpLoggingInterceptor.Level - ): HttpLoggingInterceptor = HttpLoggingInterceptor() - .apply { level = loggingLevel } - // Gson private var cachedGson: Gson? = null @@ -66,51 +34,6 @@ object Injection { .create() .also { cachedGson = it } - // Converter Factory - @JvmStatic - fun provideConverterFactory(gson: Gson): Converter.Factory = GsonConverterFactory.create(gson) - - // Call Adapter Factory - private var cachedCallAdapterFactory: CallAdapter.Factory? = null - - @JvmStatic - fun provideCallAdapterFactory(): CallAdapter.Factory = cachedCallAdapterFactory - ?: - RxJava2CallAdapterFactory.create() - .also { cachedCallAdapterFactory = it } - - // Retrofit - @JvmStatic - fun provideRetrofit( - apiDomain: String, - okHttpClient: OkHttpClient, - converterFactory: Converter.Factory, - callAdapterFactory: CallAdapter.Factory - ): Retrofit = Retrofit.Builder() - .baseUrl(apiDomain) - .client(okHttpClient) - .addConverterFactory(converterFactory) - .addCallAdapterFactory(callAdapterFactory) - .build() - - // Auth Api - @JvmStatic - fun provideAuthApi(retrofit: Retrofit): AuthApi = retrofit.create(AuthApi::class.java) - - // Token Repo - @JvmStatic - fun provideTokenRepo( - authApi: AppAuthApi, - realmName: String, - grantType: String, - redirectUri: String, - clientId: String, - gson: Gson, - secureSharedPrefs: SecureSharedPrefs - ): TokenRepo = TokenRepo.getInstance( - TokenRemoteDataSource.getInstance(authApi, realmName, grantType, redirectUri, clientId), - TokenLocalDataSource.getInstance(gson, secureSharedPrefs)) - // Secure shared prefs @JvmStatic fun provideSecureSharedPrefs( diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/InjectionUtils.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/InjectionUtils.kt index 181bafe..4607886 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/InjectionUtils.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/di/InjectionUtils.kt @@ -1,19 +1,12 @@ package ca.bc.gov.mobileauthentication.di import android.content.SharedPreferences -import ca.bc.gov.mobileauthentication.data.AuthApi -import ca.bc.gov.mobileauthentication.common.Constants import ca.bc.gov.mobileauthentication.data.AppAuthApi import ca.bc.gov.mobileauthentication.data.repos.token.SecureSharedPrefs import ca.bc.gov.mobileauthentication.data.repos.token.TokenLocalDataSource import ca.bc.gov.mobileauthentication.data.repos.token.TokenRemoteDataSource import ca.bc.gov.mobileauthentication.data.repos.token.TokenRepo import com.google.gson.Gson -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.CallAdapter -import retrofit2.Converter -import retrofit2.Retrofit import java.security.KeyStore /** @@ -35,40 +28,17 @@ import java.security.KeyStore */ object InjectionUtils { - /** - * Gets Auth Api with standard params - */ - fun getAuthApi( - apiDomain: String, - gson: Gson = Injection.provideGson(), - converterFactory: Converter.Factory = Injection.provideConverterFactory(gson), - callAdapterFactory : CallAdapter.Factory = Injection.provideCallAdapterFactory(), - readTimeOut: Long = Constants.READ_TIME_OUT, - connectTimeOut: Long = Constants.CONNECT_TIME_OUT, - loggingLevel: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.BODY, - httpLoggingInterceptor: HttpLoggingInterceptor = Injection.provideHttpLoggingInterceptor( - loggingLevel), - okHttpClient: OkHttpClient = Injection.provideOkHttpClient( - readTimeOut, connectTimeOut, httpLoggingInterceptor), - retrofit: Retrofit = Injection.provideRetrofit( - apiDomain, okHttpClient, converterFactory, callAdapterFactory) - ): AuthApi = Injection.provideAuthApi(retrofit) - /** * Gets Token Repo with standard params */ fun getTokenRepo( authApi: AppAuthApi, - realmName: String, - grantType: String, - redirectUri: String, - clientId: String, sharedPreferences: SharedPreferences, gson: Gson = Injection.provideGson(), keyStore: KeyStore = Injection.provideKeyStore(), secureSharedPrefs: SecureSharedPrefs = Injection.provideSecureSharedPrefs(keyStore, sharedPreferences) ): TokenRepo = TokenRepo.getInstance( - TokenRemoteDataSource.getInstance(authApi, realmName, grantType, redirectUri, clientId), + TokenRemoteDataSource.getInstance(authApi), TokenLocalDataSource.getInstance(gson, secureSharedPrefs) ) } \ No newline at end of file diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt index 63862c5..8e1afcc 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt @@ -4,18 +4,18 @@ import android.app.Activity import android.content.Intent import android.os.Bundle import android.preference.PreferenceManager +import android.view.View import android.widget.Toast -import ca.bc.gov.mobileauthentication.R -import ca.bc.gov.mobileauthentication.di.InjectionUtils -import androidx.browser.customtabs.CustomTabsIntent -import androidx.core.content.ContextCompat import androidx.appcompat.app.AppCompatActivity -import android.view.View import androidx.browser.customtabs.CustomTabColorSchemeParams +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.content.ContextCompat import ca.bc.gov.mobileauthentication.MobileAuthenticationClient +import ca.bc.gov.mobileauthentication.R import ca.bc.gov.mobileauthentication.common.Constants import ca.bc.gov.mobileauthentication.data.AppAuthApi import ca.bc.gov.mobileauthentication.di.Injection +import ca.bc.gov.mobileauthentication.di.InjectionUtils import kotlinx.android.synthetic.main.activity_login.* /** @@ -96,13 +96,11 @@ class RedirectActivity : AppCompatActivity(), RedirectContract.View { } // Building presenter params - val grantType = Constants.GRANT_TYPE_AUTH_CODE val responseType = Constants.RESPONSE_TYPE_CODE appauthApi = AppAuthApi(this, baseUrl, realmName, authEndpoint, redirectUri, clientId, hint) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext) - val tokenRepo = InjectionUtils.getTokenRepo( - appauthApi, realmName, grantType, redirectUri, clientId, sharedPreferences) + val tokenRepo = InjectionUtils.getTokenRepo(appauthApi, sharedPreferences) val gson = Injection.provideGson() diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectPresenter.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectPresenter.kt index f01dc96..a90323a 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectPresenter.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectPresenter.kt @@ -1,7 +1,6 @@ package ca.bc.gov.mobileauthentication.screens.redirect import android.content.Intent -import ca.bc.gov.mobileauthentication.common.utils.UrlUtils import ca.bc.gov.mobileauthentication.data.repos.token.TokenRepo import com.google.gson.Gson import io.reactivex.android.schedulers.AndroidSchedulers From 242c8a14accaff4372c49b66cf77015ce7af938e Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 13:21:18 -0700 Subject: [PATCH 18/21] Update README with changes --- README.md | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e256d5a..bc60760 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ MobileAuthentication supports Android API 23 and above so AES encryption can be ## Getting Started project build.gradle: -``` +```groovy allprojects { repositories { ... @@ -16,7 +16,7 @@ allprojects { ``` app build.gradle: -``` +```groovy dependencies { implementation "com.github.bcgov:mobile-authentication-android:" } @@ -25,20 +25,34 @@ dependencies { ### Prerequisites A Red Hat Single Sign-On server component that is setup to handle an OAuth2 authorization code flow. -### Android Manifest -You will need to add this to your AndroidManifest and specify what custom schema you're using in your redirectUri. -```xml - - - - - - - - +### Handling Custom Scheme Redirects + +Specify the redirect value by setting the manifest placeholder value `appAuthRedirectScheme` in your project's build.gradle file: + +```groovy +android { + defaultConfig { + ... + + manifestPlaceholders = [ + 'appAuthRedirectScheme': '' + ] + + ... + } +} ``` +Only the scheme is required: + +> 'appAuthRedirectScheme': 'custom-scheme' <- happiness + +Adding any more isn't valid: + +> 'appAuthRedirectScheme': 'custom-scheme://somePath' <- runtime issues + +***Note:*** you were previously required to add RedirectActivity with an intent filter to your application's manifest. This is no longer required because the functionality is handled by this manifest placeholder value. + ## Usage There are four main commands for handling tokens that will be called using the MobileAuthenticationClient class. 1. Authenticate From 4aec9a461be131a05680a3a0c63b4b961c3dcfcb Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Wed, 21 Sep 2022 15:45:10 -0700 Subject: [PATCH 19/21] Refactor and fix expiration times implementation --- .../mobileauthentication/data/AppAuthApi.kt | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt index 28c64d6..f4c4fbe 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt @@ -56,25 +56,29 @@ class AppAuthApi(private val context: Context, } private fun convertToToken(tokenResponse: TokenResponse): Token { - val accessExpirationTime = tokenResponse.accessTokenExpirationTime - val accessExpirationUnixTime = accessExpirationTime?.div(1000) - val accessExpiresInMillis = accessExpirationUnixTime?.minus(System.currentTimeMillis()) + val current = System.currentTimeMillis() + val currentUnixTime = current / 1000 - val refreshExpiresIn = tokenResponse.additionalParameters[REFRESH_EXPIRES_IN]?.toLong() - val refreshExpiresInMillis = refreshExpiresIn?.times(1000) - val refreshExpirationUnixTime = refreshExpiresInMillis?.plus(System.currentTimeMillis()) + val accessExpirationUnixTimeMillis = tokenResponse.accessTokenExpirationTime + val accessExpirationUnixTime = accessExpirationUnixTimeMillis?.div(1000) + val accessExpiresInSeconds = accessExpirationUnixTime?.minus(currentUnixTime) + val accessExpiresInMillis = accessExpiresInSeconds?.times(1000) + + val refreshExpiresInSeconds = tokenResponse.additionalParameters[REFRESH_EXPIRES_IN]?.toLong() + val refreshExpirationUnixTime = refreshExpiresInSeconds?.plus(currentUnixTime) + val refreshExpirationUnixTimeMillis = refreshExpirationUnixTime?.times(1000) return Token( tokenResponse.accessToken, accessExpiresInMillis, - refreshExpiresIn, + refreshExpiresInSeconds, tokenResponse.refreshToken, tokenResponse.tokenType, tokenResponse.idToken, tokenResponse.additionalParameters[NOT_BEFORE_POLICY]?.toLong(), tokenResponse.additionalParameters[SESSION_STATE], - accessExpirationUnixTime, - refreshExpirationUnixTime + accessExpirationUnixTimeMillis, + refreshExpirationUnixTimeMillis ) } From 4aebea8c1f73b8f763e55048338cd3ce8afe1287 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Thu, 22 Sep 2022 13:02:39 -0700 Subject: [PATCH 20/21] Make token requests on IO thread --- .../java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt index f4c4fbe..f648b6f 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/data/AppAuthApi.kt @@ -7,6 +7,7 @@ import androidx.browser.customtabs.CustomTabsIntent import ca.bc.gov.mobileauthentication.common.utils.UrlUtils import ca.bc.gov.mobileauthentication.data.models.Token import io.reactivex.Observable +import io.reactivex.schedulers.Schedulers import net.openid.appauth.* @@ -103,7 +104,7 @@ class AppAuthApi(private val context: Context, else if (ex != null) it.onError(ex) } - } + }.subscribeOn(Schedulers.io()) } /** From c626aff5267afd42bb43e807c8463233b40075a6 Mon Sep 17 00:00:00 2001 From: Todd Scharien Date: Thu, 22 Sep 2022 16:09:49 -0700 Subject: [PATCH 21/21] Show better descriptive message on cancel --- .../screens/redirect/RedirectActivity.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt index 8e1afcc..4ecedb7 100644 --- a/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt +++ b/mobileauthentication/src/main/java/ca/bc/gov/mobileauthentication/screens/redirect/RedirectActivity.kt @@ -195,4 +195,9 @@ class RedirectActivity : AppCompatActivity(), RedirectContract.View { data.putExtra(MobileAuthenticationClient.TOKEN_JSON, tokenJson) setResult(Activity.RESULT_OK, data) } + + override fun onBackPressed() { + setResultError("Login cancelled by user") + super.onBackPressed() + } }