From 7011cb8868dd9c6b821d1b8ab8a106ca8da549aa Mon Sep 17 00:00:00 2001 From: Stanislav Mukhametshin Date: Wed, 30 Dec 2020 16:06:27 +0300 Subject: [PATCH 1/7] MC-1464 Initial commit --- .gitattributes | 1 + .gitignore | 16 + .idea/codeStyles/Project.xml | 142 ++++ .idea/codeStyles/codeStyleConfig.xml | 5 + CHANGELOG.md | 9 + README.md | 83 +++ app-demo/.gitignore | 1 + app-demo/build.gradle | 43 ++ app-demo/proguard-rules.pro | 21 + app-demo/src/main/AndroidManifest.xml | 39 + .../core/app_demo_partner/PartnerActivity.kt | 78 ++ .../core/app_demo_partner/PartnerPresenter.kt | 115 +++ .../drawable-v24/ic_launcher_foreground.xml | 34 + .../res/drawable/ic_launcher_background.xml | 170 +++++ .../src/main/res/layout/activity_partner.xml | 72 ++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1908 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 1807 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1286 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 1055 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2587 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 2609 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 4074 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 4606 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 5839 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 7132 bytes app-demo/src/main/res/values-ru/strings.xml | 7 + app-demo/src/main/res/values/colors.xml | 6 + .../res/values/ic_launcher_background.xml | 4 + app-demo/src/main/res/values/strings.xml | 6 + app-demo/src/main/res/values/styles.xml | 11 + .../main/res/xml/network_security_config.xml | 9 + build.gradle | 35 + gradle.properties | 5 +- gradle/plugin/android.gradle | 55 ++ gradle/plugin/config/detekt.yml | 689 ++++++++++++++++++ gradle/plugin/detekt.gradle | 22 + gradle/plugin/kotlin-library.gradle | 7 + gradle/plugin/publish.gradle | 95 +++ gradle/versions.gradle | 25 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 +++++ gradlew.bat | 84 +++ imgs/tinkoff_id_sign_in_button/compact.png | Bin 0 -> 53743 bytes imgs/tinkoff_id_sign_in_button/standard.png | Bin 0 -> 57846 bytes settings.gradle | 2 + tinkoff-id/.gitignore | 1 + tinkoff-id/build.gradle | 16 + tinkoff-id/gradle.properties | 3 + tinkoff-id/src/main/AndroidManifest.xml | 6 + .../ru/tinkoff/core/tinkoffId/AppLinkUtil.kt | 73 ++ .../ru/tinkoff/core/tinkoffId/TinkoffCall.kt | 28 + .../tinkoff/core/tinkoffId/TinkoffIdAuth.kt | 133 ++++ .../core/tinkoffId/TinkoffIdStatusCode.kt | 13 + .../tinkoffId/TinkoffPartnerApiService.kt | 121 +++ .../core/tinkoffId/TinkoffTokenPayload.kt | 17 + .../core/tinkoffId/api/TinkoffIdApi.kt | 95 +++ .../codeVerifier/CodeVerifierStore.kt | 22 + .../codeVerifier/CodeVerifierUtil.kt | 91 +++ .../tinkoffId/error/TinkoffErrorMessage.kt | 14 + .../error/TinkoffRequestException.kt | 13 + .../error/TinkoffTokenErrorConstants.kt | 48 ++ .../error/TokenSignOutErrorConstants.kt | 23 + .../tinkoffId/ui/TinkoffIdSignInButton.kt | 197 +++++ .../drawable-hdpi/tinkoff_id_tinkoff_logo.png | Bin 0 -> 2567 bytes .../drawable-mdpi/tinkoff_id_tinkoff_logo.png | Bin 0 -> 1671 bytes .../tinkoff_id_tinkoff_logo.png | Bin 0 -> 3808 bytes .../tinkoff_id_tinkoff_logo.png | Bin 0 -> 5835 bytes .../tinkoff_id_tinkoff_logo.png | Bin 0 -> 8406 bytes .../tinkoff_id_sign_in_button_background.xml | 20 + .../font/tinkoff_id_font_roboto_regular.ttf | Bin 0 -> 171272 bytes tinkoff-id/src/main/res/values-ru/strings.xml | 7 + tinkoff-id/src/main/res/values/attr.xml | 11 + tinkoff-id/src/main/res/values/colors.xml | 9 + tinkoff-id/src/main/res/values/dimens.xml | 6 + tinkoff-id/src/main/res/values/strings.xml | 7 + 77 files changed, 3046 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 app-demo/.gitignore create mode 100644 app-demo/build.gradle create mode 100644 app-demo/proguard-rules.pro create mode 100644 app-demo/src/main/AndroidManifest.xml create mode 100644 app-demo/src/main/java/ru/tinkoff/core/app_demo_partner/PartnerActivity.kt create mode 100644 app-demo/src/main/java/ru/tinkoff/core/app_demo_partner/PartnerPresenter.kt create mode 100644 app-demo/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 app-demo/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app-demo/src/main/res/layout/activity_partner.xml create mode 100644 app-demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app-demo/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app-demo/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 app-demo/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app-demo/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 app-demo/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app-demo/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 app-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app-demo/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 app-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 app-demo/src/main/res/values-ru/strings.xml create mode 100644 app-demo/src/main/res/values/colors.xml create mode 100644 app-demo/src/main/res/values/ic_launcher_background.xml create mode 100644 app-demo/src/main/res/values/strings.xml create mode 100644 app-demo/src/main/res/values/styles.xml create mode 100644 app-demo/src/main/res/xml/network_security_config.xml create mode 100644 build.gradle create mode 100644 gradle/plugin/android.gradle create mode 100644 gradle/plugin/config/detekt.yml create mode 100644 gradle/plugin/detekt.gradle create mode 100644 gradle/plugin/kotlin-library.gradle create mode 100644 gradle/plugin/publish.gradle create mode 100644 gradle/versions.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 imgs/tinkoff_id_sign_in_button/compact.png create mode 100644 imgs/tinkoff_id_sign_in_button/standard.png create mode 100644 settings.gradle create mode 100644 tinkoff-id/.gitignore create mode 100644 tinkoff-id/build.gradle create mode 100644 tinkoff-id/gradle.properties create mode 100644 tinkoff-id/src/main/AndroidManifest.xml create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/AppLinkUtil.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/TinkoffCall.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/TinkoffIdAuth.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/TinkoffIdStatusCode.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/TinkoffPartnerApiService.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/TinkoffTokenPayload.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/api/TinkoffIdApi.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/codeVerifier/CodeVerifierStore.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/codeVerifier/CodeVerifierUtil.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/error/TinkoffErrorMessage.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/error/TinkoffRequestException.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/error/TinkoffTokenErrorConstants.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/error/TokenSignOutErrorConstants.kt create mode 100644 tinkoff-id/src/main/java/ru/tinkoff/core/tinkoffId/ui/TinkoffIdSignInButton.kt create mode 100644 tinkoff-id/src/main/res/drawable-hdpi/tinkoff_id_tinkoff_logo.png create mode 100644 tinkoff-id/src/main/res/drawable-mdpi/tinkoff_id_tinkoff_logo.png create mode 100644 tinkoff-id/src/main/res/drawable-xhdpi/tinkoff_id_tinkoff_logo.png create mode 100644 tinkoff-id/src/main/res/drawable-xxhdpi/tinkoff_id_tinkoff_logo.png create mode 100644 tinkoff-id/src/main/res/drawable-xxxhdpi/tinkoff_id_tinkoff_logo.png create mode 100644 tinkoff-id/src/main/res/drawable/tinkoff_id_sign_in_button_background.xml create mode 100644 tinkoff-id/src/main/res/font/tinkoff_id_font_roboto_regular.ttf create mode 100644 tinkoff-id/src/main/res/values-ru/strings.xml create mode 100644 tinkoff-id/src/main/res/values/attr.xml create mode 100644 tinkoff-id/src/main/res/values/colors.xml create mode 100644 tinkoff-id/src/main/res/values/dimens.xml create mode 100644 tinkoff-id/src/main/res/values/strings.xml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c0a9744 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +changelog.md merge=union \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56ff1c1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +.gradle/ +.idea/*.xml +.idea/caches +.idea/libraries +build/ +.DS_Store + +/local.properties +/gradle.properties +installlocal.bat + +*.iml +*.eml +*.iws + +gradle/plugin/custom.gradle diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..8d10e09 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,142 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..50f6cf9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +## 1.0.0 + +#### Fixed +#### Changes +#### Additions + + +[app-demo]: app-demo +[tinkoff-id]: tinkoff-id \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c57da4 --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# Tinkoff ID + +`Tinkoff ID` - SDK для авторизации пользователей Android приложений с помощью аккаунта Тинькофф. + +## Установка + +Для начала работы добавьте следующую строчку в `build.gradle`: + +```groovy +implementation "ru.tinkoff.core.tinkoffauth:tinkoff-id:${version}" +``` + +## Требования к приложению + +Для работы SDK необходимо следующее: + ++ Зарегистрированный идентификатор авторизуемого приложения (`client_id`) ++ Зарегистрированная авторизуемым приложением [App Link](https://developer.android.com/training/app-links), которая будет использоваться для возврата в приложение после авторизации ++ Tinkoff ID SDK поддерживает авторизацию через Тинькофф только на api >= 23, на более ранних версиях получить авторизационные данные не будет возможности. + +## Интеграция + +Все необходимое взаимодействие в библиотеке идет только через класс `TinkoffIdAuth`. + +1. Необходимо добавить в лэйаут кнопку `TinkoffIdSignInButton.kt`. + Пример по ее настройке доступен в `PartnerActivity.kt` и `activity_partner.xml`. Размер кнопки настраивается через атрибут + `app:tinkoff_id_size`, который может иметь значение `compact` или `standard` (по умолчанию). Подробное описание + различий находится в комментарии в `TinkoffIdSignInButton.kt`. + Вид кнопок: + + compact + ![compact.png](imgs/tinkoff_id_sign_in_button/compact.png) + + standard + ![standard.png](imgs/tinkoff_id_sign_in_button/standard.png) + +2. Необходимо создать объект `TinkoffIdAuth(applicationContext, clientId)` - это основной класс для работы с библиотекой. +3. Проверить возможность авторизации пользователя через Тинькофф - `tinkoffIdAuth.isTinkoffAuthAvailable()` +4. Запустить партнерскую авторизацию, передав ваш App Link (по данному аплинку приложение группы Тинькофф вернется обратно после процесса авторизации) +```kotlin + val intent = tinkoffIdAuth.createTinkoffAuthIntent(partnerUri) + startActivity(intent) +``` +5. После того как пользователь пройдет флоу авторизации в приложении Тинькофф произойдет переход в ваше приложение. В `intent.data` будет храниться информация по авторизации. +6. Вы можете проверить успешность авторизации методом +```kotlin + // метод вернет статус SUCCESS или CANCELLED_BY_USER + tinkoffIdAuth.getStatusCode(uriFromIntentData) +} +``` +7. Если статус успешен, то можно вызвать `getTinkoffTokenPayload(uri).getResponse()`, необходимо выполнять `getResponse()` на io потоке, удобными для вас средствами +```kotlin + tinkoffIdAuth.getTinkoffTokenPayload(uriFromIntentData).getResponse() +``` +8. После этого вы получите `TinkoffTokenPayload`, содержащий креденшиалы +9. Желательно реализовать безопасное хранение tinkoffTokenPayload.refreshToken в приложении, так как он необходим для перевыпуска токенов + +### Перевыпуск авторизационных данных + +Для перевыпуска accessToken необходимо использовать метод `tinkoffIdAuth.obtainTokenPayload(refreshToken)` + +В него нужно передать refreshToken полученный ранее. Выполнять вызов `getResponse()` необходимо не на ui потоке + +### Отзыв авторизационных данных + +Иногда может возникнуть ситуация когда полученные авторизационные данные более не нужны. Например, при выходе, смене или отключении аккаунта пользователя в авторизованном приложении. В таком случае, приложению необходимо выполнить отзыв авторизационных данных с помощью методов: +```kotlin + fun signOutByAccessToken(accessToken: String): TinkoffCall + fun signOutByRefreshToken(refreshToken: String): TinkoffCall +``` + +### Структура TinkoffTokenPayload + +В результате успешной авторизации приложение получает объект `TinkoffTokenPayload`, содержащий следующие свойства: + ++ `accessToken` - токен для обращения к API Тинькофф ++ `refreshToken` - токен, необходимый для получения нового `accessToken` ++ `idToken` - идентификатор пользователя в формате JWT ++ `expiresIn` - время, через которое `accessToken` станет неактуальным и нужно будет получить новый с помощью `obtainTokenPayload(refreshToken)` + +## Дополнительно + +SDK поставляется с примером приложения, где можно посмотреть работу авторизации. \ No newline at end of file diff --git a/app-demo/.gitignore b/app-demo/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app-demo/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app-demo/build.gradle b/app-demo/build.gradle new file mode 100644 index 0000000..ab9e366 --- /dev/null +++ b/app-demo/build.gradle @@ -0,0 +1,43 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: rootProject.file('gradle/versions.gradle') +apply from: rootProject.file('gradle/plugin/detekt.gradle') + +def getBuildVersionCode = (project.findProperty("buildCounter") == null) + ? 1 + : Integer.parseInt(project.findProperty("buildCounter")) + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + + defaultConfig { + multiDexEnabled = true + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode getBuildVersionCode + versionName authVersion + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + +} + +dependencies { + api project(':tinkoff-id') + api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" + api "androidx.appcompat:appcompat:${appCompatVersion}" + api "androidx.constraintlayout:constraintlayout:${constraintLayoutVersion}" + //rx + api "io.reactivex.rxjava2:rxjava:$rxJavaVersion" + api "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion" +} \ No newline at end of file diff --git a/app-demo/proguard-rules.pro b/app-demo/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app-demo/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app-demo/src/main/AndroidManifest.xml b/app-demo/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a3640ab --- /dev/null +++ b/app-demo/src/main/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app-demo/src/main/java/ru/tinkoff/core/app_demo_partner/PartnerActivity.kt b/app-demo/src/main/java/ru/tinkoff/core/app_demo_partner/PartnerActivity.kt new file mode 100644 index 0000000..47476cd --- /dev/null +++ b/app-demo/src/main/java/ru/tinkoff/core/app_demo_partner/PartnerActivity.kt @@ -0,0 +1,78 @@ +package ru.tinkoff.core.app_demo_partner + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.View +import android.widget.Button +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import ru.tinkoff.core.tinkoffId.TinkoffIdAuth +import kotlin.LazyThreadSafetyMode.NONE + +class PartnerActivity : AppCompatActivity() { + + private val partnerUri: Uri = Uri.Builder() + .scheme("https") + .authority("www.partner.com") + .appendPath("partner") + .build() + + private val tinkoffPartnerAuth by lazy(NONE) { TinkoffIdAuth(applicationContext, "test-partner-mobile") } + private val partnerPresenter by lazy(NONE) { PartnerPresenter(tinkoffPartnerAuth, this) } + + private val compactButtonTinkoffAuth by lazy(NONE) { findViewById