├── example
├── .gitignore
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── twigbit
│ │ │ │ └── identsdk
│ │ │ │ └── example
│ │ │ │ ├── .gitignore
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── IndependentIdentificationActivity.kt
│ │ ├── ic_launcher-web.png
│ │ ├── res
│ │ │ ├── font
│ │ │ │ └── nexa_bold.ttf
│ │ │ ├── drawable
│ │ │ │ ├── twigbit_logo.png
│ │ │ │ ├── illu_twigbit_ident.png
│ │ │ │ ├── twigbit_logo_text.png
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_independent_identification.xml
│ │ │ │ └── activity_main.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── twigbit
│ │ │ └── identsdk
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── twigbit
│ │ └── identsdk
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── identsdk
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── twigbit_ident_header_logo.png
│ │ │ │ ├── twigbit_ident_icon_arrow_right.xml
│ │ │ │ ├── twigbit_ident_icon_back.xml
│ │ │ │ ├── twigbit_ident_icon_info.xml
│ │ │ │ ├── twigbit_ident_icon_service_provider.xml
│ │ │ │ └── twigbit_ident_icon_purpose.xml
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ └── layout
│ │ │ │ ├── holder_access_right.xml
│ │ │ │ ├── fragment_loader.xml
│ │ │ │ ├── fragment_insert_card.xml
│ │ │ │ ├── activity_dropin_identification.xml
│ │ │ │ ├── fragment_intro.xml
│ │ │ │ ├── fragment_error.xml
│ │ │ │ ├── fragment_success.xml
│ │ │ │ ├── fragment_authorisation.xml
│ │ │ │ ├── fragment_certificate.xml
│ │ │ │ └── fragment_access_rights.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── twigbit
│ │ │ │ └── identsdk
│ │ │ │ ├── util
│ │ │ │ ├── Tags.kt
│ │ │ │ ├── Extensions.kt
│ │ │ │ ├── NfcInterceptorActivity.kt
│ │ │ │ ├── ForegroundDispatcher.kt
│ │ │ │ └── StringUtil.kt
│ │ │ │ ├── core
│ │ │ │ ├── IdentificationManagerProvider.kt
│ │ │ │ ├── AccessRights.kt
│ │ │ │ ├── IdentificationActivity.kt
│ │ │ │ ├── IdentificationFragment.kt
│ │ │ │ ├── IdentificationUtil.kt
│ │ │ │ └── IdentificationManager.kt
│ │ │ │ ├── dropinui
│ │ │ │ ├── DropInRequest.kt
│ │ │ │ ├── LoaderFragment.kt
│ │ │ │ ├── IntroFragment.kt
│ │ │ │ ├── ErrorFragment.kt
│ │ │ │ ├── SuccessFragment.kt
│ │ │ │ ├── InsertCardFragment.kt
│ │ │ │ ├── CertificateFragment.kt
│ │ │ │ ├── AccessRightsFragment.kt
│ │ │ │ ├── AuthorisationFragment.kt
│ │ │ │ └── DropInIdentificationActivity.kt
│ │ │ │ └── ausweisident
│ │ │ │ ├── AusweisIdentScopes.kt
│ │ │ │ ├── UserInfo.kt
│ │ │ │ ├── AusweisIdentBuilder.kt
│ │ │ │ ├── AusweisIdentResultHandler.kt
│ │ │ │ └── InMemoryCookieJar.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── twigbit
│ │ │ └── identsdk
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── twigbit
│ │ └── identsdk
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── docs
├── images
│ ├── header.png
│ ├── logos.png
│ ├── logo-twigbit.png
│ ├── logo-governikus.png
│ └── logo-ausweisident.png
├── Architecture.md
├── STYLEGUIDE.md
└── README.md
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── encodings.xml
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
├── markdown-navigator
│ └── profiles_settings.xml
├── vcs.xml
├── markdown-exported-files.xml
├── runConfigurations.xml
├── gradle.xml
├── markdown-navigator.xml
├── misc.xml
├── markdown-navigator-enh.xml
└── assetWizardSettings.xml
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── gradlew
└── LICENSE
/example/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/identsdk/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':example', ':identsdk'
2 |
--------------------------------------------------------------------------------
/example/src/main/java/com/twigbit/identsdk/example/.gitignore:
--------------------------------------------------------------------------------
1 | Secrets.kt
2 |
--------------------------------------------------------------------------------
/docs/images/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/docs/images/header.png
--------------------------------------------------------------------------------
/docs/images/logos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/docs/images/logos.png
--------------------------------------------------------------------------------
/docs/images/logo-twigbit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/docs/images/logo-twigbit.png
--------------------------------------------------------------------------------
/docs/images/logo-governikus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/docs/images/logo-governikus.png
--------------------------------------------------------------------------------
/docs/images/logo-ausweisident.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/docs/images/logo-ausweisident.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/example/src/main/res/font/nexa_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/font/nexa_bold.ttf
--------------------------------------------------------------------------------
/example/src/main/res/drawable/twigbit_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/drawable/twigbit_logo.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Twigbit Ident SDK Example Application
3 |
4 |
--------------------------------------------------------------------------------
/example/src/main/res/drawable/illu_twigbit_ident.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/drawable/illu_twigbit_ident.png
--------------------------------------------------------------------------------
/example/src/main/res/drawable/twigbit_logo_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/drawable/twigbit_logo_text.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/identsdk/src/main/res/drawable/twigbit_ident_header_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/identsdk/src/main/res/drawable/twigbit_ident_header_logo.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twigbit/ident-sdk/HEAD/example/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/util/Tags.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.util
2 |
3 | object Tags {
4 | val TAG_IDENT_DEBUG = "TwigbitIdent::DEBUG";
5 | }
6 |
--------------------------------------------------------------------------------
/example/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #F8FDFF
4 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/core/IdentificationManagerProvider.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.core
2 |
3 | interface IdentificationManagerProvider {
4 | val identificationManager: IdentificationManager?;
5 | }
--------------------------------------------------------------------------------
/.idea/markdown-exported-files.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/util/Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.util
2 |
3 | import android.app.Activity
4 | import androidx.fragment.app.Fragment
5 | import com.twigbit.identsdk.dropinui.DropInIdentificationActivity
6 |
--------------------------------------------------------------------------------
/example/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #2A4769
4 | #273F57
5 | #87BD49
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jan 27 14:31:07 CET 2020
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-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #4C4E7C
4 | #37354B
5 | #2AD785
6 | #0D141D
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Android Studio Settings
2 | /.idea/caches/build_file_checksums.ser
3 | /.idea/libraries
4 | /.idea/modules.xml
5 | /.idea/workspace.xml
6 | \.idea/markdown-exported-files\.xml
7 |
8 | *.iml
9 | .gradle
10 | /local.properties
11 |
12 | /build
13 | /captures
14 | .externalNativeBuild
15 |
16 | # other
17 | .DS_Store
18 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/drawable/twigbit_ident_icon_arrow_right.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/drawable/twigbit_ident_icon_back.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/example/src/test/java/com/twigbit/identsdk/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/identsdk/src/test/java/com/twigbit/identsdk/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/docs/Architecture.md:
--------------------------------------------------------------------------------
1 | # twigbit ident sdk
2 | ## Architecture considerations
3 |
4 | The SDK consists of several modules:
5 |
6 | | module | description |
7 | | ------ | ----------- |
8 | | core | - bundles `com.governikus.ausweisapp`
- capability checks (nfc / nfc extended length support)
- handle ident state machine |
9 | | ui | - drop-in ui |
10 | | compat | - compat activity that wraps activity lifecycle |
11 | | ausweisident | - AusweisIdentBuilder class for building AusweisIdent tcTokenUrls |
12 |
13 |
--------------------------------------------------------------------------------
/identsdk/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/drawable/twigbit_ident_icon_info.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/DropInRequest.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 |
6 | class DropInRequest(private val tcTokenUrl: String){
7 | fun getIntent(context: Context): Intent{
8 | val intent = Intent(context, DropInIdentificationActivity::class.java)
9 | intent.putExtra(EXTRA_TC_TOKEN_URL, tcTokenUrl)
10 | return intent
11 | }
12 |
13 | companion object {
14 | val EXTRA_TC_TOKEN_URL = "twigbit-ident-extra-tc-token-url"
15 | }
16 | }
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/holder_access_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/fragment_loader.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/drawable/twigbit_ident_icon_service_provider.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/drawable/twigbit_ident_icon_purpose.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/example/src/androidTest/java/com/twigbit/identsdk/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getTargetContext()
22 | assertEquals("com.twigbit.identsdk", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/activity_independent_identification.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/ausweisident/AusweisIdentScopes.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.ausweisident
2 |
3 | object AusweisIdentScopes {
4 | const val BIRTH_NAME = "BirthName"
5 | const val FAMILY_NAMES = "FamilyNames"
6 | const val GIVEN_NAMES = "GivenNames"
7 | const val DATE_OF_BIRTH = "DateOfBirth"
8 | const val PLACE_OF_RESIDENCE = "PlaceOfResidence"
9 | const val NATIONALITY = "Nationality"
10 | const val ACADEMIC_TITLE = "AcademicTitle"
11 | const val ARTISTIC_NAME = "ArtisticName"
12 | const val ISSUING_STATE = "IssuingState"
13 | const val RESTRICTED_ID = "RestrictedID"
14 | const val PLACE_OF_BIRTH = "PlaceOfBirth"
15 | const val DOCUMENT_TYPE = "DocumentType"
16 | const val RESIDENCE_PERMIT_I = "ResidencePermitI"
17 | const val DATE_OF_EXPIRY = "DateOfExpiry"
18 | }
--------------------------------------------------------------------------------
/example/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/identsdk/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/example/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
15 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/identsdk/src/androidTest/java/com/twigbit/identsdk/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk;
2 |
3 | import android.content.Context;
4 | import androidx.test.platform.app.InstrumentationRegistry;
5 | import androidx.test.ext.junit.runners.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.twigbit.identsdk.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # Kotlin code style for this project: "official" or "obsolete":
15 | kotlin.code.style=official
16 | android.useAndroidX=true
17 | android.enableJetifier=true
18 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/LoaderFragment.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 |
4 | import android.os.Bundle
5 | import androidx.fragment.app.Fragment
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 |
10 | import com.twigbit.identsdk.R
11 |
12 | // TODO: Rename parameter arguments, choose names that match
13 | // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
14 | private const val ARG_PARAM1 = "param1"
15 | private const val ARG_PARAM2 = "param2"
16 |
17 | /**
18 | * A simple [Fragment] subclass.
19 | *
20 | */
21 | class LoaderFragment : androidx.fragment.app.Fragment() {
22 |
23 | override fun onCreateView(
24 | inflater: LayoutInflater, container: ViewGroup?,
25 | savedInstanceState: Bundle?
26 | ): View? {
27 | // Inflate the layout for this fragment
28 | return inflater.inflate(R.layout.fragment_loader, container, false)
29 | }
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/core/AccessRights.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.core
2 |
3 | object AccessRights{
4 | const val ADDRESS = "Address"
5 | const val BIRTH_NAME = "BirthName"
6 | const val FAMILY_NAME = "FamilyName"
7 | const val GIVEN_NAMES = "GivenNames"
8 | const val PLACE_OF_BIRTH = "PlaceOfBirth"
9 | const val DATE_OF_BIRTH = "DateOfBirth"
10 | const val DOCTORAL_DEGREE = "DoctoralDegree"
11 | const val ARTISTIC_NAME = "ArtisticName"
12 | const val PSEUDONYM = "Pseudonym"
13 | const val VALID_UNTIL = "ValidUntil"
14 | const val NATIONALITY = "Nationality"
15 | const val ISSUING_COUNTRY = "IssuingCountry"
16 | const val DOCUMENT_TYPE = "DocumentType"
17 | const val RESIDENCE_PERMIT_I = "ResidencePermitI"
18 | const val RESIDENCE_PERMIT_II = "ResidencePermitII"
19 | const val COMMUNITY_ID = "CommunityID"
20 | const val ADDRESS_VERIFICATION = "AddressVerification"
21 | const val AGE_VERIFICATION = "AgeVerification"
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/example/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/IntroFragment.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 |
4 | import android.os.Bundle
5 | import androidx.fragment.app.Fragment
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 |
10 | import com.twigbit.identsdk.R
11 | import kotlinx.android.synthetic.main.fragment_intro.view.*
12 |
13 | /**
14 | * A simple [Fragment] subclass.
15 | *
16 | */
17 | class IntroFragment : androidx.fragment.app.Fragment() {
18 |
19 | override fun onCreateView(
20 | inflater: LayoutInflater, container: ViewGroup?,
21 | savedInstanceState: Bundle?
22 | ): View? {
23 | // Inflate the layout for this fragment
24 | val v = inflater.inflate(R.layout.fragment_intro, container, false)
25 | v.buttonStart.setOnClickListener {
26 | activity?.asIdentificationUI()!!.startIdent()
27 | activity!!.asIdentificationUI()!!.showLoader()
28 | }
29 | return v;
30 | }
31 |
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/ErrorFragment.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 |
4 | import android.os.Bundle
5 | import androidx.fragment.app.Fragment
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 |
10 | import com.twigbit.identsdk.R
11 | import kotlinx.android.synthetic.main.fragment_success.view.*
12 |
13 | // TODO: Rename parameter arguments, choose names that match
14 | // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
15 | private const val ARG_PARAM1 = "param1"
16 | private const val ARG_PARAM2 = "param2"
17 |
18 | /**
19 | * A simple [Fragment] subclass.
20 | *
21 | */
22 | class ErrorFragment : androidx.fragment.app.Fragment() {
23 |
24 | override fun onCreateView(
25 | inflater: LayoutInflater, container: ViewGroup?,
26 | savedInstanceState: Bundle?
27 | ): View? {
28 | // Inflate the layout for this fragment
29 | val v = inflater.inflate(R.layout.fragment_error, container, false)
30 | v.buttonFinish.setOnClickListener { activity!!.finish() }
31 | return v
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/SuccessFragment.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 |
4 | import android.os.Bundle
5 | import androidx.fragment.app.Fragment
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 |
10 | import com.twigbit.identsdk.R
11 | import kotlinx.android.synthetic.main.fragment_success.view.*
12 |
13 | // TODO: Rename parameter arguments, choose names that match
14 | // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
15 | private const val ARG_PARAM1 = "param1"
16 | private const val ARG_PARAM2 = "param2"
17 |
18 | /**
19 | * A simple [Fragment] subclass.
20 | *
21 | */
22 | class SuccessFragment : androidx.fragment.app.Fragment() {
23 |
24 | override fun onCreateView(
25 | inflater: LayoutInflater, container: ViewGroup?,
26 | savedInstanceState: Bundle?
27 | ): View? {
28 | // Inflate the layout for this fragment
29 | val v = inflater.inflate(R.layout.fragment_success, container, false)
30 | v.buttonFinish.setOnClickListener { activity!!.finish() }
31 | return v
32 | }
33 |
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/InsertCardFragment.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import android.os.Bundle
6 | import androidx.fragment.app.Fragment
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 |
11 | import com.twigbit.identsdk.R
12 |
13 | /**
14 | * A simple [Fragment] subclass.
15 | * Activities that contain this fragment must implement the
16 | * [InsertCardFragment.OnFragmentInteractionListener] interface
17 | * to handle interaction events.
18 | * Use the [InsertCardFragment.newInstance] factory method to
19 | * create an instance of this fragment.
20 | *
21 | */
22 | class InsertCardFragment : androidx.fragment.app.Fragment() {
23 | // TODO: Rename and change types of parameters
24 |
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | super.onCreate(savedInstanceState)
27 | arguments?.let {
28 | }
29 | }
30 |
31 | override fun onCreateView(
32 | inflater: LayoutInflater, container: ViewGroup?,
33 | savedInstanceState: Bundle?
34 | ): View? {
35 | // Inflate the layout for this fragment
36 | val v = inflater.inflate(R.layout.fragment_insert_card, container, false)
37 | return v
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/util/NfcInterceptorActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018. Moritz Morgenroth- All Rights Reserved
3 | * Unauthorized copying of this file, via any medium is strictly prohibited
4 | * Proprietary and confidential
5 | * Written by Moritz Morgenroth
6 | */
7 |
8 | package com.twigbit.identsdk.util
9 |
10 | import android.os.Bundle
11 | import androidx.appcompat.app.AppCompatActivity
12 |
13 | /*
14 |
15 | Additional layer for NFC dispatcher that allows manifest configuration for NFC Intent filter
16 |
17 | This does not appear to be necessary. Should be implemented for good measure later though.
18 |
19 | Elsewise tell Governikus to move from their docs.
20 |
21 | Currently Unused
22 |
23 | // TODO: Check under which conditions the NFC filter maifest configuration is needed. Maybe unnecessarry in activities filterin for other intents?
24 |
25 | */
26 | open class NfcInterceptorActivity : AppCompatActivity() {
27 | var mDispatcher: ForegroundDispatcher? = null;
28 |
29 | override fun onCreate(savedInstanceState: Bundle?) {
30 | super.onCreate(savedInstanceState)
31 | mDispatcher = ForegroundDispatcher(this)
32 | }
33 |
34 | public override fun onResume() {
35 | super.onResume()
36 | mDispatcher!!.enable()
37 | }
38 |
39 | public override fun onPause() {
40 | super.onPause()
41 | mDispatcher!!.disable()
42 | }
43 | }
--------------------------------------------------------------------------------
/identsdk/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
15 |
18 |
21 |
23 |
25 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/identsdk/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 28
7 |
8 |
9 | defaultConfig {
10 | minSdkVersion 21
11 | targetSdkVersion 29
12 | versionCode 3
13 | versionName "0.2.1"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 |
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 |
31 | implementation 'androidx.appcompat:appcompat:1.1.0'
32 |
33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
34 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
35 | testImplementation 'junit:junit:4.12'
36 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
37 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
38 |
39 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
40 |
41 | implementation 'com.governikus:ausweisapp:1.20.0'
42 | implementation 'com.google.code.gson:gson:2.8.5'
43 | implementation 'com.squareup.okhttp3:okhttp:3.11.0'
44 |
45 | implementation 'com.google.android.material:material:1.0.0'
46 | }
47 | repositories {
48 | mavenCentral()
49 | }
50 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/fragment_insert_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/util/ForegroundDispatcher.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018. Moritz Morgenroth, Inc - All Rights Reserved
3 | * Unauthorized copying of this file, via any medium is strictly prohibited
4 | * Proprietary and confidential
5 | * Written by Moritz Morgenroth
6 | */
7 |
8 | package com.twigbit.identsdk.util
9 |
10 | import android.app.Activity
11 | import android.app.PendingIntent
12 | import android.content.Intent
13 | import android.content.IntentFilter
14 | import android.nfc.NfcAdapter
15 | import android.nfc.tech.IsoDep
16 |
17 | class ForegroundDispatcher(private val mActivity: Activity) {
18 | private val mAdapter: NfcAdapter?
19 | private val mPendingIntent: PendingIntent
20 | private val mFilters: Array
21 | private val mTechLists: Array>
22 |
23 | init {
24 | mAdapter = NfcAdapter.getDefaultAdapter(mActivity)
25 | val intent = Intent(mActivity, mActivity.javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
26 | mPendingIntent = PendingIntent.getActivity(mActivity, 0, intent, 0)
27 |
28 | mFilters = arrayOf(IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED))
29 | mTechLists = arrayOf(arrayOf(IsoDep::class.java.name))
30 | }
31 |
32 | fun enable() {
33 | mAdapter?.enableForegroundDispatch(mActivity,
34 | mPendingIntent,
35 | mFilters,
36 | mTechLists)
37 | }
38 |
39 | fun disable() {
40 | mAdapter?.disableForegroundDispatch(mActivity)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/core/IdentificationActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018. Moritz Morgenroth- All Rights Reserved
3 | * Unauthorized copying of this file, via any medium is strictly prohibited
4 | * Proprietary and confidential
5 | * Written by Moritz Morgenroth
6 | */
7 |
8 | package com.twigbit.identsdk.core
9 |
10 | import android.content.Intent
11 | import android.nfc.NfcAdapter
12 | import android.nfc.Tag
13 | import android.os.Bundle
14 | import com.twigbit.identsdk.util.NfcInterceptorActivity
15 |
16 | abstract class IdentificationActivity : NfcInterceptorActivity() {
17 | var identificationManager = IdentificationManager()
18 |
19 | override fun onNewIntent(intent: Intent?) {
20 | super.onNewIntent(intent)
21 | val tag = intent!!.getParcelableExtra(NfcAdapter.EXTRA_TAG)
22 | if (tag != null) {
23 | identificationManager.dispatchNfcTag(tag)
24 | }
25 | }
26 |
27 | override fun onCreate(savedInstanceState: Bundle?) {
28 | super.onCreate(savedInstanceState)
29 | identificationManager.bind(applicationContext)
30 |
31 | }
32 | override fun onStart() {
33 | super.onStart()
34 | // identificationManager.bind(applicationContext)
35 | }
36 |
37 | override fun onDestroy() {
38 | super.onDestroy()
39 | identificationManager.unBind(applicationContext)
40 | }
41 | override fun onStop() {
42 | super.onStop()
43 | // identificationManager.unBind(applicationContext)
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/activity_dropin_identification.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
30 |
31 |
--------------------------------------------------------------------------------
/example/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'kotlin-android-extensions'
6 |
7 | android {
8 | signingConfigs {
9 | alpha {
10 | storeFile file('/Users/moritzmorgenroth/Documents/alpha.jks')
11 | storePassword 'bm6NJLHnFBcse5Yn9k'
12 | keyPassword 'bm6NJLHnFBcse5Yn9k'
13 | keyAlias = 'key0'
14 | }
15 | }
16 | compileSdkVersion 28
17 | defaultConfig {
18 | applicationId "com.twigbit.identsdk.example"
19 | minSdkVersion 21
20 | targetSdkVersion 28
21 | versionCode 1
22 | versionName "1.0"
23 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
24 | }
25 | buildTypes {
26 | release {
27 | minifyEnabled false
28 | signingConfig signingConfigs.alpha
29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
30 | }
31 | }
32 | }
33 |
34 | dependencies {
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
37 | implementation 'androidx.appcompat:appcompat:1.0.0'
38 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
39 | // implementation 'com.github.twigbit:ident-sdk:0.1.5'
40 | implementation project(path: ':identsdk')
41 |
42 | testImplementation 'junit:junit:4.12'
43 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
44 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
45 |
46 | implementation 'com.google.android.material:material:1.0.0'
47 | }
48 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/fragment_intro.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
21 |
22 |
28 |
29 |
--------------------------------------------------------------------------------
/example/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/fragment_error.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
21 |
22 |
29 |
30 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/fragment_success.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
21 |
22 |
29 |
30 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/util/StringUtil.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.util
2 |
3 | import android.content.Context
4 | import com.twigbit.identsdk.R
5 | import com.twigbit.identsdk.core.AccessRights
6 |
7 | object StringUtil{
8 | fun translate(context: Context, string: String): String{
9 | val id = when (string){
10 | AccessRights.ADDRESS -> R.string.twigbit_ident_access_right_address
11 | AccessRights.BIRTH_NAME -> R.string.twigbit_ident_access_right_birth_name
12 | AccessRights.FAMILY_NAME -> R.string.twigbit_ident_access_right_family_name
13 | AccessRights.GIVEN_NAMES -> R.string.twigbit_ident_access_right_given_names
14 | AccessRights.PLACE_OF_BIRTH -> R.string.twigbit_ident_access_right_place_of_birth
15 | AccessRights.DATE_OF_BIRTH -> R.string.twigbit_ident_access_right_date_of_birth
16 | AccessRights.DOCTORAL_DEGREE -> R.string.twigbit_ident_access_right_doctoral_degree
17 | AccessRights.ARTISTIC_NAME -> R.string.twigbit_ident_access_right_artistic_name
18 | AccessRights.PSEUDONYM -> R.string.twigbit_ident_access_right_pseudonym
19 | AccessRights.VALID_UNTIL -> R.string.twigbit_ident_access_right_valid_until
20 | AccessRights.NATIONALITY -> R.string.twigbit_ident_access_right_nationality
21 | AccessRights.ISSUING_COUNTRY -> R.string.twigbit_ident_access_right_issuing_country
22 | AccessRights.DOCUMENT_TYPE -> R.string.twigbit_ident_access_right_document_type
23 | AccessRights.RESIDENCE_PERMIT_I -> R.string.twigbit_ident_access_right_residence_permit_i
24 | AccessRights.RESIDENCE_PERMIT_II -> R.string.twigbit_ident_access_right_residence_permit_ii
25 | AccessRights.COMMUNITY_ID -> R.string.twigbit_ident_access_right_community_id
26 | AccessRights.ADDRESS_VERIFICATION -> R.string.twigbit_ident_access_right_address_verification
27 | AccessRights.AGE_VERIFICATION -> R.string.twigbit_ident_access_right_age_verification
28 | else -> R.string.twigbit_ident_access_right_unknown_value
29 | }
30 | return context.getString(id)
31 | }
32 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/fragment_authorisation.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
27 |
28 |
44 |
45 |
58 |
--------------------------------------------------------------------------------
/docs/STYLEGUIDE.md:
--------------------------------------------------------------------------------
1 | # Drop-In UI Styleguide
2 |
3 | In the following, we described how you can use android styles to adapt the drop in UI to your apps style.
4 |
5 | Generally, all UI components in the Drop-In UI are themed with specific styles that you can override.
6 | To avoid collision with your projects resources, all styles are prefixed with `TwigbitIdentTheme.` and string resources are prefixed with `twigbit_ident_`
7 |
8 | ## Styling the colors
9 |
10 | As per default, the Drop-In UI adjusts to the colors set according to the commona android naming conventions, i.e. `colorPrimary`, `colorPrimaryDart`, and `colorAccent`.
11 | To adjust the colors of the drop in UI, simply override these colors in your `colors.xml` file.
12 | Gernerally, this should already be implemented in your project and should not need any specific adjustments.
13 |
14 | To adjust the colors of specific UI components such as texts of buttons, override the component-specific styles as described below.
15 |
16 | ## Styling text appearances
17 |
18 | If you want to customize the text appearances to use a custom font or custom styling, you can restyle the text appearance of the Drop-In UI.
19 | There are three common text appearances that you can override in your `styles.xml` `TwigbitIdentTheme.TextAppearanceOverline`, `TwigbitIdentTheme.TextAppearanceBody`, `TwigbitIdentTheme.TextAppearanceHeadline` are used to style the respective text instances.
20 |
21 | We recomment that you extend one of the `TextAppearance.MaterialComponents.` text appearances for overall integrity.
22 |
23 | ## Customizing the text content
24 |
25 | If you want to customize the texts displayed or add translations to the Drop-In UI, you can override the string resource values.
26 | The values are all prefixed with `twigbit_ident_`.
27 | For a full list of identification related string resources, that can be overridden, please look at the libraries [strings.xml](../identsdk/src/main/res/values/strings.xml)
28 |
29 | If you should add translations for different common languages, feel free to open a [pull request](https://github.com/twigbit/ident-sdk/pulls) to merge your translations to the library generally. Currently, the library only supports german.
30 |
31 | ## Styling the buttons
32 |
33 | To customize the button appearance of the UI, you need to override `TwigbitIdentTheme.ButtonStyle` for the default raised buttons and `TwigbitIdentTheme.ButtonStyle.TextButton` for the text button with you own custom button style implementation.
34 | It is recommended that you extend a `MaterialComponent` as a base style, such as `Widget.MaterialComponents.Button`.
35 |
36 | ## Displaying custom icons
37 |
38 | To customize the icons displayed in the UI, you can override the drawable files related to the SDK.
39 | Aagain, all files are prefixed with `twigbit_ident_`.
40 | For a full list of drawables that can be overridden, please look at the libraries [drawable folder](../identsdk/src/main/res/drawable/)
41 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/ausweisident/UserInfo.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.ausweisident
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | data class UserInfo(
6 | @SerializedName("given_name")
7 | val givenName: String?,
8 | @SerializedName("family_name")
9 | val familyName: String?,
10 | @SerializedName("name")
11 | val name: String?,
12 | /**
13 | * birthdate
14 | *
15 | * Format: YYYY-MM-DD
16 | */
17 | @SerializedName("birthdate")
18 | val birthdate: String?,
19 | @SerializedName("https://ref-ausweisident.eid-service.de/addressType")
20 | val addressType: String?,
21 | @SerializedName("address")
22 | val address: UserInfo.Address?,
23 | @SerializedName("artistic_name")
24 | val artisticName: String?,
25 |
26 | @SerializedName("https://ref-ausweisident.eid-service.de/birthname")
27 | val birthname: String?,
28 | @SerializedName("https://ref-ausweisident.eid-service.de/nationality")
29 | val nationality: String?,
30 | @SerializedName("https://ref-ausweisident.eid-service.de/academicTitle")
31 | val academicTitle: String,
32 | @SerializedName("https://ref-ausweisident.eid-service.de/issuingState")
33 | val issuingState: String,
34 | @SerializedName("https://ref-ausweisident.eid-service.de/restrictedId")
35 | val restrictedId: String,
36 | @SerializedName("https://ref-ausweisident.eid-service.de/placeOfBirthType")
37 | val placeOfBirthType: String?,
38 | @SerializedName("https://ref-ausweisident.eid-service.de/placeOfBirth")
39 | val placeOfBirth: UserInfo.Address?,
40 | @SerializedName("https://ref-ausweisident.eid-service.de/documentType")
41 | val documentType: String?,
42 | @SerializedName("https://ref-ausweisident.eid-service.de/residencePermitI")
43 | val residencePermitI: String?,
44 |
45 | val iss: String?,
46 | val sub: String?,
47 | val aud: String?,
48 | val iat: String?,
49 | val jti: String?
50 |
51 |
52 | ) {
53 | data class Address(
54 | /**
55 | * One letter country code
56 | * @example "D"
57 | */
58 | val country: String?,
59 | val streetAddress: String?,
60 | val formatted: String?,
61 | val postalCode: String?,
62 | val locality: String?,
63 | val region: String?
64 | )
65 |
66 | override fun toString(): String {
67 | return "UserInfo(givenName=$givenName, familyName=$familyName, name=$name, birthdate=$birthdate, " +
68 | "addressType=$addressType, address=$address, artisticName=$artisticName, birthname=$birthname, nationality=$nationality, academicTitle='$academicTitle', issuingState='$issuingState', restrictedId='$restrictedId', placeOfBirthType=$placeOfBirthType, placeOfBirth=$placeOfBirth, documentType=$documentType, residencePermitI=$residencePermitI, iss=$iss, sub=$sub, aud=$aud, iat=$iat, jti=$jti" +
69 | ")"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/core/IdentificationFragment.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.core
2 |
3 | import android.os.Build
4 | import android.os.Bundle
5 | import androidx.fragment.app.Fragment
6 | import androidx.appcompat.app.AppCompatActivity
7 | import java.lang.Exception
8 |
9 | class IdentificationFragment : androidx.fragment.app.Fragment() {
10 | var identificationManager = IdentificationManager()
11 |
12 | override fun onCreate(savedInstanceState: Bundle?) {
13 | super.onCreate(savedInstanceState)
14 | context?.let { identificationManager.bind(it) }
15 | }
16 |
17 | override fun onDestroy() {
18 | super.onDestroy()
19 | context?.let { identificationManager.unBind(it) }
20 | }
21 |
22 | override fun onStart() {
23 | super.onStart()
24 | // context?.let { identificationManager.bind(it) }
25 | }
26 |
27 | override fun onStop() {
28 | super.onStop()
29 | // context?.let { identificationManager.unBind(it) }
30 | }
31 |
32 | companion object {
33 | val TAG = "com.twigbit.identsdk.core.IdentificationFragment"
34 |
35 | fun newInstance(activity: AppCompatActivity): IdentificationFragment {
36 | // try to get the fragment instance if it is already attached
37 | val fm = activity.supportFragmentManager
38 | var identificationFragment: IdentificationFragment? = fm.findFragmentByTag(TAG) as IdentificationFragment?
39 | if (identificationFragment == null) {
40 | identificationFragment = IdentificationFragment()
41 | try {
42 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
43 | try {
44 | fm.beginTransaction().add(identificationFragment, TAG).commitNow()
45 | } catch (e: IllegalStateException) {
46 | fm.beginTransaction().add(identificationFragment, TAG).commit()
47 | try {
48 | fm.executePendingTransactions()
49 | } catch (ignored: IllegalStateException) {
50 | }
51 |
52 | } catch (e: NullPointerException) {
53 | fm.beginTransaction().add(identificationFragment, TAG).commit()
54 | try {
55 | fm.executePendingTransactions()
56 | } catch (ignored: IllegalStateException) {
57 | }
58 | }
59 |
60 | } else {
61 | fm.beginTransaction().add(identificationFragment, TAG).commit()
62 | try {
63 | fm.executePendingTransactions()
64 | } catch (ignored: IllegalStateException) {
65 | }
66 |
67 | }
68 | } catch (e: IllegalStateException) {
69 | throw Exception(e.message)
70 | }
71 |
72 | }
73 | return identificationFragment
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/.idea/markdown-navigator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
18 |
23 |
24 |
32 |
37 |
42 |
43 |
49 |
50 |
52 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/CertificateFragment.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 |
4 | import android.os.Bundle
5 | import androidx.fragment.app.Fragment
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 |
10 | import com.twigbit.identsdk.R
11 | import com.twigbit.identsdk.core.CertificateInfo
12 | import com.twigbit.identsdk.core.CertificateValidity
13 | import kotlinx.android.synthetic.main.activity_dropin_identification.*
14 | import kotlinx.android.synthetic.main.fragment_certificate.view.*
15 |
16 | // {"description":{"issuerName":"D-Trust GmbH","issuerUrl":"http://www.d-trust.net","purpose":"AusweisIDent - Online Ausweis Identifizierungsservice der Bundesdruckerei GmbH","subjectName":"Bundesdruckerei GmbH","subjectUrl":"https://ref-ausweisident.eid-service.de","termsOfUsage":"Name, Anschrift und E-Mail-Adresse des Diensteanbieters:\r\nBundesdruckerei GmbH\r\nOlaf Clemens\r\nKommandantenstraße 18\r\n10969 Berlin\r\nsupport@bdr.de\r\n\r\nGeschäftszweck:\r\nAusweisIDent - Online Ausweis Identifizierungsservice der Bundesdruckerei GmbH\r\n\r\nHinweis auf die für den Diensteanbieter zuständigen Stellen, die die Einhaltung der Vorschriften zum Datenschutz kontrollieren:\r\nDie Bundesbeauftragte für den Datenschutz und die Informationsfreiheit\r\nHusarenstraße 30\r\n53117 Bonn\r\n+49 (0)228 997799-0\r\npoststelle@bfdi.bund.de\r\n"},"msg":"CERTIFICATE","validity":{"effectiveDate":"2019-06-24","expirationDate":"2019-06-25"}}
17 |
18 | /**
19 | * A simple [Fragment] subclass.
20 | *
21 | */
22 | class CertificateFragment : androidx.fragment.app.Fragment() {
23 | var certificateInfo: CertificateInfo? = null
24 | set(value) {
25 | field = value
26 | view?.let { showCertificateData(it) }
27 | }
28 | var certificateValidity: CertificateValidity? = null
29 | set(value) {
30 | field = value
31 | view?.let { showCertificateData(it) }
32 | }
33 |
34 | override fun onCreateView(
35 | inflater: LayoutInflater, container: ViewGroup?,
36 | savedInstanceState: Bundle?
37 | ): View? {
38 | if (activity is DropInIdentificationActivity) (activity as DropInIdentificationActivity).imageView.visibility =
39 | View.GONE
40 | // Inflate the layout for this fragment
41 | val v = inflater.inflate(R.layout.fragment_certificate, container, false)
42 | v.buttonBack.setOnClickListener {
43 | activity!!.supportFragmentManager.popBackStack()
44 | if (activity is DropInIdentificationActivity) (activity as DropInIdentificationActivity).imageView.visibility =
45 | View.VISIBLE
46 | }
47 | showCertificateData(v)
48 | return v;
49 | }
50 |
51 | fun showCertificateData(v: View) {
52 | if (certificateInfo != null) {
53 | v.textIssuerName?.text = certificateInfo?.issuerName
54 | v.textIssuerUrl?.text = certificateInfo?.issuerUrl
55 | v.textServiceProviderName?.text = certificateInfo?.subjectName
56 | v.textServiceProviderUrl?.text = certificateInfo?.subjectUrl
57 | v.textPurpose?.text = certificateInfo?.purpose
58 | v.textServiceProviderInfo?.text = certificateInfo?.termsOfUsage
59 | }
60 | if (certificateValidity != null) {
61 | v.textValidity?.text =
62 | "${certificateValidity?.effectiveDate} - ${certificateValidity?.expirationDate}"
63 | }
64 | }
65 |
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/ausweisident/AusweisIdentBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.ausweisident
2 |
3 | import android.net.Uri
4 |
5 | /**
6 | * Enables declarative building of the `tcTokenUrl` required for AusweisIdent.
7 | *
8 | * Fluid API to construct a `tcTokenUrl`.
9 | * Calls to [clientId] and [redirectUrl] methods are required.
10 | *
11 | * Example:
12 | *
13 | * ```
14 | * val ausweisIdentTcTokenUrl: String = AusweisIdentBuilder()
15 | * .scope(AusweisIdentScopes.BIRTH_NAME)
16 | * .scope(AusweisIdentScopes.GIVEN_NAMES)
17 | * .state("123456")
18 | * .clientId("ABCDEFG")
19 | * .redirectUrl("https://yourserver.com")
20 | * .build()
21 | * ```
22 | *
23 | */
24 | class AusweisIdentBuilder {
25 |
26 | private var host: String = OPENID_HOST
27 | private var state: String = ""
28 | private var nonce: String = ""
29 | private var clientId: String = ""
30 | private var redirectUrl: String = ""
31 | private val scopes: MutableSet = LinkedHashSet()
32 |
33 | /**
34 | * Set the AusweisIdent Server Endpoint.
35 | *
36 | * @param host Must not contain the scheme (e.g. ausweis-ident.de and not https://ausw...)
37 | */
38 | fun host(host: String) = apply { this.host = host }
39 |
40 | /**
41 | * Set the AusweisIdent Server Endpoint to the default one for the reference / test system.
42 | *
43 | * @param enable Weather to enable or disable the reference endpoint.
44 | */
45 | fun ref(enable: Boolean = true) = apply { host = if (enable) OPENID_HOST_REF else OPENID_HOST }
46 |
47 | /**
48 | * Add a scope. See [AusweisIdentScopes] for currently available ones.
49 | *
50 | * @param scope String Identifier of the scope. Check AusweisIdent docs for details.
51 | */
52 | fun scope(scope: String) = apply { scopes.add(scope) }
53 |
54 | /**
55 | * Set the OpenID state parameter
56 | */
57 | fun state(state: String) = apply { this.state = state }
58 |
59 | /**
60 | * Set the OpenID nonce parameter
61 | */
62 | fun nonce(nonce: String) = apply { this.nonce = nonce }
63 |
64 | /**
65 | * Set your the AusweisIdent clientId.
66 | */
67 | fun clientId(clientId: String) = apply { this.clientId = clientId }
68 |
69 | /**
70 | * Set your AusweisIdent redirectUrl. Must match the one, you set in your AusweisIdent registration.
71 | */
72 | fun redirectUrl(redirectUrl: String) = apply { this.redirectUrl = redirectUrl }
73 |
74 | /**
75 | * Build the `tcTokenUrl`
76 | */
77 | fun build(): String {
78 | val b = Uri.Builder()
79 |
80 | b.scheme("https")
81 | b.authority(host)
82 | b.appendEncodedPath(OPENID_AUTHORIZATION_ENDPOINT)
83 |
84 | b.appendQueryParameter("client_id", clientId)
85 | b.appendQueryParameter("redirect_uri", redirectUrl)
86 |
87 | if (scopes.isNotEmpty()) b.appendQueryParameter("scope", scopes.joinToString(" "))
88 | if (state.isNotBlank()) b.appendQueryParameter("state", state)
89 | if (nonce.isNotBlank()) b.appendQueryParameter("nonce", nonce)
90 |
91 | b.appendQueryParameter("response_type", "code")
92 | b.appendQueryParameter("acr_values", "integrated")
93 |
94 | return b.build().toString()
95 | }
96 |
97 | companion object {
98 | const val OPENID_HOST = "ausweis-ident.de"
99 | const val OPENID_HOST_REF = "ref-ausweisident.eid-service.de"
100 | const val OPENID_AUTHORIZATION_ENDPOINT = "oic/authorize"
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/AccessRightsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.Fragment
5 | import androidx.recyclerview.widget.RecyclerView
6 | import android.util.Log
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import androidx.recyclerview.widget.LinearLayoutManager
11 |
12 | import com.twigbit.identsdk.R
13 | import com.twigbit.identsdk.core.CertificateInfo
14 | import com.twigbit.identsdk.util.Tags
15 | import kotlinx.android.synthetic.main.activity_dropin_identification.*
16 | import kotlinx.android.synthetic.main.fragment_access_rights.view.*
17 | import kotlinx.android.synthetic.main.fragment_access_rights.view.textPurpose
18 | import kotlinx.android.synthetic.main.fragment_access_rights.view.textServiceProvider
19 | import kotlinx.android.synthetic.main.holder_access_right.view.*
20 |
21 | /**
22 | * A simple [Fragment] subclass.
23 | *
24 | */
25 | class AccessRightsFragment : androidx.fragment.app.Fragment() {
26 |
27 | var certificateInfo: CertificateInfo? = null
28 | set(value) {
29 | field = value
30 | view?.let {
31 | it.textServiceProvider.text = value?.subjectName; it.textPurpose.text =
32 | value?.purpose
33 | }
34 | }
35 |
36 | var accessRights: ArrayList = arrayListOf()
37 | set(value) {
38 | field = value
39 | Log.d(Tags.TAG_IDENT_DEBUG, accessRights.toString())
40 | adapter.data = value
41 | adapter.notifyDataSetChanged()
42 | }
43 | val adapter = MyAdapter(accessRights)
44 |
45 | override fun onCreateView(
46 | inflater: LayoutInflater, container: ViewGroup?,
47 | savedInstanceState: Bundle?
48 | ): View? {
49 | // Inflate the layout for this fragment
50 | val v = inflater.inflate(R.layout.fragment_access_rights, container, false)
51 | v.buttonAccept.setOnClickListener {
52 | activity?.asIdentificationUI()?.identificationManager?.acceptAccessRights()
53 | activity?.asIdentificationUI()?.showLoader()
54 | }
55 | v.cardServiceProvider.setOnClickListener {
56 | activity?.asIdentificationUI()?.showCertificate()
57 | }
58 | v.buttonDeny.setOnClickListener {
59 | activity?.asIdentificationUI()?.identificationManager?.cancel()
60 | activity?.finish()
61 | }
62 |
63 | v.recyclerView.adapter = adapter
64 | if (certificateInfo != null) {
65 | v.textServiceProvider?.text = certificateInfo?.subjectName
66 | v.textPurpose?.text = certificateInfo?.purpose
67 | }
68 | if (activity is DropInIdentificationActivity) (activity as DropInIdentificationActivity).imageView.visibility =
69 | View.VISIBLE
70 | return v
71 | }
72 | }
73 |
74 | // TODO display the certificate information
75 |
76 | class MyAdapter(var data: List) :
77 | RecyclerView.Adapter() {
78 |
79 | class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view)
80 |
81 |
82 | // Create new views (invoked by the layout manager)
83 | override fun onCreateViewHolder(
84 | parent: ViewGroup,
85 | viewType: Int
86 | ): MyViewHolder {
87 | val v = LayoutInflater.from(parent.context)
88 | .inflate(R.layout.holder_access_right, parent, false)
89 | return MyViewHolder(v)
90 | }
91 |
92 | override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
93 | holder.view.text.text = data.get(position)
94 | }
95 |
96 | override fun getItemCount() = data.size
97 | }
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/AuthorisationFragment.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.Fragment
5 | import android.util.Log
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 |
10 | import com.twigbit.identsdk.R
11 | import com.twigbit.identsdk.util.Tags
12 | import kotlinx.android.synthetic.main.fragment_authorisation.view.*
13 |
14 | /**
15 | * A simple [Fragment] subclass.
16 | *
17 | */
18 | class AuthorisationFragment : androidx.fragment.app.Fragment() {
19 |
20 | override fun onCreateView(
21 | inflater: LayoutInflater, container: ViewGroup?,
22 | savedInstanceState: Bundle?
23 | ): View? {
24 | // Inflate the layout for this fragment
25 | val v = inflater.inflate(R.layout.fragment_authorisation, container, false)
26 | v.buttonContinue.setOnClickListener {
27 | if (v.editPin.text.length != 6) {
28 | v.editPin.setError("Die PIN hat 6 Stellen")
29 | } else onEntered(v.editPin.text.toString())
30 | }
31 | v.editPin.setOnEditorActionListener { v, actionId, event ->
32 | if (v.editPin.text.length != 6) {
33 | v.editPin.setError("Die PIN hat 6 Stellen")
34 | } else onEntered(v.editPin.text.toString())
35 | true
36 | }
37 | this.mode = arguments?.getInt(KEY_MODE)
38 |
39 | when (mode) {
40 | MODE_CAN -> {
41 | v.text?.text = getText(R.string.twigbit_ident_drop_in_enter_can)
42 | }
43 | MODE_PIN -> {
44 | v.text?.text = getText(R.string.twigbit_ident_drop_in_enter_pin)
45 | }
46 | MODE_PUK -> {
47 | v.text?.text = getText(R.string.twigbit_ident_drop_in_enter_puk)
48 | }
49 | }
50 | return v
51 | }
52 |
53 |
54 | override fun onResume() {
55 | super.onResume()
56 | view?.editPin?.text = null
57 | }
58 |
59 | fun onEntered(pin: String) {
60 | // TODO check pin lenght
61 | Log.d(Tags.TAG_IDENT_DEBUG, "Pin entered $pin");
62 | when (mode) {
63 | MODE_CAN -> {
64 | activity?.asIdentificationUI()?.identificationManager?.setCan(pin)
65 | }
66 | MODE_PIN -> {
67 | activity?.asIdentificationUI()?.identificationManager?.setPin(pin)
68 | }
69 | MODE_PUK -> {
70 | activity?.asIdentificationUI()?.identificationManager?.setPuk(pin)
71 | }
72 | }
73 | activity?.asIdentificationUI()?.showLoader()
74 | }
75 |
76 | companion object {
77 | const val MODE_CAN = 0;
78 | const val MODE_PIN = 1;
79 | const val MODE_PUK = 2;
80 | const val KEY_MODE = "key-mode"
81 | }
82 |
83 | override fun setArguments(args: Bundle?) {
84 | super.setArguments(args)
85 | this.mode = args!!.getInt(KEY_MODE)
86 | }
87 |
88 | var mode: Int? = MODE_CAN
89 | set(value) {
90 | when (value) {
91 | MODE_CAN -> {
92 | view?.text?.text = getText(R.string.twigbit_ident_drop_in_enter_can)
93 | }
94 | MODE_PIN -> {
95 | view?.text?.text = getText(R.string.twigbit_ident_drop_in_enter_pin)
96 | }
97 | MODE_PUK -> {
98 | view?.text?.text = getText(R.string.twigbit_ident_drop_in_enter_puk)
99 | }
100 | }
101 | field = value
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/ausweisident/AusweisIdentResultHandler.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.ausweisident
2 |
3 | import android.net.Uri
4 | import android.util.Log
5 | import com.google.gson.FieldNamingPolicy
6 | import com.google.gson.Gson
7 | import com.google.gson.GsonBuilder
8 | import com.twigbit.identsdk.util.Tags
9 | import okhttp3.*
10 | import java.io.IOException
11 | import java.net.CookieManager
12 | import java.net.CookieStore
13 | import java.net.URL
14 | import java.net.URLEncoder
15 |
16 | class AusweisIdentResultHandler(val callback: AusweisIdentResultHandler.Callback) {
17 |
18 | interface Callback {
19 | fun onError(message: String)
20 | fun onComplete(userInfo: UserInfo)
21 | }
22 |
23 | private val gson = GsonBuilder()
24 | .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
25 | .create()
26 | private var client: OkHttpClient = OkHttpClient.Builder()
27 | .cookieJar(InMemoryCookieJar(CookieManager()))
28 | .build()
29 |
30 | fun fetchResult(resultUrl: String) {
31 | execRequest(HttpUrl.parse(resultUrl)!!)
32 | }
33 |
34 | private fun execRequest(url: HttpUrl) {
35 | val request = Request.Builder()
36 | .url(url)
37 | .build()
38 |
39 | client.newCall(request).enqueue(object : okhttp3.Callback {
40 | override fun onFailure(call: Call, e: IOException) {
41 | Log.e(TAG, "Error while calling saml url")
42 | e.printStackTrace()
43 | callback.onError("Network error")
44 | }
45 |
46 | override fun onResponse(call: Call, response: Response) {
47 | Log.d(TAG, "Got response: $response")
48 |
49 | try {
50 | if (!response.isSuccessful) throw IOException("Unexpected status code");
51 |
52 | val body = response.body() ?: throw IOException("Unexpected empty body")
53 |
54 | val userInfo = gson.fromJson(body.charStream(), UserInfo::class.java)
55 |
56 | Log.d(Tags.TAG_IDENT_DEBUG, userInfo.toString())
57 |
58 | callback.onComplete(userInfo)
59 | } catch (ex: IOException) {
60 | Log.e(TAG, ex.toString())
61 | ex.printStackTrace()
62 | callback.onError("Unknown Error")
63 | }
64 | }
65 | })
66 | }
67 |
68 | companion object {
69 | const val TAG = "AusweisIdent"
70 |
71 | // AusweisIdent OpenID Connect Endpoints
72 | // const val ENDPOINT_OIC = "https://ausweis-ident.de/oic"
73 | const val ENDPOINT_OIC = "https://ref-ausweisident.eid-service.de/oic"
74 | const val ENDPOINT_OIC_AUTHORIZE = "$ENDPOINT_OIC/authorize"
75 | const val ENDPOINT_OIC_TOKEN = "$ENDPOINT_OIC/token"
76 | const val ENDPOINT_OIC_USERINFO = "$ENDPOINT_OIC/user-info"
77 |
78 | fun getTokenUrl(redirectUrl: String, clientId: String): String {
79 | val scopes = arrayOf(
80 | "BirthName",
81 | "FamilyNames",
82 | "GivenNames",
83 | "DateOfBirth",
84 | "PlaceOfResidence",
85 | "Nationality",
86 | "AcademicTitle",
87 | "ArtisticName",
88 | "IssuingState",
89 | "RestrictedID",
90 | "PlaceOfBirth",
91 | "DocumentType",
92 | "ResidencePermitI",
93 | "DateOfExpiry"
94 | ).joinToString("+")
95 | val encRedirectUrl = URLEncoder.encode(redirectUrl, "UTF-8")
96 |
97 | return "$ENDPOINT_OIC_AUTHORIZE?scope=$scopes&response_type=code&redirect_uri=$encRedirectUrl&state=123456&client_id=$clientId&acr_values=integrated"
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/example/src/main/java/com/twigbit/identsdk/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.example
2 |
3 | import android.os.Bundle
4 | import com.twigbit.identsdk.dropinui.DropInRequest
5 | import android.app.Activity
6 | import android.content.Intent
7 | import android.net.Uri
8 | import androidx.appcompat.app.AppCompatActivity
9 | import android.util.Log
10 | import com.twigbit.identsdk.ausweisident.AusweisIdentBuilder
11 | import com.twigbit.identsdk.ausweisident.AusweisIdentResultHandler
12 | import com.twigbit.identsdk.ausweisident.AusweisIdentScopes
13 | import com.twigbit.identsdk.ausweisident.UserInfo
14 | import com.twigbit.identsdk.core.IdentificationManager
15 | import com.twigbit.identsdk.util.Tags
16 | import kotlinx.android.synthetic.main.activity_main.*
17 |
18 |
19 | class MainActivity : AppCompatActivity() {
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | setContentView(R.layout.activity_main)
24 |
25 | buttonDropIn.setOnClickListener {
26 | startDropInIdentification()
27 | }
28 | buttonCustomAuth.setOnClickListener {
29 | startIdentificationActivity()
30 | }
31 | referenceTwigbit.setOnClickListener {
32 | val intent = Intent()
33 | intent.action = Intent.ACTION_VIEW
34 | intent.addCategory(Intent.CATEGORY_BROWSABLE)
35 | intent.data = Uri.parse("https://twigbit.com")
36 | startActivity(intent)
37 | }
38 | }
39 |
40 | private fun startIdentificationActivity() {
41 | val intent = Intent(this, IndependentIdentificationActivity::class.java)
42 | startActivity(intent)
43 | }
44 |
45 | val REQUEST_CODE_IDENTIFICATION = 0;
46 | private fun startDropInIdentification() {
47 | val tcTokenUrl = AusweisIdentBuilder()
48 | .ref()
49 | .clientId(Secrets.CLIENT_ID)
50 | .redirectUrl(Secrets.CLIENT_REDIRECT_URL)
51 | .scope(AusweisIdentScopes.FAMILY_NAMES)
52 | .scope(AusweisIdentScopes.GIVEN_NAMES)
53 | .scope(AusweisIdentScopes.DATE_OF_BIRTH)
54 | .scope(AusweisIdentScopes.ACADEMIC_TITLE)
55 | .scope(AusweisIdentScopes.ARTISTIC_NAME)
56 | .scope(AusweisIdentScopes.DATE_OF_EXPIRY)
57 | .scope(AusweisIdentScopes.DOCUMENT_TYPE)
58 | .scope(AusweisIdentScopes.RESTRICTED_ID)
59 | .state("123456")
60 | .build()
61 |
62 | val dropInRequest = DropInRequest(tcTokenUrl)
63 | startActivityForResult(dropInRequest.getIntent(this), REQUEST_CODE_IDENTIFICATION)
64 | }
65 |
66 | val resultHandler: AusweisIdentResultHandler =
67 | AusweisIdentResultHandler(object : AusweisIdentResultHandler.Callback {
68 | override fun onError(message: String) {
69 | Log.d(Tags.TAG_IDENT_DEBUG, "An error occured")
70 | }
71 |
72 | override fun onComplete(userInfo: UserInfo) {
73 | Log.d(Tags.TAG_IDENT_DEBUG, userInfo.toString())
74 | }
75 | })
76 |
77 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
78 | if (requestCode == REQUEST_CODE_IDENTIFICATION) {
79 | if (resultCode == Activity.RESULT_OK) {
80 | // Success. Update the UI to reflect the successful identification
81 | // and fetch the user data from the server where they were delivered.
82 |
83 | val resultUrl = data!!.getStringExtra(IdentificationManager.EXTRA_DROPIN_RESULT)
84 | Log.d(Tags.TAG_IDENT_DEBUG, resultUrl);
85 |
86 | resultHandler.fetchResult(resultUrl);
87 | // to deliver the data to the server, call this URL and follow the redirect chain
88 |
89 | } else if (resultCode == Activity.RESULT_CANCELED) {
90 | // The user canceled the identification
91 | } else {
92 | // An error occured during the identification
93 | }
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | xmlns:android
17 |
18 | ^$
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | xmlns:.*
28 |
29 | ^$
30 |
31 |
32 | BY_NAME
33 |
34 |
35 |
36 |
37 |
38 |
39 | .*:id
40 |
41 | http://schemas.android.com/apk/res/android
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | .*:name
51 |
52 | http://schemas.android.com/apk/res/android
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | name
62 |
63 | ^$
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | style
73 |
74 | ^$
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | .*
84 |
85 | ^$
86 |
87 |
88 | BY_NAME
89 |
90 |
91 |
92 |
93 |
94 |
95 | .*
96 |
97 | http://schemas.android.com/apk/res/android
98 |
99 |
100 | ANDROID_ATTRIBUTE_ORDER
101 |
102 |
103 |
104 |
105 |
106 |
107 | .*
108 |
109 | .*
110 |
111 |
112 | BY_NAME
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator-enh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Twigbit Ident Identifizierung
3 |
4 | Bitte halten Sie ihren Personalausweis jetzt für den gesamten
5 | Vorgang gegen den
6 | NFC Sensor Ihres Smartphones.
7 |
8 | Willkommen, wir werden Sie jetzt durch die Identifizierung
9 | begleiten. Bitte
10 | halten Sie ihren Personalausweis gegen die Rückseite Ihres Smartphones.
11 |
12 | Starten
13 | Akzeptieren
14 | Abbrechen
15 | Beenden
16 | Bitte geben sie die CAN ein:
17 | Bitte geben sie ihre PIN ein:
18 | Bitte geben sie ihre PUK ein:
19 | Ihre Daten wurden erfolgreich ausgelesen.
20 |
21 | Ein Fehler ist aufgetreten.
22 | Willkommen, wir werden Sie jetzt durch die Identifizierung begleiten.
23 | Bitte halten Sie
24 | ihren Personalausweis während des gesamten Vorgangs gegen die Rückseite Ihres Smartphones.
25 |
26 | Weiter
27 |
28 | Anschrift
29 | Geburtsname
30 | Familienname
31 | Vornamen
32 | Ort der Geburt
33 | Tag der Geburt
34 | Akademisches Grad
35 | Ordens- / Künstlername
36 | Pseudonym
37 | Ablaufdatum
38 | Staatsangehöritgkeit
39 | Ausstellender Staat
40 | Dokumentenart
41 | Aufenthaltserlaubnis
42 | Aufenthaltserlaubnis 2
43 | community_id
44 | Wohnortverifikation
45 | Altersverifikation
46 | Unbekannter Wert
47 |
48 |
49 | Hello blank fragment
50 | Anbieterinformationen
51 | Diensteanbieter
52 | Aussteller des Berechtigungszertifikats
53 | Zweck
54 | Gültigkeit
55 | Angaben zum Diensteanbieter
56 |
57 | Der folgende Diensteanbieter möchte Ihre Identität
58 | überprüfen:
59 |
60 | Diensteanbieter
61 | Zweck der Auskunft
62 | Folgende Attribute werden ausgelesen
63 | Attribute
64 | Logo des Diensteanbieters
65 |
66 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/ausweisident/InMemoryCookieJar.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.ausweisident
2 |
3 | /*
4 | * Copyright (C) 2016 Square, Inc.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import java.io.IOException
20 | import java.net.CookieHandler
21 | import java.net.HttpCookie
22 | import java.util.ArrayList
23 | import java.util.Collections
24 | import okhttp3.Cookie
25 | import okhttp3.CookieJar
26 | import okhttp3.HttpUrl
27 | import okhttp3.internal.platform.Platform
28 |
29 | import okhttp3.internal.Util.delimiterOffset
30 | import okhttp3.internal.Util.trimSubstring
31 | import okhttp3.internal.platform.Platform.WARN
32 |
33 | /** A cookie jar that delegates to a [java.net.CookieHandler]. */
34 | class InMemoryCookieJar(private val cookieHandler: CookieHandler?) : CookieJar {
35 |
36 | override fun saveFromResponse(url: HttpUrl, cookies: List) {
37 | if (cookieHandler != null) {
38 | val cookieStrings = ArrayList()
39 | for (cookie in cookies) {
40 | cookieStrings.add(cookie.toString().replace("; domain=".toRegex(), "; domain=."))
41 | }
42 | val multimap = Collections.singletonMap>("Set-Cookie", cookieStrings)
43 | try {
44 | cookieHandler.put(url.uri(), multimap)
45 | } catch (e: IOException) {
46 | Platform.get().log(WARN, "Saving cookies failed for " + url.resolve("/...")!!, e)
47 | }
48 |
49 | }
50 | }
51 |
52 | override fun loadForRequest(url: HttpUrl): List {
53 | // The RI passes all headers. We don't have 'em, so we don't pass 'em!
54 | val headers = emptyMap>()
55 | val cookieHeaders: Map>
56 | try {
57 | cookieHeaders = cookieHandler!!.get(url.uri(), headers)
58 | } catch (e: IOException) {
59 | Platform.get().log(WARN, "Loading cookies failed for " + url.resolve("/...")!!, e)
60 | return emptyList()
61 | }
62 |
63 | var cookies: MutableList? = null
64 | for ((key, value) in cookieHeaders) {
65 | if (("Cookie".equals(key, ignoreCase = true) || "Cookie2".equals(
66 | key,
67 | ignoreCase = true
68 | )) && !value.isEmpty()
69 | ) {
70 | for (header in value) {
71 | if (cookies == null) cookies = ArrayList()
72 | cookies.addAll(decodeHeaderAsJavaNetCookies(url, header))
73 | }
74 | }
75 | }
76 |
77 | return if (cookies != null)
78 | Collections.unmodifiableList(cookies)
79 | else
80 | emptyList()
81 | }
82 |
83 | /**
84 | * Convert a request header to OkHttp's cookies via [HttpCookie]. That extra step handles
85 | * multiple cookies in a single request header, which [Cookie.parse] doesn't support.
86 | */
87 | private fun decodeHeaderAsJavaNetCookies(url: HttpUrl, header: String): List {
88 | val result = ArrayList()
89 | var pos = 0
90 | val limit = header.length
91 | var pairEnd: Int
92 | while (pos < limit) {
93 | pairEnd = delimiterOffset(header, pos, limit, ";,")
94 | val equalsSign = delimiterOffset(header, pos, pairEnd, '=')
95 | val name = trimSubstring(header, pos, equalsSign)
96 | if (name.startsWith("$")) {
97 | pos = pairEnd + 1
98 | continue
99 | }
100 |
101 | // We have either name=value or just a name.
102 | val value = if (equalsSign < pairEnd)
103 | trimSubstring(header, equalsSign + 1, pairEnd)
104 | else
105 | ""
106 |
107 | result.add(
108 | Cookie.Builder()
109 | .name(name)
110 | .value(value)
111 | .domain(url.host())
112 | .build()
113 | )
114 | pos = pairEnd + 1
115 | }
116 | return result
117 | }
118 | }
--------------------------------------------------------------------------------
/example/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/core/IdentificationUtil.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018. Moritz Morgenroth- All Rights Reserved
3 | * Unauthorized copying of this file, via any medium is strictly prohibited
4 | * Proprietary and confidential
5 | * Written by Moritz Morgenroth
6 | */
7 |
8 | package com.twigbit.identsdk.core
9 |
10 | import com.google.gson.Gson
11 | import java.net.URLEncoder
12 |
13 | object IdentificationUtil {
14 | const val MSG_ACCESS_RIGHTS = "ACCESS_RIGHTS"
15 | const val MSG_ENTER_PIN = "ENTER_PIN"
16 | const val MSG_ENTER_PUK = "ENTER_PUK"
17 | const val MSG_ENTER_CAN = "ENTER_CAN"
18 | const val MSG_INSERT_CARD = "INSERT_CARD"
19 | const val MSG_INSERT_CERTIFICATE = "CERTIFICATE"
20 | const val MSG_BAD_STATE = "BAD_STATE"
21 | const val MSG_READER = "READER"
22 | const val MSG_CUSTOM_URL = "URL"
23 | const val MSG_AUTH = "AUTH"
24 | const val CMD_RUN_AUTH = "RUN_AUTH"
25 | const val CMD_GET_CERTIFICATE = "GET_CERTIFICATE"
26 | const val CMD_ACCEPT = "ACCEPT"
27 | const val CMD_CANCEL = "CANCEL"
28 | const val CMD_GET_READER = "GET_READER_LIST"
29 | const val CMD_SET_PIN = "SET_PIN"
30 | const val CMD_SET_PUK = "SET_PUK"
31 | const val CMD_SET_CAN = "SET_CAN"
32 | const val PARAM_TCTOKEN = "tcTokenURL"
33 | const val PARAM_VALUE = "value"
34 | const val PARAM_CMD = "cmd"
35 |
36 |
37 | val gson = Gson()
38 | fun parseJson(msg: String): Message? {
39 | return gson.fromJson(msg, Message::class.java)
40 | }
41 |
42 | fun buildTokenUrl(redirectUrl: String, clientId: String): String {
43 | return "https://ref-ausweisident.eid-service.de/oic/authorize?scope=FamilyNames+GivenNames+AcademicTitle+PlaceOfBirth+DateOfBirth+PlaceOfResidence+&response_type=code&redirect_uri=${URLEncoder.encode(redirectUrl, "UTF-8")}&state=123456&&client_id=$clientId&acr_values=integrated"
44 | }
45 | fun buildCmdString(cmd: String, payload: Pair? = null): String{
46 | return "{\"$PARAM_CMD\": \"${cmd}\" " + (if (payload!= null) ", \"${payload.first}\": \"${payload.second}\"" else "") + "}"
47 | }
48 | }
49 |
50 | class Command {
51 | val cmd: String = ""
52 | }
53 |
54 | // "aux":{"validityDate":"2019-05-09"},"chat":{"effective":["Address","PlaceOfBirth","DateOfBirth","DoctoralDegree","FamilyName","GivenNames"],"optional":[],"required":["Address","PlaceOfBirth","DateOfBirth","DoctoralDegree","FamilyName","GivenNames"]}
55 | // {"description":{"issuerName":"D-Trust GmbH","issuerUrl":"http://www.d-trust.net","purpose":"AusweisIDent - Online Ausweis Identifizierungsservice der Bundesdruckerei GmbH","subjectName":"Bundesdruckerei GmbH","subjectUrl":"https://ref-ausweisident.eid-service.de","termsOfUsage":"Name, Anschrift und E-Mail-Adresse des Diensteanbieters:\r\nBundesdruckerei GmbH\r\nOlaf Clemens\r\nKommandantenstraße 18\r\n10969 Berlin\r\nsupport@bdr.de\r\n\r\nGeschäftszweck:\r\nAusweisIDent - Online Ausweis Identifizierungsservice der Bundesdruckerei GmbH\r\n\r\nHinweis auf die für den Diensteanbieter zuständigen Stellen, die die Einhaltung der Vorschriften zum Datenschutz kontrollieren:\r\nDie Bundesbeauftragte für den Datenschutz und die Informationsfreiheit\r\nHusarenstraße 30\r\n53117 Bonn\r\n+49 (0)228 997799-0\r\npoststelle@bfdi.bund.de\r\n"},"msg":"CERTIFICATE","validity":{"effectiveDate":"2019-06-24","expirationDate":"2019-06-25"}}
56 |
57 | class Message {
58 | val msg: String = ""
59 | val name: String = ""
60 | val card: Card? = null
61 | val result: Result? = null
62 | val chat: AccessRightPayload? = null
63 | val validity: CertificateValidity? = null
64 | val description: CertificateInfo? = null
65 | // TODO add description param for certificates
66 | override fun toString(): String {
67 | return "Message(msg='$msg', name='$name', card=$card, result=$result, chat=$chat)"
68 | }
69 |
70 | }
71 |
72 | class CertificateInfo{
73 | val issuerName: String = ""
74 | val issuerUrl: String = ""
75 | val purpose: String = ""
76 | val subjectName: String = ""
77 | val subjectUrl: String = ""
78 | val termsOfUsage: String = ""
79 | override fun toString(): String {
80 | return "CertificateInfo(issuerName=$issuerName, purpose=$purpose, subjectName=$subjectName)"
81 | }
82 | }
83 | class CertificateValidity{
84 | val effectiveDate: String = ""
85 | val expirationDate: String = ""
86 | override fun toString(): String {
87 | return "CertificateValidity(effectiveDate=$effectiveDate, expirationDate=$expirationDate)"
88 | }
89 | }
90 |
91 | class AccessRightPayload{
92 | val effective: List? = null;
93 | val optional: List? = null;
94 | val required: List? = null;
95 | override fun toString(): String {
96 | return "AccessRightPayload(effective=$effective, optional=$optional, required=$required)"
97 | }
98 |
99 | }
100 |
101 | class Card {
102 | val deactivated: Boolean = true;
103 | val inoperative: Boolean = false;
104 | val retryCounter: Int = -1;
105 | override fun toString(): String {
106 | return "Card(deactivated=$deactivated, inoperative=$inoperative, retryCounter=$retryCounter)"
107 | }
108 |
109 | }
110 |
111 | class Result {
112 | val major: String = ""
113 | val url: String = ""
114 | val description: String = ""
115 |
116 | override fun toString(): String {
117 | return "Result(major='$major', url='$url', description='$description' )"
118 | }
119 | }
120 |
121 | enum class IdentMode {
122 | PIN,
123 | PUK,
124 | CAN
125 | }
126 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/dropinui/DropInIdentificationActivity.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.dropinui
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import androidx.fragment.app.Fragment
7 | import android.util.Log
8 | import com.twigbit.identsdk.R
9 | import com.twigbit.identsdk.core.*
10 | import com.twigbit.identsdk.util.*
11 | import kotlinx.android.synthetic.main.fragment_intro.*
12 |
13 | fun Activity.asIdentificationUI(): IsIdentificationUI? {
14 | if (this is IsIdentificationUI) return this else return null
15 | }
16 |
17 | class DropInIdentificationActivity : IdentificationActivity(), IsIdentificationUI {
18 |
19 | val introFragment = IntroFragment()
20 | val loaderFragment = LoaderFragment()
21 | val accessRightsFragment = AccessRightsFragment()
22 | val authorisationFragment = AuthorisationFragment()
23 | val insertCardFragment = InsertCardFragment()
24 | val successFragment = SuccessFragment()
25 | val errorFragment = ErrorFragment()
26 | val certificateFragment = CertificateFragment()
27 |
28 | val identificationCallback = object : IdentificationManager.Callback {
29 |
30 | override fun onRequestCertificate(
31 | certificateInfo: CertificateInfo,
32 | certificateValidity: CertificateValidity
33 | ) {
34 | // The certificate info has beed requested and is delivered here
35 | accessRightsFragment.certificateInfo = certificateInfo;
36 | certificateFragment.certificateInfo = certificateInfo;
37 | certificateFragment.certificateValidity = certificateValidity;
38 | }
39 |
40 | override fun onCompleted(resultUrl: String) {
41 | // The identification was complete, display a success message to the user and fetch the identification result from the server using the resultUrl
42 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onComplete Callback")
43 | // showFragment(successFragment)
44 | returnResult(resultUrl)
45 | }
46 |
47 | override fun onRequestAccessRights(accessRights: List) {
48 | // A list of the fields that the sdk is trying to access has arrived. Display them to the user and await his confirmation.
49 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onRequestAccessRights Callback")
50 |
51 | identificationManager.getCertificate()
52 |
53 | accessRightsFragment.accessRights =
54 | ArrayList(accessRights.map {
55 | StringUtil.translate(
56 | this@DropInIdentificationActivity,
57 | it
58 | )
59 | })
60 | // for the moment just accept them
61 | showFragment(accessRightsFragment)
62 | }
63 |
64 | override fun onCardRecognized(card: Card?) {
65 | // A card was attached to the NFC reader
66 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onCardRecognized Callback")
67 | showFragment(insertCardFragment)
68 | }
69 |
70 | override fun onRequestPin() {
71 | // The id cards PIN was requested. Display a PIN dialog to the user.
72 | // To continue the identification process, call identificationManager.setPin(pin: String)
73 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onRequestPin Callback")
74 | authorisationFragment.mode = AuthorisationFragment.MODE_PIN
75 | authorisationFragment.arguments =
76 | Bundle().apply {
77 | putInt(
78 | AuthorisationFragment.KEY_MODE,
79 | AuthorisationFragment.MODE_PIN
80 | )
81 | }
82 | showFragment(authorisationFragment)
83 | }
84 |
85 | override fun onRequestPuk() {
86 | // The id cards PUK was requested. Display a PUK diaphlog to the user.
87 | // To continue the identification process, call identificationManager.setPuk(puk: String)
88 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onRequestPuk Callback")
89 | authorisationFragment.mode = AuthorisationFragment.MODE_PUK
90 | authorisationFragment.arguments =
91 | Bundle().apply {
92 | putInt(
93 | AuthorisationFragment.KEY_MODE,
94 | AuthorisationFragment.MODE_PUK
95 | )
96 | }
97 | showFragment(authorisationFragment)
98 | }
99 |
100 | override fun onRequestCan() {
101 | // The id cards CAN was requested. Display a CAN dialog to the user.
102 | // To continue the identification process, call identificationManager.setCan(can: String)
103 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onRequestCan Callback")
104 | authorisationFragment.mode = AuthorisationFragment.MODE_CAN
105 | authorisationFragment.arguments =
106 | Bundle().apply {
107 | putInt(
108 | AuthorisationFragment.KEY_MODE,
109 | AuthorisationFragment.MODE_CAN
110 | )
111 | }
112 | showFragment(authorisationFragment)
113 | }
114 |
115 | override fun onInitilized() {
116 | showFragment(introFragment)
117 | }
118 |
119 | override fun onError(error: String) {
120 | // An error occured. Display an error/issue dialog to the user.
121 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onError Callback")
122 | showFragment(errorFragment)
123 | }
124 | }
125 |
126 | fun showFragment(fragment: androidx.fragment.app.Fragment) {
127 | supportFragmentManager.beginTransaction().replace(R.id.container, fragment).commit()
128 | }
129 |
130 | override fun onCreate(savedInstanceState: Bundle?) {
131 | super.onCreate(savedInstanceState)
132 | setContentView(R.layout.activity_dropin_identification)
133 |
134 | identificationManager.addCallback(identificationCallback)
135 | showFragment(loaderFragment)
136 | }
137 |
138 | override fun startIdent() {
139 | identificationManager.startIdent(intent.getStringExtra(DropInRequest.EXTRA_TC_TOKEN_URL))
140 | }
141 |
142 |
143 | override fun showLoader() {
144 | showFragment(loaderFragment);
145 | }
146 |
147 | override fun showCertificate() {
148 | supportFragmentManager.beginTransaction().addToBackStack("")
149 | .replace(R.id.container, certificateFragment)
150 | .commit()
151 | }
152 |
153 | fun returnResult(resultUrl: String) {
154 | val data = Intent()
155 | data.putExtra(IdentificationManager.EXTRA_DROPIN_RESULT, resultUrl);
156 | setResult(RESULT_OK, data);
157 | finish();
158 | }
159 | }
160 |
161 | interface IsIdentificationUI : IdentificationManagerProvider {
162 | fun showLoader();
163 | fun startIdent();
164 | fun showCertificate();
165 | }
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/fragment_certificate.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
14 |
17 |
18 |
20 |
21 |
24 |
26 |
29 |
33 |
38 |
39 |
43 |
44 |
47 |
51 |
55 |
56 |
60 |
63 |
67 |
71 |
72 |
76 |
79 |
83 |
84 |
88 |
91 |
95 |
96 |
100 |
103 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/example/src/main/java/com/twigbit/identsdk/example/IndependentIdentificationActivity.kt:
--------------------------------------------------------------------------------
1 | package com.twigbit.identsdk.example
2 |
3 | import android.content.Intent
4 | import android.nfc.NfcAdapter
5 | import android.nfc.Tag
6 | import androidx.appcompat.app.AppCompatActivity
7 | import android.os.Bundle
8 | import androidx.fragment.app.Fragment
9 | import android.util.Log
10 | import com.twigbit.identsdk.ausweisident.AusweisIdentBuilder
11 | import com.twigbit.identsdk.ausweisident.AusweisIdentScopes
12 | import com.twigbit.identsdk.core.*
13 | import com.twigbit.identsdk.dropinui.*
14 | import com.twigbit.identsdk.util.ForegroundDispatcher
15 | import com.twigbit.identsdk.util.StringUtil
16 | import com.twigbit.identsdk.util.Tags
17 |
18 | class IndependentIdentificationActivity : AppCompatActivity(), IsIdentificationUI {
19 |
20 | override fun startIdent() {
21 | val tcTokenUrl = AusweisIdentBuilder()
22 | .ref()
23 | .clientId(Secrets.CLIENT_ID)
24 | .redirectUrl(Secrets.CLIENT_REDIRECT_URL)
25 | .scope(AusweisIdentScopes.FAMILY_NAMES)
26 | .scope(AusweisIdentScopes.GIVEN_NAMES)
27 | .scope(AusweisIdentScopes.DATE_OF_BIRTH)
28 | .state("123456")
29 | .build()
30 | identificationFragment?.identificationManager?.startIdent(tcTokenUrl);
31 | }
32 | override fun showLoader() {
33 | showFragment(loaderFragment)
34 | }
35 |
36 | var identificationFragment: IdentificationFragment? = null
37 | // convenience getter
38 | override val identificationManager: IdentificationManager?
39 | get() {
40 | return identificationFragment?.identificationManager
41 | }
42 | /*
43 | To receive and dispatch NFC tags to the SDK, we need to initalize a forground dispatcher and attach it to the lifecycle.
44 | Then, we can pass Tag Intents from the `onNewIntent` to the `Identificationmanager`
45 | */
46 |
47 | var mDispatcher: ForegroundDispatcher? = null;
48 |
49 |
50 | override fun onCreate(savedInstanceState: Bundle?) {
51 | super.onCreate(savedInstanceState)
52 | setContentView(R.layout.activity_independent_identification)
53 |
54 |
55 | identificationFragment = IdentificationFragment.newInstance(this)
56 | identificationFragment!!.identificationManager.addCallback(identificationCallback)
57 |
58 | // Initialize the NFC Tag foreground dispatcher
59 | mDispatcher = ForegroundDispatcher(this)
60 |
61 | showFragment(introFragment)
62 | }
63 | public override fun onResume() {
64 | super.onResume()
65 | mDispatcher!!.enable()
66 | }
67 |
68 | public override fun onPause() {
69 | super.onPause()
70 | mDispatcher!!.disable()
71 | }
72 |
73 | override fun onNewIntent(intent: Intent?) {
74 | super.onNewIntent(intent)
75 | val tag = intent!!.getParcelableExtra(NfcAdapter.EXTRA_TAG)
76 | if (tag != null) {
77 | identificationManager?.dispatchNfcTag(tag)
78 | }
79 | }
80 |
81 | /*
82 | These are the same fragments used by the drop in UI.
83 | For a custom UI implementation, you could implement your own fragments with your own UI components
84 | */
85 |
86 | val introFragment = IntroFragment()
87 | val loaderFragment = LoaderFragment()
88 | val accessRightsFragment = AccessRightsFragment()
89 | val authorisationFragment = AuthorisationFragment()
90 | val insertCardFragment = InsertCardFragment()
91 | val successFragment = SuccessFragment()
92 | val certificateFragment = CertificateFragment()
93 | val errorFragment = ErrorFragment()
94 |
95 | val identificationCallback = object: IdentificationManager.Callback{
96 | override fun onRequestCertificate(certificateInfo: CertificateInfo, certificateValidity: CertificateValidity) {
97 | // The certificate info has beed requested and is delivered here
98 | certificateFragment.certificateInfo = certificateInfo;
99 | certificateFragment.certificateValidity = certificateValidity;
100 | accessRightsFragment.certificateInfo = certificateInfo;
101 | }
102 |
103 | override fun onCompleted(resultUrl: String) {
104 | // The identification was complete, display a success message to the user and fetch the identification result from the server using the resultUrl
105 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onComplete Callback")
106 | showFragment(successFragment)
107 | }
108 |
109 | override fun onRequestAccessRights(accessRights: List) {
110 | // A list of the fields that the sdk is trying to access has arrived. Display them to the user and await his confirmation.
111 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onRequestAccessRights Callback")
112 |
113 | identificationManager?.getCertificate();
114 |
115 | accessRightsFragment.accessRights = ArrayList(accessRights.map { StringUtil.translate(this@IndependentIdentificationActivity, it)})
116 | // for the moment just accept them
117 | showFragment(accessRightsFragment)
118 | }
119 |
120 | override fun onCardRecognized(card: Card?) {
121 | // A card was attached to the NFC reader
122 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onCardRecognized Callback")
123 | showFragment(insertCardFragment)
124 | }
125 |
126 | override fun onRequestPin() {
127 | // The id cards PIN was requested. Display a PIN dialog to the user.
128 | // To continue the identification process, call identificationManager.setPin(pin: String)
129 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onRequestPin Callback")
130 | authorisationFragment.mode = AuthorisationFragment.MODE_PIN
131 | authorisationFragment.arguments = Bundle().apply { putInt(AuthorisationFragment.KEY_MODE, AuthorisationFragment.MODE_PIN) }
132 | showFragment(authorisationFragment)
133 | }
134 |
135 | override fun onRequestPuk() {
136 | // The id cards PUK was requested. Display a PUK diaphlog to the user.
137 | // To continue the identification process, call identificationManager.setPuk(puk: String)
138 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onRequestPuk Callback")
139 | authorisationFragment.mode = AuthorisationFragment.MODE_PUK
140 | authorisationFragment.arguments = Bundle().apply { putInt(AuthorisationFragment.KEY_MODE, AuthorisationFragment.MODE_PUK) }
141 | showFragment(authorisationFragment)
142 | }
143 |
144 | override fun onRequestCan() {
145 | // The id cards CAN was requested. Display a CAN dialog to the user.
146 | // To continue the identification process, call identificationManager.setCan(can: String)
147 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onRequestCan Callback")
148 | authorisationFragment.mode = AuthorisationFragment.MODE_CAN
149 | authorisationFragment.arguments = Bundle().apply { putInt(AuthorisationFragment.KEY_MODE, AuthorisationFragment.MODE_CAN) }
150 | showFragment(authorisationFragment)
151 | }
152 |
153 | override fun onError(error: String) {
154 | // An error occured. Display an error/issue dialog to the user.
155 | Log.d(Tags.TAG_IDENT_DEBUG, "Got onError Callback")
156 | showFragment(errorFragment)
157 | }
158 |
159 | }
160 | override fun showCertificate() {
161 | supportFragmentManager.beginTransaction().addToBackStack("").replace(com.twigbit.identsdk.R.id.container, certificateFragment).commit()
162 | }
163 |
164 | fun showFragment(fragment: androidx.fragment.app.Fragment){
165 | supportFragmentManager.beginTransaction().replace(com.twigbit.identsdk.R.id.container, fragment).commit()
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/.idea/assetWizardSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/identsdk/src/main/res/layout/fragment_access_rights.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
20 |
21 |
28 |
29 |
35 |
36 |
40 |
41 |
47 |
48 |
52 |
53 |
59 |
60 |
65 |
66 |
71 |
72 |
78 |
79 |
80 |
81 |
85 |
86 |
92 |
93 |
98 |
99 |
104 |
105 |
110 |
111 |
112 |
113 |
114 |
120 |
121 |
122 |
123 |
129 |
130 |
134 |
135 |
139 |
140 |
147 |
148 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
163 |
164 |
173 |
174 |
184 |
185 |
--------------------------------------------------------------------------------
/identsdk/src/main/java/com/twigbit/identsdk/core/IdentificationManager.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018. Moritz Morgenroth- All Rights Reserved
3 | * Unauthorized copying of this file, via any medium is strictly prohibited
4 | * Proprietary and confidential
5 | * Written by Moritz Morgenroth
6 | */
7 |
8 | package com.twigbit.identsdk.core
9 |
10 |
11 | import android.content.ComponentName
12 | import android.content.Context
13 | import android.content.Intent
14 | import android.content.ServiceConnection
15 | import android.nfc.Tag
16 | import android.os.IBinder
17 | import android.os.RemoteException
18 | import android.util.Log
19 | import com.governikus.ausweisapp2.IAusweisApp2Sdk
20 | import com.governikus.ausweisapp2.IAusweisApp2SdkCallback
21 | import com.twigbit.identsdk.util.*
22 |
23 | class IdentificationManager {
24 |
25 | companion object {
26 | @JvmField
27 | val EXTRA_DROPIN_RESULT = "extra-identification-result"
28 | }
29 |
30 | // TODO refactor into callback array or else replace by setter
31 | var callback: Callback? = null
32 |
33 | fun addCallback(callback: Callback) {
34 | this.callback = callback
35 | }
36 |
37 | // Initialize the auth Process via this function
38 | fun bind(context: Context) {
39 | bindIdIdentificationService(context)
40 | }
41 |
42 | fun unBind(context: Context) {
43 | context.unbindService(sdkConnection)
44 | }
45 |
46 | fun startIdent(tokenURL: String) {
47 | send(
48 | IdentificationUtil.buildCmdString(
49 | IdentificationUtil.CMD_RUN_AUTH, Pair(
50 | IdentificationUtil.PARAM_TCTOKEN, tokenURL
51 | )
52 | )
53 | )
54 | }
55 |
56 | fun setPin(pin: String) {
57 | send(
58 | IdentificationUtil.buildCmdString(
59 | IdentificationUtil.CMD_SET_PIN, Pair(
60 | IdentificationUtil.PARAM_VALUE, pin
61 | )
62 | )
63 | )
64 | }
65 |
66 | fun setPuk(puk: String) {
67 | send(
68 | IdentificationUtil.buildCmdString(
69 | IdentificationUtil.CMD_SET_PUK, Pair(
70 | IdentificationUtil.PARAM_VALUE, puk
71 | )
72 | )
73 | )
74 | }
75 |
76 | fun setCan(can: String) {
77 | send(
78 | IdentificationUtil.buildCmdString(
79 | IdentificationUtil.CMD_SET_CAN, Pair(
80 | IdentificationUtil.PARAM_VALUE, can
81 | )
82 | )
83 | )
84 | }
85 |
86 | fun acceptAccessRights() {
87 | send(IdentificationUtil.buildCmdString(IdentificationUtil.CMD_ACCEPT))
88 | }
89 |
90 | fun cancel() {
91 | send(IdentificationUtil.buildCmdString(IdentificationUtil.CMD_CANCEL))
92 | }
93 |
94 | fun getCertificate() {
95 | send(IdentificationUtil.buildCmdString(IdentificationUtil.CMD_GET_CERTIFICATE))
96 | }
97 |
98 | private fun send(cmd: String) {
99 | Log.d(Tags.TAG_IDENT_DEBUG, "Sending command: " + cmd)
100 | try {
101 | if (!sdk!!.send(sdkCallback.mSessionID, cmd)) {
102 | // TODO error
103 | Log.e(Tags.TAG_IDENT_DEBUG, "Could not sendRaw command to SDK")
104 | }
105 | } catch (e: Exception) {
106 | // TODO error
107 | // FIXME 20190509 This sometimes gets called without an error message. Possibly due to improperly terminated background service.
108 | Log.e(Tags.TAG_IDENT_DEBUG, "Could not sendRaw command to SDK")
109 | //Log.e(Tags.TAG_IDENT_DEBUG, e.message)
110 | }
111 | }
112 |
113 | private fun handleMessage(messageJson: String) {
114 | // Workaround for extracting the url from the messageJson.
115 |
116 | Log.d(Tags.TAG_IDENT_DEBUG, messageJson);
117 | // TODO refactor for production
118 | if (messageJson.indexOf("url") != -1) {
119 | // it contains the response
120 | val s = messageJson.substring(messageJson.indexOf("url") + 6, messageJson.length - 2);
121 | callback?.onCompleted(s)
122 | }
123 |
124 | Log.d(Tags.TAG_IDENT_DEBUG, messageJson);
125 |
126 | val message = IdentificationUtil.parseJson(messageJson)
127 | Log.d(Tags.TAG_IDENT_DEBUG, message.toString());
128 |
129 | if (message == null) {
130 | Log.d(Tags.TAG_IDENT_DEBUG, "Bad state")
131 | return
132 | }
133 |
134 |
135 | // Pass raw sdk messageJson to callback for debugging/migration
136 | //callback.onMessage(message)
137 |
138 | when (message.msg) {
139 | IdentificationUtil.MSG_AUTH -> {
140 | if (!message.result?.description.isNullOrEmpty() || message.result?.major == "http://www.bsi.bund.de/ecard/api/1.1/resultmajor#error") {
141 | // An error occured
142 | callback?.onError(message.result!!.description)
143 | authInProgress = false
144 | return
145 | } else if (messageJson.indexOf("url") != -1) {
146 | val s = messageJson.substring(
147 | messageJson.indexOf("url") + 6,
148 | messageJson.length - 2
149 | );
150 | //callback.onComplete(s)
151 | } else {
152 | authInProgress = true
153 | }
154 | };
155 | IdentificationUtil.MSG_ACCESS_RIGHTS -> {
156 | Log.d(Tags.TAG_IDENT_DEBUG, "Access rights")
157 | Log.d(Tags.TAG_IDENT_DEBUG, message.chat!!.effective!!.toString())
158 | val orderedParameters = arrayListOf(
159 | "FamilyName",
160 | "BirthName",
161 | "GivenNames",
162 | "DoctoralDegree",
163 | "DateOfBirth",
164 | "PlaceOfBirth",
165 | "Address",
166 | "Nationality",
167 | "DocumentType",
168 | "ValidUntil",
169 | "IssuingCountry",
170 | "ArtisticName",
171 | "Pseudonym",
172 | "ResidencePermitI"
173 | );
174 | callback?.onRequestAccessRights(message.chat!!.effective!!.sortedWith(compareBy({
175 | orderedParameters.indexOf(
176 | it
177 | )
178 | })))
179 | }
180 |
181 | IdentificationUtil.MSG_INSERT_CARD -> {
182 | callback?.onCardRecognized(message.card)
183 | }
184 | IdentificationUtil.MSG_ENTER_PIN -> {
185 | callback?.onRequestPin()
186 | }
187 | IdentificationUtil.MSG_ENTER_PUK -> {
188 | callback?.onRequestPuk()
189 | }
190 | IdentificationUtil.MSG_ENTER_CAN -> {
191 | callback?.onRequestCan()
192 | }
193 | IdentificationUtil.MSG_INSERT_CERTIFICATE -> {
194 | callback?.onRequestCertificate(message.description!!, message.validity!!)
195 | }
196 | IdentificationUtil.MSG_BAD_STATE -> {
197 | callback?.onError(
198 | messageJson
199 | )
200 | }
201 | // IdentificationUtil.MSG_READER -> {
202 | // callback?.onCardRecognized(message.card)
203 | // }
204 | // else -> Log.d(TAG, "Unhandled messageJson ${message}")
205 | }
206 |
207 | }
208 |
209 | interface Callback {
210 | fun onCompleted(resultUrl: String)
211 | fun onRequestAccessRights(accessRights: List)
212 | fun onCardRecognized(card: Card?) // when the card is null, there is no card available
213 | fun onRequestPin()
214 | fun onRequestPuk()
215 | fun onRequestCan()
216 | fun onInitilized()
217 | fun onRequestCertificate(
218 | certificateInfo: CertificateInfo,
219 | certificateValidity: CertificateValidity
220 | )
221 |
222 | fun onError(error: String)
223 | }
224 |
225 | private var authInProgress: Boolean = false
226 |
227 | /*
228 | AUSWEISAPP2 SDK Communication
229 |
230 | TODO: Error Callbacks
231 | TODO: State modeling
232 |
233 | */
234 |
235 | var sdk: IAusweisApp2Sdk? = null
236 | private var sdkConnection: ServiceConnection? = null
237 |
238 | private var sdkCallback = object : IAusweisApp2SdkCallback.Stub() {
239 | var mSessionID: String? = null
240 |
241 | @Throws(RemoteException::class)
242 | override fun sessionIdGenerated(
243 | pSessionId: String, pIsSecureSessionId: Boolean
244 | ) {
245 | mSessionID = pSessionId
246 | }
247 |
248 | @Throws(RemoteException::class)
249 | override fun receive(pJson: String) {
250 | handleMessage(pJson)
251 | }
252 |
253 | @Throws(RemoteException::class)
254 | override fun sdkDisconnected() {
255 | Log.d(Tags.TAG_IDENT_DEBUG, "SDK Disconnected")
256 | }
257 | }
258 |
259 | fun bindIdIdentificationService(context: Context) {
260 | Log.d(Tags.TAG_IDENT_DEBUG, "Binding auth service... ")
261 |
262 | sdkConnection = object : ServiceConnection {
263 | override fun onServiceConnected(className: ComponentName, service: IBinder) {
264 | Log.d(Tags.TAG_IDENT_DEBUG, "Service Connected")
265 |
266 | try {
267 | sdk = IAusweisApp2Sdk.Stub.asInterface(service)
268 | connectSDK()
269 | } catch (e: ClassCastException) {
270 | Log.e(Tags.TAG_IDENT_DEBUG, e.message)
271 | }
272 | // TODO callback
273 | }
274 |
275 | override fun onServiceDisconnected(className: ComponentName) {
276 | // TODO callback
277 | Log.d(Tags.TAG_IDENT_DEBUG, "Service Disconnected")
278 | sdk = null
279 | }
280 | }
281 | val name = "com.governikus.ausweisapp2.START_SERVICE"
282 | val serviceIntent = Intent(name)
283 | serviceIntent.setPackage(context.packageName)
284 | context.bindService(serviceIntent, sdkConnection, Context.BIND_AUTO_CREATE)
285 | }
286 |
287 | private fun connectSDK() {
288 | Log.d(Tags.TAG_IDENT_DEBUG, "Binding SDK...")
289 | try {
290 | if (!sdk!!.connectSdk(sdkCallback)) {
291 |
292 | // TODO error
293 | // already connected? Handle error...
294 | Log.d(Tags.TAG_IDENT_DEBUG, "Connection Issue")
295 | }
296 | } catch (e: RemoteException) {
297 | // handle exception
298 | Log.e(Tags.TAG_IDENT_DEBUG, e.toString())
299 | // TODO error
300 | }
301 | callback?.onInitilized();
302 | Log.d(Tags.TAG_IDENT_DEBUG, "Bound sdk")
303 | }
304 |
305 | fun dispatchNfcTag(tag: Tag) {
306 | try {
307 | sdk?.updateNfcTag(sdkCallback.mSessionID, tag)
308 | } catch (e: Exception) {
309 | // TODO error
310 | Log.d(Tags.TAG_IDENT_DEBUG, "An error occured updating/dispating a NFC Tag")
311 | }
312 | }
313 | }
314 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | EUROPEAN UNION PUBLIC LICENCE v. 1.2
2 | EUPL © the European Union 2007, 2016
3 |
4 | This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined below) which is provided under the terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such use is covered by a right of the copyright holder of the Work).
5 |
6 | The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following notice immediately following the copyright notice for the Work:
7 |
8 | Licensed under the EUPL
9 |
10 | or has expressed by any other means his willingness to license under the EUPL.
11 |
12 | 1. Definitions
13 | In this Licence, the following terms have the following meaning:
14 |
15 | — ‘The Licence’: this Licence.
16 |
17 | — ‘The Original Work’: the work or software distributed or communicated by the Licensor under this Licence, available as Source Code and also as Executable Code as the case may be.
18 |
19 | — ‘Derivative Works’: the works or software that could be created by the Licensee, based upon the Original Work or modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in the country mentioned in Article 15.
20 |
21 | — ‘The Work’: the Original Work or its Derivative Works.
22 |
23 | — ‘The Source Code’: the human-readable form of the Work which is the most convenient for people to study and modify.
24 |
25 | — ‘The Executable Code’: any code which has generally been compiled and which is meant to be interpreted by a computer as a program.
26 |
27 | — ‘The Licensor’: the natural or legal person that distributes or communicates the Work under the Licence.
28 |
29 | — ‘Contributor(s)’: any natural or legal person who modifies the Work under the Licence, or otherwise contributes to the creation of a Derivative Work.
30 |
31 | — ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of the Work under the terms of the Licence.
32 |
33 | — ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential functionalities at the disposal of any other natural or legal person.
34 |
35 | 2. Scope of the rights granted by the Licence
36 | The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for the duration of copyright vested in the Original Work:
37 |
38 | — use the Work in any circumstance and for all usage,
39 |
40 | — reproduce the Work,
41 |
42 | — modify the Work, and make Derivative Works based upon the Work,
43 |
44 | — communicate to the public, including the right to make available or display the Work or copies thereof to the public and perform publicly, as the case may be, the Work,
45 |
46 | — distribute the Work or copies thereof,
47 |
48 | — lend and rent the Work or copies thereof,
49 |
50 | — sublicense rights in the Work or copies thereof.
51 |
52 | Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the applicable law permits so.
53 |
54 | In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed by law in order to make effective the licence of the economic rights here above listed.
55 |
56 | The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the extent necessary to make use of the rights granted on the Work under this Licence.
57 |
58 | 3. Communication of the Source Code
59 | The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to distribute or communicate the Work.
60 |
61 | 4. Limitations on copyright
62 | Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations thereto.
63 |
64 | 5. Obligations of the Licensee
65 | The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those obligations are the following:
66 |
67 | Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work to carry prominent notices stating that the Work has been modified and the date of modification.
68 | Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless the Original Work is expressly distributed only under this version of the Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the Work or Derivative Work that alter or restrict the terms of the Licence.
69 | Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done under the terms of this Compatible Licence. For the sake of this clause, ‘Compatible Licence’ refers to the licences listed in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail.
70 | Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available for as long as the Licensee continues to distribute or communicate the Work.
71 | Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the copyright notice.
72 | 6. Chain of Authorship
73 | The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence.
74 |
75 | Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence.
76 |
77 | Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contri butions to the Work, under the terms of this Licence.
78 |
79 | 7. Disclaimer of Warranty
80 | The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work and may therefore contain defects or ‘bugs’ inherent to this type of development.
81 |
82 | For the above reason, the Work is provided under the Licence on an ‘as is’ basis and without warranties of any kind concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this Licence.
83 |
84 | This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work.
85 |
86 | 8. Disclaimer of Liability
87 | Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, the Licensor will be liable under statutory product liability laws as far such laws apply to the Work.
88 |
89 | 9. Additional agreements
90 | While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by the fact You have accepted any warranty or additional liability.
91 |
92 | 10. Acceptance of the Licence
93 | The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ placed under the bottom of a window displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms and conditions.
94 |
95 | Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution or Communication by You of the Work or copies thereof.
96 |
97 | 11. Information to the public
98 | In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, by offering to download the Work from a remote location) the distribution channel or media (for example, a website) must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence and the way it may be accessible, concluded, stored and reproduced by the Licensee.
99 |
100 | 12. Termination of the Licence
101 | The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms of the Licence.
102 |
103 | Such a termination will not terminate the licences of any person who has received the Work from the Licensee under the Licence, provided such persons remain in full compliance with the Licence.
104 |
105 | 13. Miscellaneous
106 | Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the Work.
107 |
108 | If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid and enforceable.
109 |
110 | The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. New versions of the Licence will be published with a unique version number.
111 |
112 | All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take advantage of the linguistic version of their choice.
113 |
114 | 14. Jurisdiction
115 | Without prejudice to specific agreement between parties,
116 |
117 | — any litigation resulting from the interpretation of this License, arising between the European Union institutions, bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union,
118 |
119 | — any litigation arising between other parties and resulting from the interpretation of this License, will be subject to the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business.
120 |
121 | 15. Applicable Law
122 | Without prejudice to specific agreement between parties,
123 |
124 | — this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, resides or has his registered office,
125 |
126 | — this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside a European Union Member State.
127 |
128 | Appendix
129 | ‘Compatible Licences’ according to Article 5 EUPL are:
130 |
131 | — GNU General Public License (GPL) v. 2, v. 3
132 |
133 | — GNU Affero General Public License (AGPL) v. 3
134 |
135 | — Open Software License (OSL) v. 2.1, v. 3.0
136 |
137 | — Eclipse Public License (EPL) v. 1.0
138 |
139 | — CeCILL v. 2.0, v. 2.1
140 |
141 | — Mozilla Public Licence (MPL) v. 2
142 |
143 | — GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
144 |
145 | — Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software
146 |
147 | — European Union Public Licence (EUPL) v. 1.1, v. 1.2
148 |
149 | — Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+)
150 |
151 | The European Commission may update this Appendix to later versions of the above licences without producing a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the covered Source Code from exclusive appropriation.
152 |
153 | All other changes or additions to this Appendix require the production of a new EUPL version.
154 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 | The twigbit Ident SDK is a lightweight convenience layer on top of the [AusweisApp2 SDK](https://www.ausweisapp.bund.de/fuer-diensteanbieter/software-development-kit-sdk/) written in Kotlin.
4 | We are aiming to extract and eliminate the recurring code and configuration that every developer faces integrating eid services functionality in their apps.
5 | Moreover, we are providing optional convenience tooling for the [AusweisIdent mobile identification service](https://www.ausweisident.de/) provided by Bundesdruckerei GmbH and Governikus KG.
6 |
7 | 
8 |
9 | ## Features
10 |
11 | - Simplify the tedious [AusweisApp2 SDK](https://www.ausweisapp.bund.de/sdk/) configuration
12 | - Replace the JSON based messaging system by convenient wrapper methods, giving developers to must-have convenience such as code completion
13 | - Lightweight — besides the [AusweisApp2 SDK](https://www.ausweisapp.bund.de/sdk/), the only other dependency is [Google GSON](https://github.com/google/gson) for JSON parsing
14 | - Drop-in UI — Provide a simple, customizable drop in UI as a quick integration with identification processes
15 |
16 | **Looking for some backend examples?** See our AusweisIDent backend examples repository at [twigbit/ausweisident-backend-examples](https://github.com/twigbit/ausweisident-backend-examples).
17 |
18 | # Documentation
19 |
20 |
21 | All code is provided in Kotlin. The integration works in Java analogously, all examples are interchangable.
22 |
23 | ## Adding the dependency
24 |
25 | First, add the jitpack maven repository to the **root** `build.gradle` file.
26 |
27 | ```gradle
28 | allprojects {
29 | repositories {
30 | ...
31 | maven { url 'https://jitpack.io' }
32 | }
33 | }
34 | ```
35 |
36 | Then, add the drop in ui as a dependency to your **projects** `build.gradle` file.
37 | Also make sure, that your projects `minSdkVersion` is set to at least version 21.
38 |
39 | ```gradle
40 | android {
41 | defaultConfig {
42 | minSdkVersion 21
43 | }
44 | }
45 | dependencies {
46 | implementation 'com.github.twigbit:ident-sdk:0.1.6'
47 | }
48 | ```
49 |
50 | ## Quick start
51 | Using the drop in UI, you can implement a fully functional AusweisIDent identification cycle within minutes.
52 |
53 | To start the identification process, use the `AusweisIdentBuilder` to create the TCTokenURL with your credentials and the permitted/required scopes and pass it to the `DropInRequest`.
54 | Optionally, you can give the process an identifier to match the result using the `.state(...)` argument.
55 |
56 | ```kotlin
57 | val REQUEST_CODE_IDENTIFICATION = 0;
58 | private fun startDropInIdentification(){
59 | val tcTokenUrl = AusweisIdentBuilder()
60 | .ref()
61 | .clientId("your-client-id")
62 | .redirectUrl("your-redirect-url")
63 | .state("your-persistant-id")
64 | .scope(AusweisIdentScopes.FAMILY_NAMES)
65 | .scope(AusweisIdentScopes.GIVEN_NAMES)
66 | .scope(AusweisIdentScopes.DATE_OF_BIRTH)
67 | .build()
68 |
69 | val dropInRequest = DropInRequest(tcTokenUrl)
70 | startActivityForResult(dropInRequest.getIntent(this), REQUEST_CODE_IDENTIFICATION)
71 | }
72 | ```
73 |
74 | To get the resulting delivery URL, listen to the activity result.
75 |
76 | ```kotlin
77 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
78 | if (requestCode == REQUEST_CODE_IDENTIFICATION) {
79 | if (resultCode == Activity.RESULT_OK) {
80 | // Success. Update the UI to reflect the successful identification
81 | // and fetch the user data from the server where they were delivered.
82 |
83 | val resultUrl = data!!.getStringExtra(IdentificationManager.EXTRA_DROPIN_RESULT)
84 | Log.d(Tags.TAG_IDENT_DEBUG, resultUrl);
85 | // to deliver the data to the server, call this URL and follow the redirect chain
86 |
87 | } else if (resultCode == Activity.RESULT_CANCELED) {
88 | // The user canceled the identification
89 | } else {
90 | // An error occured during the identification
91 | }
92 | }
93 | }
94 | ```
95 |
96 | You will get the `resultUrl` as described above. To evalutate the resultUrl and deliver the result to you backend, read the section on [Handling the result URL](#handling-the-result-url)
97 |
98 |
99 | ### Styling the Drop-In UI
100 | To change the appearance of the drop in UI, please read the [styleguide](STYLEGUIDE.md).
101 |
102 | ## Implement your own UI
103 |
104 | To implement your own identification UI, you can use a custom activity and react to the IdentificationManagers callbacks.
105 |
106 | ### Implementing the callback
107 |
108 | First, create the `IdentificationManager.Callback` in your activity to listen and react to identification events.
109 |
110 | **Warning:** Please note that these events will be called from a service thread, so please make sure you run all UI related logic on the UI thread explicitly with `runOnUiThread`.
111 |
112 | ```kotlin
113 |
114 | val identificationCallback = object: IdentificationManager.Callback{
115 | override fun onCompleted(resultUrl: String) {
116 | // The identification was complete, display a success message to the user and fetch the identification result from the server using the resultUrl
117 | TODO("not implemented")
118 | }
119 |
120 | override fun onRequestAccessRights(accessRights: List) {
121 | // A list of the fields that the sdk is trying to access has arrived. Display them to the user and await his confirmation.
122 | TODO("not implemented")
123 | }
124 |
125 | override fun onRequestCertificate(certificateInfo: CertificateInfo, certificateValidity: CertificateValidity) {
126 | // The certificate info has beed requested and is delivered here
127 | TODO("not implemented")
128 | }
129 |
130 | override fun onCardRecognized(card: Card?) {
131 | // A card was attached to the NFC reader
132 | TODO("not implemented")
133 | }
134 |
135 | override fun onRequestPin() {
136 | // The id cards PIN was requested. Display a PIN dialog to the user.
137 | // To continue the identification process, call identificationManager.setPin(pin: String)
138 | TODO("not implemented")
139 | }
140 |
141 | override fun onRequestPuk() {
142 | // The id cards PUK was requested. Display a PUK diaphlog to the user.
143 | // To continue the identification process, call identificationManager.setPuk(puk: String)
144 | TODO("not implemented")
145 | }
146 |
147 | override fun onRequestCan() {
148 | // The id cards CAN was requested. Display a CAN dialog to the user.
149 | // To continue the identification process, call identificationManager.setCan(can: String)
150 | TODO("not implemented")
151 | }
152 |
153 | override fun onError(error: String) {
154 | // An error occured. Display an error/issue dialog to the user.
155 | TODO("not implemented")
156 | }
157 |
158 | }
159 | ```
160 |
161 | ### Binding the IdentificationManager to your activities lifecycle
162 |
163 | Then, initialize an `IdentificationFragment` in your activites `onCreate` method to bind to the activity livecycle.
164 | For concenience, we make it available within the activity with a getter.
165 |
166 | To access the `identificationManager` to communicate with the SDK, implement the `IdentificationManagerProvider` interface.
167 |
168 | ```kotlin
169 | class IndependentIdentificationActivity : AppCompatActivity(), IdentificationManagerProvider {
170 |
171 | var identificationFragment: IdentificationFragment? = null
172 | // convenience getter
173 | override val identificationManager: IdentificationManager?
174 | get() {
175 | return identificationFragment?.identificationManager
176 | }
177 |
178 | override fun onCreate(savedInstanceState: Bundle?) {
179 | // ...
180 |
181 | identificationFragment = IdentificationFragment.newInstance(this)
182 | identificationFragment!!.identificationManager.addCallback(identificationCallback)
183 |
184 | }
185 | }
186 | ```
187 |
188 |
189 | ### Dispatching NFC Tags to the manager
190 |
191 | In order to read the id cards data from the NFC chip, you need to dispatch NFC tags from your activity to the `IdentificationManager`.
192 |
193 | Add this code to your activity to receive and pass the NFC intents:
194 |
195 | ```kotlin
196 | /*
197 | To receive and dispatch NFC tags to the SDK, we need to initalize a forground dispatcher and attach it to the lifecycle.
198 | Then, we can pass Tag Intents from the `onNewIntent` to the `Identificationmanager`
199 | */
200 |
201 | var mDispatcher: ForegroundDispatcher? = null;
202 |
203 |
204 | override fun onCreate(savedInstanceState: Bundle?) {
205 |
206 | // ...
207 |
208 | // Initialize the NFC Tag foreground dispatcher
209 | mDispatcher = ForegroundDispatcher(this)
210 | }
211 | public override fun onResume() {
212 | super.onResume()
213 | mDispatcher!!.enable()
214 | }
215 |
216 | public override fun onPause() {
217 | super.onPause()
218 | mDispatcher!!.disable()
219 | }
220 |
221 | override fun onNewIntent(intent: Intent?) {
222 | // dispatch NFC tag intents to the manager
223 | super.onNewIntent(intent)
224 | val tag = intent!!.getParcelableExtra(NfcAdapter.EXTRA_TAG)
225 | if (tag != null) {
226 | identificationManager?.dispatchNfcTag(tag)
227 | }
228 | }
229 | ```
230 |
231 | To start the identification process call the `identificationManagers.startIdent` method with your `tcTokenUrl`.
232 |
233 |
234 | ```kotlin
235 | val tcTokenUrl = AusweisIdentBuilder()
236 | .ref()
237 | .clientId("your-client-id")
238 | .redirectUrl("your-redirect-url")
239 | .state("your-persistant-id")
240 | .scope(AusweisIdentScopes.FAMILY_NAMES)
241 | .scope(AusweisIdentScopes.GIVEN_NAMES)
242 | .scope(AusweisIdentScopes.DATE_OF_BIRTH)
243 | .build()
244 | identificationFragment?.identificationManager?.startIdent(tcTokenUrl);
245 | ```
246 |
247 | For a fully working example, see [IndependentIdentificationActivity](../example/src/main/java/com/twigbit/identsdk/example/IndependentIdentificationActivity.kt).
248 |
249 | ### Displaying certificate information
250 |
251 | To display information about the service providers certificate, you can call the method `identificationManager.getCertificate()` to request the certificate information.
252 | The certificate info will be delivered asynchronously in the callback via the `onRequestCertificate(certificateInfo: CertificateInfo, certificateValidity: CertificateValidity)` method.
253 | The certificate informations are mirrored from the AusweisApp2 SDK messanges and are wrapped by the classes [CertificateInfo](../identsdk/src/main/java/com/twigbit/identsdk/core/IdentificationUtil.kt) and [CertificateValidity](../identsdk/src/main/java/com/twigbit/identsdk/core/IdentificationUtil.kt)
254 |
255 | ## Handling the result URL
256 |
257 | ### Opening the result in the browser
258 |
259 | To finish the data transmission, simply open the `resultUrl` in the browser. This will redirect you to the final redirect page provided by the server.
260 |
261 | ```kotlin
262 | val resultUrl = data!!.getStringExtra(IdentificationManager.EXTRA_DROPIN_RESULT)
263 | val i = Intent(Intent.ACTION_VIEW)
264 | i.data = Uri.parse(resultUrl)
265 | startActivity(i)
266 | ```
267 |
268 |
269 | ### Retreiving the data locally with the AusweisIDent server side sample
270 |
271 | If you are using a setup similar to our [serverside reference implementation](https://github.com/twigbit/ausweisident-backend-examples), you cant alternatively directly evaluate the redirect chain locally and retreive the data from the redirect response for showcasing purposes. For most use cases, it is probably not desired to return the data directly to the device.
272 |
273 | Calling this url will result in several redirects with the last redirect pointing to your redirectUrl with the `code` query parameter after a successful identification or an `error` and `error_description` parameter in case of an error. Your server needs this `code` to receive the user info.
274 |
275 | ```
276 | https://localhost:10443/demo/login/authcode?code=S6GKv5dJNwy6SXlRrllay6fcaoWeUWjA6ar5gahrGSI823sFa4&state=123456
277 | ```
278 |
279 | > _**Warning:** If you decide to call the url on your own (and not pass it to a browser) you need to make sure to store and send cookies between the redirects._
280 |
281 | > **AusweisIdent Backend Examples:** Our [twigbit/ausweisident-backend-examples](https://github.com/twigbit/ausweisident-backend-examples) repository contains example implementations (currently for NodeJS only, Go and other languages coming soon) to receive the user info on the server side.
282 |
283 | If you are using the same server side architecture as the example, you can use the `AusweisIdentResultHanlder` to take care of handline the result for you.
284 |
285 | ```kotlin
286 | val resultHandler: AusweisIdentResultHandler =
287 | AusweisIdentResultHandler(object : AusweisIdentResultHandler.Callback {
288 | override fun onError(message: String) {
289 | Log.d(Tags.TAG_IDENT_DEBUG, "An error occured")
290 | }
291 |
292 | override fun onComplete(userInfo: UserInfo) {
293 | Log.d(Tags.TAG_IDENT_DEBUG, userInfo.toString())
294 | }
295 | })
296 |
297 | resultHandler.fetchResult(resultUrl);
298 |
299 | ```
300 |
301 |
302 | ## Server side implementation
303 |
304 | Please see the AusweisIdent documentation for further details or check out our [server example](https://github.com/twigbit/ausweisident-backend-examples).
305 |
306 |
307 | ### Example
308 |
309 | A working implementation can be found in the `/example` directory. Please note that you need a test PA to test the identification flow in the reference system.
310 |
311 | ### Limitations
312 |
313 | - The [AusweisApp2 SDK](https://www.ausweisapp.bund.de/sdk/) only supports arm64-v8a architecures since version 15.03. Unfortunalety, we are bound to that limitation.
314 |
315 | # Roadmap
316 |
317 | ## This project is actively under development.
318 |
319 | For informations, contact [post@twigbit.com](mailto:post@twigbit.com) .
320 |
321 | | Status | Version |
322 | | --------- | ---------------- |
323 | | released | 0.1.2 unreleased |
324 |
325 | ## Changelog/Milestones
326 |
327 | ### 0.1.6
328 | * [core] Fix typos
329 | * [core] Improve docs
330 |
331 | ### 0.1.5
332 | * [ausweisident] Server implementation guide
333 | * [core] Improve docs
334 | * [core] Bugfixes
335 |
336 | ### 0.1.4
337 | * [core] Small usability improvements
338 | * [core] Result URL resolution example
339 | * [documentation] Improve docs
340 |
341 | ### 0.1.3
342 | * [core] Usability improvements
343 | * [documentation] Improve docs
344 |
345 | ### 0.1.2
346 | * [dropin] Certificate view
347 | * [core] Test simplified configuration procedure.
348 |
349 | ### 0.1.1
350 | * [ausweisident] Return Result URL directly, refactor call redirects into optional method in AusweisIdent helper.
351 | * [ausweisident] Configuration helper
352 | * [core] Refactor state into callbacks.
353 | * [core] Persistant abstractions for the command and message system
354 | * [core] Review inheritance model and draft alternative livecycle-aware architecture that offers more flexibility.
355 | * [core] Explicitly handle result URL.
356 | * [dropin] Dropin UI basic implementation
357 | * [dropin] Dropin styling
358 |
359 |
360 | ### Backlog
361 | * [ausweisident] Provide Util for evaluating the result URL.
362 | * [UI] Add manual for fixing activity orientation
363 | * [core] Add support for optional attribute selection
364 | * [core] Investigate card recognition behavior before intialisation
365 |
366 |
367 | ### Nice to have
368 |
369 | * Vibrate on NFC message.
370 | * Capability check- check whether the users device has the required architecture and NFC capabilities
371 | * Provides a fallback to prompt the user to install the official [AusweisApp2] (https://www.ausweisapp.bund.de/) in case of unsupported architecture (see section `Limitations`)
372 |
373 | ---
374 |
375 | ### Copyright
376 |
377 | ```
378 | (c) Copyright 2018 twigbit technologies GmbH. All rights reserved.
379 | ```
380 |
--------------------------------------------------------------------------------