├── demo-android ├── .gitignore ├── src │ └── main │ │ ├── ic_launcher-playstore.png │ │ ├── res │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── values │ │ │ ├── ic_launcher_background.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── styles.xml │ │ │ └── strings.xml │ │ ├── layout │ │ │ ├── activity_login.xml │ │ │ ├── activity_main.xml │ │ │ ├── listitem_repository.xml │ │ │ └── activity_repository_list.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ ├── xml │ │ │ └── authenticator.xml │ │ ├── drawable │ │ │ ├── ic_baseline_lock_24.xml │ │ │ ├── ic_baseline_lock_open_24.xml │ │ │ └── ic_launcher_foreground.xml │ │ └── menu │ │ │ └── menu.xml │ │ ├── java │ │ └── com │ │ │ └── andretietz │ │ │ └── retroauth │ │ │ └── demo │ │ │ ├── RetroauthDemoApplication.kt │ │ │ ├── screen │ │ │ └── main │ │ │ │ ├── MainViewState.kt │ │ │ │ ├── SwitchAccountContract.kt │ │ │ │ ├── RepositoryAdapter.kt │ │ │ │ ├── MainViewModel.kt │ │ │ │ └── MainActivity.kt │ │ │ ├── api │ │ │ └── GithubApi.kt │ │ │ ├── auth │ │ │ ├── DemoAuthenticationService.kt │ │ │ ├── GithubAuthenticator.kt │ │ │ └── LoginActivity.kt │ │ │ └── di │ │ │ └── ApiModule.kt │ │ └── AndroidManifest.xml └── build.gradle.kts ├── android-accountmanager ├── proguard-rules.pro ├── src │ ├── test │ │ ├── resources │ │ │ └── mockito-extensions │ │ │ │ └── org.mockito.plugins.MockMaker │ │ └── java │ │ │ └── com │ │ │ └── andretietz │ │ │ └── retroauth │ │ │ ├── ActivityManagerTest.kt │ │ │ ├── WeakActivityStackTest.kt │ │ │ └── AccountAuthenticatorTest.kt │ └── main │ │ ├── java │ │ └── com │ │ │ └── andretietz │ │ │ └── retroauth │ │ │ ├── RetroauthInitProvider.kt │ │ │ ├── RetroauthAndroid.kt │ │ │ ├── AuthenticationService.kt │ │ │ ├── WeakActivityStack.kt │ │ │ ├── AndroidAccountManagerOwnerStorage.kt │ │ │ ├── ActivityManager.kt │ │ │ ├── AndroidAccountManagerCredentialStorage.kt │ │ │ ├── AccountAuthenticator.kt │ │ │ └── AuthenticationActivity.kt │ │ └── AndroidManifest.xml ├── gradle.properties ├── build.gradle.kts └── README.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── retroauth ├── gradle.properties ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── andretietz │ │ │ └── retroauth │ │ │ ├── RequestType.kt │ │ │ ├── Credentials.kt │ │ │ ├── AuthenticationRequiredException.kt │ │ │ ├── AuthenticationCanceledException.kt │ │ │ ├── Authenticated.kt │ │ │ ├── Retroauth.kt │ │ │ ├── CredentialStorage.kt │ │ │ ├── OwnerStorage.kt │ │ │ ├── Authenticator.kt │ │ │ └── CredentialInterceptor.kt │ └── test │ │ └── java │ │ └── com │ │ └── andretietz │ │ └── retroauth │ │ ├── CredentialTest.kt │ │ ├── MockServerRule.kt │ │ ├── LockingTest.kt │ │ └── CredentialInterceptorTest.kt ├── build.gradle.kts └── README.md ├── .gitignore ├── sqlite ├── gradle.properties ├── src │ ├── main │ │ └── kotlin │ │ │ └── com │ │ │ └── andretietz │ │ │ └── retroauth │ │ │ ├── sqlite │ │ │ ├── data │ │ │ │ └── Account.kt │ │ │ ├── TableDefinitions.kt │ │ │ └── EntityDefinitions.kt │ │ │ ├── Main.kt │ │ │ ├── SQLiteOwnerStore.kt │ │ │ └── SQLiteCredentialStore.kt │ └── test │ │ └── kotlin │ │ └── com │ │ └── andretietz │ │ └── retroauth │ │ └── SQLiteOwnerStoreTest.kt └── build.gradle.kts ├── settings.gradle.kts ├── .editorconfig ├── .github └── workflows │ ├── pr_build.yml │ ├── snapshot.yml │ └── release.yml ├── gradle.properties ├── quality ├── detekt.yml ├── lint.xml └── checkstyle.xml ├── README.md ├── gradlew.bat ├── gradlew ├── CHANGELOG.md └── LICENSE /demo-android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android-accountmanager/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android-accountmanager/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /retroauth/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=retroauth 2 | POM_NAME=Retroauth 3 | POM_DESCRIPTION=Retroauth base project 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | gradle-local.properties 5 | .idea 6 | .DS_Store 7 | build 8 | /captures 9 | -------------------------------------------------------------------------------- /demo-android/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sqlite/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=sqlite 2 | POM_NAME=Implementation of the Credential- and OwnerStorage using sqlite as persistence. 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /retroauth/src/main/java/com/andretietz/retroauth/RequestType.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth 2 | 3 | data class RequestType( 4 | val credentialType: String 5 | ) 6 | -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "retroauth-root" 2 | 3 | include( 4 | ":retroauth", 5 | ":android-accountmanager", 6 | ":sqlite", 7 | ":demo-android", 8 | ) 9 | -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andretietz/retroauth/HEAD/demo-android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /demo-android/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /sqlite/src/main/kotlin/com/andretietz/retroauth/sqlite/data/Account.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth.sqlite.data 2 | 3 | data class Account( 4 | val id: Int, 5 | val name: String, 6 | val email: String 7 | ) 8 | -------------------------------------------------------------------------------- /retroauth/src/main/java/com/andretietz/retroauth/Credentials.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth 2 | 3 | class Credentials @JvmOverloads constructor( 4 | val token: String, 5 | val data: Map? = null 6 | ) 7 | -------------------------------------------------------------------------------- /android-accountmanager/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=android-accountmanager 2 | POM_NAME=Retroauth: Android Account Manager 3 | POM_DESCRIPTION=Android implementation of retroauth, using the Android AccountManager as Credential- and OwnerStorage 4 | -------------------------------------------------------------------------------- /demo-android/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /demo-android/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon May 22 11:49:44 PDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip 7 | -------------------------------------------------------------------------------- /demo-android/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo-android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo-android/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /demo-android/src/main/res/xml/authenticator.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /demo-android/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /retroauth/src/test/java/com/andretietz/retroauth/CredentialTest.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | 7 | class CredentialTest { 8 | @Test 9 | fun dataCheck() { 10 | val credentials = Credentials("token", mapOf("refresh" to "refresh")) 11 | assertEquals("token", credentials.token) 12 | assertEquals("refresh", requireNotNull(credentials.data)["refresh"]) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /demo-android/src/main/java/com/andretietz/retroauth/demo/RetroauthDemoApplication.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth.demo 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | //import dagger.hilt.android.HiltAndroidApp 6 | import timber.log.Timber 7 | 8 | @HiltAndroidApp 9 | class RetroauthDemoApplication : Application() { 10 | override fun onCreate() { 11 | super.onCreate() 12 | Timber.plant(Timber.DebugTree()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Unix-style newlines with a newline ending every file 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | charset = utf-8 8 | indent_style = space 9 | ij_formatter_tags_enabled = true 10 | ij_formatter_off_tag = @formatter:off 11 | ij_formatter_on_tag = @formatter:on 12 | 13 | [*.{kt, kts, java}] 14 | indent_size = 2 15 | trim_trailing_whitespace = true 16 | # no star imports! 17 | ij_kotlin_name_count_to_use_star_import = 999 18 | ij_kotlin_name_count_to_use_star_import_for_members = 999 19 | -------------------------------------------------------------------------------- /demo-android/src/main/java/com/andretietz/retroauth/demo/screen/main/MainViewState.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth.demo.screen.main 2 | 3 | import com.andretietz.retroauth.demo.api.GithubApi 4 | 5 | sealed class MainViewState { 6 | object InitialState : MainViewState() 7 | data class LoginSuccess(val account: OWNER) : MainViewState() 8 | object LogoutSuccess : MainViewState() 9 | data class Error(val throwable: Throwable) : MainViewState() 10 | class RepositoryUpdate( 11 | val repos: List 12 | ) : MainViewState() 13 | } 14 | -------------------------------------------------------------------------------- /retroauth/src/test/java/com/andretietz/retroauth/MockServerRule.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth 2 | 3 | import okhttp3.mockwebserver.MockWebServer 4 | import org.junit.rules.TestRule 5 | import org.junit.runner.Description 6 | import org.junit.runners.model.Statement 7 | 8 | class MockServerRule : TestRule { 9 | val server = MockWebServer() 10 | override fun apply(base: Statement, description: Description) = object : Statement() { 11 | override fun evaluate() { 12 | server.start() 13 | base.evaluate() 14 | server.shutdown() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /demo-android/src/main/res/drawable/ic_baseline_lock_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /demo-android/src/main/res/drawable/ic_baseline_lock_open_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /demo-android/src/main/res/menu/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /demo-android/src/main/java/com/andretietz/retroauth/demo/api/GithubApi.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth.demo.api 2 | 3 | import com.andretietz.retroauth.Authenticated 4 | import com.squareup.moshi.JsonClass 5 | import retrofit2.http.GET 6 | 7 | interface GithubApi { 8 | 9 | /** 10 | * https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user 11 | */ 12 | @Authenticated 13 | @GET("user/repos?visibility=all") 14 | suspend fun getRepositories(): List 15 | 16 | @JsonClass(generateAdapter = true) 17 | data class Repository( 18 | val id: String, 19 | val name: String, 20 | val url: String, 21 | val private: Boolean 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /android-accountmanager/src/main/java/com/andretietz/retroauth/RetroauthInitProvider.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import androidx.startup.Initializer 6 | 7 | internal class RetroauthInitProvider : Initializer { 8 | override fun create(context: Context): ActivityManager { 9 | context.takeIf { it.applicationContext is Application }?.let { 10 | return ActivityManager[it.applicationContext as Application] 11 | } 12 | throw error("Could not initialize retroauth. Context is not an application!") 13 | } 14 | 15 | override fun dependencies(): MutableList>> = mutableListOf() 16 | } 17 | -------------------------------------------------------------------------------- /retroauth/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") 3 | id("com.vanniktech.maven.publish") 4 | } 5 | 6 | dependencies { 7 | api(Dependencies.kotlin.kotlin) 8 | api(Dependencies.retrofit.retrofit) 9 | api(Dependencies.okhttp.okhttp) 10 | 11 | testImplementation(Dependencies.test.junit) 12 | testImplementation(Dependencies.test.coroutines) 13 | testImplementation(kotlin("reflect", version = Versions.kotlin)) 14 | testImplementation(Dependencies.okhttp.mockwebserver) 15 | testImplementation(Dependencies.retrofit.moshiConverter) 16 | testImplementation(Dependencies.retrofit.gsonConverter) 17 | testImplementation(Dependencies.test.mockitoKotlin) 18 | testImplementation(Dependencies.test.assertj) 19 | } 20 | -------------------------------------------------------------------------------- /demo-android/src/main/java/com/andretietz/retroauth/demo/auth/DemoAuthenticationService.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth.demo.auth 2 | 3 | import android.accounts.Account 4 | import com.andretietz.retroauth.AuthenticationService 5 | import com.andretietz.retroauth.demo.R 6 | import timber.log.Timber 7 | 8 | class DemoAuthenticationService : AuthenticationService() { 9 | override fun getLoginAction(): String = getString(R.string.authentication_ACTION) 10 | override fun cleanupAccount(account: Account) { 11 | // Here you can trigger your account cleanup. 12 | // Note: This might be executed on a different process! (when the account is removed 13 | // from the android account manager. 14 | Timber.e("Remove account: ${account.name}") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demo-android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Retroauth Demo 3 | com.andretietz.retroauth.demo.ACTION 4 | com.andretietz.retroauth.demo.ACCOUNT 5 | com.andretietz.retroauth.demo.TOKEN 6 | Add Account 7 | Invalidate Token 8 | Switch Account 9 | Logout 10 | To load repositories, pull to load/refresh 11 | 12 | -------------------------------------------------------------------------------- /android-accountmanager/src/test/java/com/andretietz/retroauth/ActivityManagerTest.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth 2 | 3 | import android.app.Application 4 | import androidx.test.core.app.ApplicationProvider 5 | import androidx.test.ext.junit.runners.AndroidJUnit4 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.junit.Before 8 | import org.junit.Test 9 | import org.junit.runner.RunWith 10 | 11 | @RunWith(AndroidJUnit4::class) 12 | class ActivityManagerTest { 13 | private val application = ApplicationProvider.getApplicationContext() 14 | 15 | @Before 16 | fun setup() { 17 | ActivityManager[application] 18 | } 19 | 20 | @Test 21 | fun initializing() { 22 | val activityManager = ActivityManager[application] 23 | assertThat(activityManager).isNotNull 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/pr_build.yml: -------------------------------------------------------------------------------- 1 | name: PR build 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | name: PR Build 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | 14 | - name: Gradle Wrapper Validation 15 | uses: gradle/wrapper-validation-action@v1 16 | 17 | - name: Install JDK 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: 11 21 | 22 | - name: Build 23 | run: | 24 | version=$(grep "VERSION_NAME" gradle.properties | cut -d'=' -f2 ) 25 | if [[ $version != *"-SNAPSHOT"* ]]; then 26 | echo "Version string MUST contain \"-SNAPSHOT\"!" 27 | exit 1; 28 | fi 29 | echo "Next Version: $version" 30 | ./gradlew build --no-daemon --no-parallel --stacktrace --warning-mode all 31 | -------------------------------------------------------------------------------- /android-accountmanager/src/main/java/com/andretietz/retroauth/RetroauthAndroid.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth 2 | 3 | import android.accounts.Account 4 | import android.app.Application 5 | import retrofit2.Retrofit 6 | 7 | object RetroauthAndroid { 8 | @JvmStatic 9 | fun setup( 10 | retrofit: Retrofit, 11 | application: Application, 12 | authenticator: Authenticator, 13 | ownerType: String 14 | ): Retrofit { 15 | return Retroauth.setup( 16 | retrofit, 17 | authenticator, 18 | AndroidAccountManagerOwnerStorage(application, ownerType), 19 | AndroidAccountManagerCredentialStorage(application) 20 | ) 21 | } 22 | } 23 | 24 | fun Retrofit.androidAuthentication( 25 | application: Application, 26 | authenticator: Authenticator, 27 | ownerType: String 28 | ) = RetroauthAndroid.setup(this, application, authenticator, ownerType) 29 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | GROUP=com.andretietz.retroauth 2 | VERSION_NAME=4.0.0-SNAPSHOT 3 | POM_DESCRIPTION=A library build on top of retrofit, for simple handling of authenticated requests. 4 | POM_URL=https://github.com/andretietz/retroauth 5 | POM_SCM_URL=https://github.com/andretietz/retroauth 6 | POM_SCM_CONNECTION=scm:git:git://github.com/andretietz/retroauth.git 7 | POM_SCM_DEV_CONNECTION=scm:git:git://github.com/andretietz/retroauth.git 8 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 9 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 10 | POM_LICENCE_DIST=repo 11 | POM_DEVELOPER_ID=andretietz 12 | POM_DEVELOPER_URL=https://github.com/andretietz/ 13 | POM_DEVELOPER_NAME=Andre Tietz 14 | POM_INCEPTION_YEAR=2016 15 | 16 | android.useAndroidX=true 17 | android.enableJetifier=false 18 | systemProp.org.gradle.internal.http.socketTimeout=120000 19 | org.gradle.jvmargs=-Xmx4608M 20 | -------------------------------------------------------------------------------- /sqlite/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") 3 | // TODO: do not publish yet 4 | // id("com.vanniktech.maven.publish") 5 | } 6 | 7 | dependencies { 8 | api(Dependencies.kotlin.kotlin) 9 | implementation(project(":retroauth")) 10 | implementation("org.jetbrains.exposed:exposed-core:0.32.1") 11 | implementation("org.jetbrains.exposed:exposed-dao:0.32.1") 12 | implementation("org.jetbrains.exposed:exposed-jdbc:0.32.1") 13 | implementation("org.xerial:sqlite-jdbc:3.36.0.1") 14 | 15 | testImplementation(Dependencies.test.mockitoKotlin) 16 | testImplementation(Dependencies.test.assertj) 17 | testImplementation(Dependencies.test.junit) 18 | testImplementation(Dependencies.test.coroutines) 19 | testImplementation(kotlin("reflect", version = Versions.kotlin)) 20 | // https://mvnrepository.com/artifact/com.h2database/h2 21 | testImplementation("com.h2database:h2:1.4.200") 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /sqlite/src/main/kotlin/com/andretietz/retroauth/sqlite/TableDefinitions.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth.sqlite 2 | 3 | import org.jetbrains.exposed.dao.id.IntIdTable 4 | 5 | 6 | internal object UserTable : IntIdTable() { 7 | val active = bool("active").default(false) 8 | val name = varchar("name", 100) 9 | val email = varchar("email", 100) 10 | 11 | init { 12 | uniqueIndex("IDX_user", name, email) 13 | } 14 | } 15 | 16 | internal object CredentialTable : IntIdTable() { 17 | val user = reference("user_id", UserTable) 18 | val type = varchar("key", 200) 19 | val value = text("value") 20 | } 21 | 22 | internal object DataTable : IntIdTable() { 23 | val credential = reference("credential_id", CredentialTable) 24 | val user = reference("user_id", UserTable) 25 | val key = varchar("key", 200) 26 | val value = text("value") 27 | override val primaryKey = PrimaryKey(credential, user, name = "PK_data_user_credential") 28 | } 29 | -------------------------------------------------------------------------------- /quality/detekt.yml: -------------------------------------------------------------------------------- 1 | potential-bugs: 2 | UnsafeCast: 3 | active: false 4 | LateinitUsage: 5 | active: false 6 | 7 | complexity: 8 | NestedBlockDepth: 9 | active: false 10 | MethodOverloading: 11 | active: false 12 | ComplexMethod: 13 | active: false 14 | LongMethod: 15 | excludes: "**/*Test.kt" 16 | 17 | exceptions: 18 | TooGenericExceptionCaught: 19 | active: false 20 | TooGenericExceptionThrown: 21 | active: false 22 | 23 | style: 24 | ReturnCount: 25 | active: false 26 | UnnecessaryAbstractClass: 27 | active: false 28 | UseDataClass: 29 | active: false 30 | MandatoryBracesIfStatements: 31 | active: false 32 | 33 | comments: 34 | UndocumentedPublicClass: 35 | active: false 36 | UndocumentedPublicFunction: 37 | active: false 38 | CommentOverPrivateFunction: 39 | active: false 40 | EndOfSentenceFormat: 41 | active: false 42 | 43 | naming: 44 | PackageNaming: 45 | active: false -------------------------------------------------------------------------------- /quality/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /retroauth/src/main/java/com/andretietz/retroauth/AuthenticationRequiredException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Andre Tietz 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.andretietz.retroauth 18 | 19 | import java.io.IOException 20 | 21 | /** 22 | * This Exception is thrown, when the user requires to login in order to fulfill an action. 23 | */ 24 | class AuthenticationRequiredException @JvmOverloads constructor( 25 | detailMessage: String? = null, 26 | throwable: Throwable? = null 27 | ) : IOException(detailMessage, throwable) 28 | -------------------------------------------------------------------------------- /.github/workflows/snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Snapshot build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | name: Snapshot Build 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Gradle Wrapper Validation 18 | uses: gradle/wrapper-validation-action@v1 19 | 20 | - name: Install JDK 21 | uses: actions/setup-java@v1 22 | with: 23 | java-version: 11 24 | 25 | - name: Building Snapshot 26 | run: | 27 | version=$(grep "VERSION_NAME" gradle.properties | cut -d'=' -f2 ) 28 | if [[ $version != *"-SNAPSHOT"* ]]; then 29 | echo "Version string MUST contain \"-SNAPSHOT\"!" 30 | exit 1; 31 | fi 32 | echo "Building Snapshot Version: $version" 33 | ./gradlew publish --no-daemon --no-parallel --stacktrace --warning-mode all 34 | env: 35 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} 36 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} 37 | -------------------------------------------------------------------------------- /retroauth/src/main/java/com/andretietz/retroauth/AuthenticationCanceledException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Andre Tietz 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.andretietz.retroauth 18 | 19 | import java.io.IOException 20 | 21 | /** 22 | * This Exception is thrown, when the user cancels the Authentication or 23 | * some other error happens. The Reason can be read, on calling [AuthenticationCanceledException.getCause] 24 | */ 25 | class AuthenticationCanceledException @JvmOverloads constructor( 26 | detailMessage: String? = null, 27 | throwable: Throwable? = null 28 | ) : IOException(detailMessage, throwable) 29 | -------------------------------------------------------------------------------- /retroauth/src/main/java/com/andretietz/retroauth/Authenticated.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Andre Tietz 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.andretietz.retroauth 18 | 19 | import kotlin.annotation.AnnotationRetention.RUNTIME 20 | import kotlin.annotation.AnnotationTarget.FUNCTION 21 | import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER 22 | import kotlin.annotation.AnnotationTarget.PROPERTY_SETTER 23 | 24 | /** 25 | * This is the annotation you can use to authorize your request. 26 | */ 27 | @Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER) 28 | @Retention(RUNTIME) 29 | annotation class Authenticated( 30 | val credentialType: Int = 0 31 | ) 32 | -------------------------------------------------------------------------------- /sqlite/src/main/kotlin/com/andretietz/retroauth/sqlite/EntityDefinitions.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth.sqlite 2 | 3 | import org.jetbrains.exposed.dao.IntEntity 4 | import org.jetbrains.exposed.dao.IntEntityClass 5 | import org.jetbrains.exposed.dao.id.EntityID 6 | 7 | internal class DatabaseUser(id: EntityID) : IntEntity(id) { 8 | companion object : IntEntityClass(UserTable) 9 | 10 | var name by UserTable.name 11 | var email by UserTable.email 12 | var active by UserTable.active 13 | } 14 | 15 | /** 16 | * This table should be encrypted at one point. 17 | */ 18 | internal class DatabaseCredential(id: EntityID) : IntEntity(id) { 19 | companion object : IntEntityClass(CredentialTable) 20 | 21 | var user by DatabaseUser referencedOn CredentialTable.user 22 | var type by CredentialTable.type 23 | var value by CredentialTable.value 24 | } 25 | 26 | internal class DatabaseData(id: EntityID) : IntEntity(id) { 27 | companion object : IntEntityClass(DataTable) 28 | 29 | var user by DatabaseUser referencedOn DataTable.user 30 | var credential by DatabaseCredential referencedOn DataTable.credential 31 | var key by DataTable.key 32 | var value by DataTable.value 33 | } 34 | -------------------------------------------------------------------------------- /android-accountmanager/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 11 | 14 | 17 | 18 | 19 | 24 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A simple way of calling authenticated requests using retrofit 2 | [![Snapshot build](https://github.com/andretietz/retroauth/workflows/Snapshot%20build/badge.svg)](https://github.com/andretietz/retroauth/actions?query=workflow%3A%22Snapshot+build%22) 3 | 4 | 5 | I split the project into 2 separate ones. 6 | 7 | * [retroauth](retroauth) 8 | This is the base implementation, to be used in plain java/kotlin projects. 9 | * [android-accountmanager](android-accountmanager/) 10 | On top of the pure Kotlin implementation there's the Android implementation, which uses the 11 | Android AccountManager in order to store Owners (Accounts) and their Credentials. 12 | 13 | ## LICENSE 14 | ``` 15 | Copyrights 2016 André Tietz 16 | 17 | Licensed under the Apache License, Version 2.0 (the "License"); 18 | you may not use this file except in compliance with the License. 19 | You may obtain a copy of the License at 20 | 21 | 22 | 23 | Unless required by applicable law or agreed to in writing, software 24 | distributed under the License is distributed on an "AS IS" BASIS, 25 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 | See the License for the specific language governing permissions and 27 | limitations under the License. 28 | ``` 29 | -------------------------------------------------------------------------------- /android-accountmanager/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | kotlin("android") 4 | id("com.vanniktech.maven.publish") 5 | } 6 | 7 | android { 8 | compileSdk = 31 9 | defaultConfig { 10 | minSdk = 21 11 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | getByName("release") { 15 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 16 | } 17 | } 18 | compileOptions { 19 | sourceCompatibility = JavaVersion.VERSION_1_8 20 | targetCompatibility = JavaVersion.VERSION_1_8 21 | } 22 | 23 | testOptions.unitTests.isIncludeAndroidResources = true 24 | buildFeatures.buildConfig = false 25 | } 26 | 27 | 28 | dependencies { 29 | implementation(Dependencies.android.appcompat) 30 | implementation(Dependencies.android.startup) 31 | api(project(":retroauth")) 32 | 33 | testImplementation(Dependencies.test.junit) 34 | testImplementation(Dependencies.test.mockito) 35 | testImplementation(Dependencies.android.test.core) 36 | testImplementation(Dependencies.android.test.junit) 37 | testImplementation(Dependencies.android.test.robolectric) 38 | testImplementation(Dependencies.android.test.runner) 39 | testImplementation(Dependencies.android.test.rules) 40 | testImplementation(Dependencies.test.assertj) 41 | } 42 | 43 | tasks.withType { 44 | kotlinOptions.jvmTarget = "1.8" 45 | } 46 | -------------------------------------------------------------------------------- /demo-android/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo-android/src/main/java/com/andretietz/retroauth/demo/auth/GithubAuthenticator.kt: -------------------------------------------------------------------------------- 1 | package com.andretietz.retroauth.demo.auth 2 | 3 | import android.accounts.Account 4 | import android.app.Application 5 | import android.content.Context 6 | import com.andretietz.retroauth.Authenticator 7 | import com.andretietz.retroauth.Credentials 8 | import com.andretietz.retroauth.demo.R 9 | import okhttp3.Request 10 | 11 | /** 12 | * This is an optimistic implementation of facebook as [Authenticator]. 13 | * 14 | * If the credential for some reason is invalid, the returning 401 will cause the deletion of the credential and a retry of the 15 | * call, in which it will get refreshed 16 | */ 17 | class GithubAuthenticator(private val application: Application) : Authenticator() { 18 | 19 | companion object { 20 | const val CLIENT_ID = "bb86ddeb2dd22163192f" 21 | const val CLIENT_SECRET = "0b2a017a3e481c1cb69739ff5a6c4de37009ed7a" 22 | const val CLIENT_CALLBACK = "https://localhost:8000/accounts/github/login/callback/" 23 | 24 | @JvmStatic 25 | fun createTokenType(context: Context) = context.getString(R.string.authentication_TOKEN) 26 | } 27 | 28 | private val credentialType = createTokenType(application) 29 | 30 | override fun getCredentialType(credentialType: Int): String = this.credentialType 31 | 32 | override fun authenticateRequest(request: Request, credential: Credentials): Request { 33 | return request.newBuilder() 34 | .header("Authorization", "Bearer ${credential.token}") 35 | .build() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /demo-android/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 12 | 13 |