diff --git a/Fido2/.gitignore b/Fido2/.gitignore deleted file mode 100644 index c94c421..0000000 --- a/Fido2/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.iml -.gradle -local.properties -.idea -.DS_Store -/build -/captures -.externalNativeBuild diff --git a/Fido2/README.md b/Fido2/README.md deleted file mode 100644 index c259eff..0000000 --- a/Fido2/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# FIDO2 Sample -This sample shows how to use the FIDO2 API, however we recommend [migrating from FIDO2 to Credential Manager](https://developer.android.com/training/sign-in/fido2-migration). diff --git a/Fido2/app/.gitignore b/Fido2/app/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/Fido2/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/Fido2/app/build.gradle b/Fido2/app/build.gradle deleted file mode 100644 index 75fd03f..0000000 --- a/Fido2/app/build.gradle +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'dagger.hilt.android.plugin' - -android { - compileSdkVersion 31 - defaultConfig { - applicationId 'com.google.android.gms.identity.sample.fido2' - minSdkVersion 21 - targetSdkVersion 31 - versionCode 1 - versionName '1.0' - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - vectorDrawables.useSupportLibrary = true - buildConfigField 'String', 'API_BASE_URL', "\"https://webauthn-codelab.glitch.me/auth\"" - resValue 'string', 'host', 'https://webauthn-codelab.glitch.me' - resValue 'string', 'asset_statements', """ - [{ - "include": "https://webauthn-codelab.glitch.me/.well-known/assetlinks.json" - }] - """ - } - signingConfigs { - debug { - storeFile file('../debug.jks') - storePassword 'android' - keyAlias 'androiddebugkey' - keyPassword 'android' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } - buildFeatures { - dataBinding true - } - buildTypes { - debug { - signingConfig signingConfigs.debug - } - release { - signingConfig signingConfigs.debug - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - - implementation 'com.google.android.gms:play-services-fido:19.0.0-beta' - - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'androidx.fragment:fragment-ktx:1.3.6' - - implementation 'androidx.core:core-ktx:1.6.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.0' - - implementation "androidx.datastore:datastore-preferences:1.0.0" - - def coroutine_version = '1.5.0' - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutine_version" - - def hilt_version = '2.38.1' - implementation "com.google.dagger:hilt-android:$hilt_version" - kapt "com.google.dagger:hilt-compiler:$hilt_version" - kapt "androidx.hilt:hilt-compiler:1.0.0" - - def lifecycle_version = '2.4.0-alpha03' - implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" - implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" - testImplementation 'androidx.arch.core:core-testing:2.1.0' - - implementation 'com.google.android.material:material:1.4.0' - - def okhttp_version = '4.9.0' - implementation "com.squareup.okhttp3:okhttp:$okhttp_version" - implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version" - implementation "ru.gildor.coroutines:kotlin-coroutines-okhttp:1.0" - - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test:runner:1.4.0' - androidTestImplementation 'androidx.test:rules:1.4.0' - - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.ext:truth:1.4.0' - androidTestImplementation 'com.google.truth:truth:1.1.2' - - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} diff --git a/Fido2/app/proguard-rules.pro b/Fido2/app/proguard-rules.pro deleted file mode 100644 index f1b4245..0000000 --- a/Fido2/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/Fido2/app/src/main/AndroidManifest.xml b/Fido2/app/src/main/AndroidManifest.xml deleted file mode 100644 index ca81adc..0000000 --- a/Fido2/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/Base64Ext.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/Base64Ext.kt deleted file mode 100644 index 87b512a..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/Base64Ext.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2 - -import android.util.Base64 - -private const val BASE64_FLAG = Base64.NO_PADDING or Base64.NO_WRAP or Base64.URL_SAFE - -fun ByteArray.toBase64(): String { - return Base64.encodeToString(this, BASE64_FLAG) -} - -fun String.decodeBase64(): ByteArray { - return Base64.decode(this, BASE64_FLAG) -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/Fido2App.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/Fido2App.kt deleted file mode 100644 index 3cc2b3f..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/Fido2App.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2 - -import android.app.Application -import android.os.Build -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.PreferenceDataStoreFactory -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.preferencesDataStoreFile -import com.google.android.gms.identity.sample.fido2.api.AddHeaderInterceptor -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.HiltAndroidApp -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit -import javax.inject.Singleton - -@HiltAndroidApp -class Fido2App : Application() - -@Module -@InstallIn(SingletonComponent::class) -object AppModule { - - @Singleton - @Provides - fun provideOkHttpClient() : OkHttpClient { - val userAgent = "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} " + - "(Android ${Build.VERSION.RELEASE}; ${Build.MODEL}; ${Build.BRAND})" - return OkHttpClient.Builder() - .addInterceptor(AddHeaderInterceptor(userAgent)) - .readTimeout(30, TimeUnit.SECONDS) - .writeTimeout(40, TimeUnit.SECONDS) - .connectTimeout(40, TimeUnit.SECONDS) - .build() - } - - @Singleton - @Provides - fun provideAppCoroutineScope() : CoroutineScope = CoroutineScope(SupervisorJob()) - - @Singleton - @Provides - fun provideDataStore(application: Application): DataStore { - return PreferenceDataStoreFactory.create { - application.preferencesDataStoreFile("auth") - } - } -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/AddHeaderInterceptor.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/AddHeaderInterceptor.kt deleted file mode 100644 index cef5d5e..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/AddHeaderInterceptor.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.api - -import okhttp3.Interceptor -import okhttp3.Response - -internal class AddHeaderInterceptor(private val userAgent: String) : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - return chain.proceed( - chain.request().newBuilder() - .header("User-Agent", userAgent) - .header("X-Requested-With", "XMLHttpRequest") - .build() - ) - } -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/ApiException.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/ApiException.kt deleted file mode 100644 index fc912ab..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/ApiException.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.api - -class ApiException(message: String) : RuntimeException(message) diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/ApiResult.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/ApiResult.kt deleted file mode 100644 index bff318f..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/ApiResult.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.api - -/** - * Represents the result of an API call. - */ -sealed class ApiResult { - - /** - * API returned successfully with data. - */ - class Success( - /** - * The session ID to be used for the subsequent API calls. - * Might be null if the API call does not return a new cookie. - */ - val sessionId: String?, - - /** - * The result data. - */ - val data: T - ) : ApiResult() - - /** - * API returned unsuccessfully with code 401, and the user should be signed out. - */ - object SignedOutFromServer : ApiResult() -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/AuthApi.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/AuthApi.kt deleted file mode 100644 index 94588cc..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/AuthApi.kt +++ /dev/null @@ -1,545 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.api - -import android.util.JsonReader -import android.util.JsonToken -import android.util.JsonWriter -import android.util.Log -import com.google.android.gms.fido.fido2.api.common.Attachment -import com.google.android.gms.fido.fido2.api.common.AuthenticatorAssertionResponse -import com.google.android.gms.fido.fido2.api.common.AuthenticatorAttestationResponse -import com.google.android.gms.fido.fido2.api.common.AuthenticatorSelectionCriteria -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialDescriptor -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialParameters -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialRequestOptions -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialRpEntity -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialType -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialUserEntity -import com.google.android.gms.identity.sample.fido2.BuildConfig -import com.google.android.gms.identity.sample.fido2.decodeBase64 -import com.google.android.gms.identity.sample.fido2.toBase64 -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response -import okhttp3.ResponseBody -import ru.gildor.coroutines.okhttp.await -import java.io.StringReader -import java.io.StringWriter -import javax.inject.Inject - -/** - * Interacts with the server API. - */ -class AuthApi @Inject constructor( - private val client: OkHttpClient -) { - - companion object { - private const val BASE_URL = BuildConfig.API_BASE_URL - private val JSON = "application/json".toMediaTypeOrNull() - private const val SessionIdKey = "connect.sid=" - private const val TAG = "AuthApi" - } - - /** - * @param username The username to be used for sign-in. - * @return The Session ID. - */ - suspend fun username(username: String): ApiResult { - val call = client.newCall( - Request.Builder() - .url("$BASE_URL/username") - .method("POST", jsonRequestBody { - name("username").value(username) - }) - .build() - ) - val response = call.await() - return response.result("Error calling /username") { } - } - - /** - * @param sessionId The session ID received on `username()`. - * @param password A password. - * @return An [ApiResult]. - */ - suspend fun password(sessionId: String, password: String): ApiResult { - val call = client.newCall( - Request.Builder() - .url("$BASE_URL/password") - .addHeader("Cookie", formatCookie(sessionId)) - .method("POST", jsonRequestBody { - name("password").value(password) - }) - .build() - ) - val response = call.await() - return response.result("Error calling /password") { } - } - - /** - * @param sessionId The session ID. - * @return A list of all the credentials registered on the server. - */ - suspend fun getKeys(sessionId: String): ApiResult> { - val call = client.newCall( - Request.Builder() - .url("$BASE_URL/getKeys") - .addHeader("Cookie", formatCookie(sessionId)) - .method("POST", jsonRequestBody {}) - .build() - ) - val response = call.await() - return response.result("Error calling /getKeys") { - parseUserCredentials(body ?: throw ApiException("Empty response from /getKeys")) - } - } - - /** - * @param sessionId The session ID. - * @return A pair. The `first` element is an [PublicKeyCredentialCreationOptions] that can be - * used for a subsequent FIDO2 API call. The `second` element is a challenge string that should - * be sent back to the server in [registerResponse]. - */ - suspend fun registerRequest(sessionId: String): ApiResult { - val call = client.newCall( - Request.Builder() - .url("$BASE_URL/registerRequest") - .addHeader("Cookie", formatCookie(sessionId)) - .method("POST", jsonRequestBody { - name("attestation").value("none") - name("authenticatorSelection").objectValue { - name("authenticatorAttachment").value("platform") - name("userVerification").value("required") - } - }) - .build() - ) - val response = call.await() - return response.result("Error calling /registerRequest") { - parsePublicKeyCredentialCreationOptions( - body ?: throw ApiException("Empty response from /registerRequest") - ) - } - } - - /** - * @param sessionId The session ID to be used for the sign-in. - * @param credential The PublicKeyCredential object. - * @return A list of all the credentials registered on the server, including the newly - * registered one. - */ - suspend fun registerResponse( - sessionId: String, - credential: PublicKeyCredential - ): ApiResult> { - val rawId = credential.rawId.toBase64() - val response = credential.response as AuthenticatorAttestationResponse - - val call = client.newCall( - Request.Builder() - .url("$BASE_URL/registerResponse") - .addHeader("Cookie", formatCookie(sessionId)) - .method("POST", jsonRequestBody { - name("id").value(rawId) - name("type").value(PublicKeyCredentialType.PUBLIC_KEY.toString()) - name("rawId").value(rawId) - name("response").objectValue { - name("clientDataJSON").value( - response.clientDataJSON.toBase64() - ) - name("attestationObject").value( - response.attestationObject.toBase64() - ) - } - }) - .build() - ) - val apiResponse = call.await() - return apiResponse.result("Error calling /registerResponse") { - parseUserCredentials( - body ?: throw ApiException("Empty response from /registerResponse") - ) - } - } - - /** - * @param sessionId The session ID. - * @param credentialId The credential ID to be removed. - */ - suspend fun removeKey(sessionId: String, credentialId: String): ApiResult { - val call = client.newCall( - Request.Builder() - .url("$BASE_URL/removeKey?credId=$credentialId") - .addHeader("Cookie", formatCookie(sessionId)) - .method("POST", jsonRequestBody {}) - .build() - ) - val response = call.await() - return response.result("Error calling /removeKey") { } - } - - /** - * @param sessionId The session ID to be used for the sign-in. - * @param credentialId The credential ID of this device. - * @return A pair. The `first` element is a [PublicKeyCredentialRequestOptions] that can be used - * for a subsequent FIDO2 API call. The `second` element is a challenge string that should - * be sent back to the server in [signinResponse]. - */ - suspend fun signinRequest( - sessionId: String, - credentialId: String? - ): ApiResult { - val call = client.newCall( - Request.Builder() - .url( - buildString { - append("$BASE_URL/signinRequest") - if (credentialId != null) { - append("?credId=$credentialId") - } - } - ) - .addHeader("Cookie", formatCookie(sessionId)) - .method("POST", jsonRequestBody {}) - .build() - ) - val response = call.await() - return response.result("Error calling /signinRequest") { - parsePublicKeyCredentialRequestOptions( - body ?: throw ApiException("Empty response from /signinRequest") - ) - } - } - - /** - * @param sessionId The session ID to be used for the sign-in. - * @param credential The PublicKeyCredential object. - * @return A list of all the credentials registered on the server, including the newly - * registered one. - */ - suspend fun signinResponse( - sessionId: String, - credential: PublicKeyCredential - ): ApiResult> { - val rawId = credential.rawId.toBase64() - val response = credential.response as AuthenticatorAssertionResponse - - val call = client.newCall( - Request.Builder() - .url("$BASE_URL/signinResponse") - .addHeader("Cookie", formatCookie(sessionId)) - .method("POST", jsonRequestBody { - name("id").value(rawId) - name("type").value(PublicKeyCredentialType.PUBLIC_KEY.toString()) - name("rawId").value(rawId) - name("response").objectValue { - name("clientDataJSON").value( - response.clientDataJSON.toBase64() - ) - name("authenticatorData").value( - response.authenticatorData.toBase64() - ) - name("signature").value( - response.signature.toBase64() - ) - name("userHandle").value( - response.userHandle?.toBase64() ?: "" - ) - } - }) - .build() - ) - val apiResponse = call.await() - return apiResponse.result("Error calling /signingResponse") { - parseUserCredentials(body ?: throw ApiException("Empty response from /signinResponse")) - } - } - - private fun parsePublicKeyCredentialRequestOptions( - body: ResponseBody - ): PublicKeyCredentialRequestOptions { - val builder = PublicKeyCredentialRequestOptions.Builder() - JsonReader(body.byteStream().bufferedReader()).use { reader -> - reader.beginObject() - while (reader.hasNext()) { - when (reader.nextName()) { - "challenge" -> builder.setChallenge(reader.nextString().decodeBase64()) - "userVerification" -> reader.skipValue() - "allowCredentials" -> builder.setAllowList(parseCredentialDescriptors(reader)) - "rpId" -> builder.setRpId(reader.nextString()) - "timeout" -> builder.setTimeoutSeconds(reader.nextDouble()) - else -> reader.skipValue() - } - } - reader.endObject() - } - return builder.build() - } - - private fun parsePublicKeyCredentialCreationOptions( - body: ResponseBody - ): PublicKeyCredentialCreationOptions { - val builder = PublicKeyCredentialCreationOptions.Builder() - JsonReader(body.byteStream().bufferedReader()).use { reader -> - reader.beginObject() - while (reader.hasNext()) { - when (reader.nextName()) { - "user" -> builder.setUser(parseUser(reader)) - "challenge" -> builder.setChallenge(reader.nextString().decodeBase64()) - "pubKeyCredParams" -> builder.setParameters(parseParameters(reader)) - "timeout" -> builder.setTimeoutSeconds(reader.nextDouble()) - "attestation" -> reader.skipValue() // Unused - "excludeCredentials" -> builder.setExcludeList( - parseCredentialDescriptors(reader) - ) - "authenticatorSelection" -> builder.setAuthenticatorSelection( - parseSelection(reader) - ) - "rp" -> builder.setRp(parseRp(reader)) - } - } - reader.endObject() - } - return builder.build() - } - - private fun parseRp(reader: JsonReader): PublicKeyCredentialRpEntity { - var id: String? = null - var name: String? = null - reader.beginObject() - while (reader.hasNext()) { - when (reader.nextName()) { - "id" -> id = reader.nextString() - "name" -> name = reader.nextString() - else -> reader.skipValue() - } - } - reader.endObject() - return PublicKeyCredentialRpEntity(id!!, name!!, /* icon */ null) - } - - private fun parseSelection(reader: JsonReader): AuthenticatorSelectionCriteria { - val builder = AuthenticatorSelectionCriteria.Builder() - reader.beginObject() - while (reader.hasNext()) { - when (reader.nextName()) { - "authenticatorAttachment" -> builder.setAttachment( - Attachment.fromString(reader.nextString()) - ) - "userVerification" -> reader.skipValue() - else -> reader.skipValue() - } - } - reader.endObject() - return builder.build() - } - - private fun parseCredentialDescriptors( - reader: JsonReader - ): List { - val list = mutableListOf() - reader.beginArray() - while (reader.hasNext()) { - var id: String? = null - reader.beginObject() - while (reader.hasNext()) { - when (reader.nextName()) { - "id" -> id = reader.nextString() - "type" -> reader.skipValue() - "transports" -> reader.skipValue() - else -> reader.skipValue() - } - } - reader.endObject() - list.add( - PublicKeyCredentialDescriptor( - PublicKeyCredentialType.PUBLIC_KEY.toString(), - id!!.decodeBase64(), - /* transports */ null - ) - ) - } - reader.endArray() - return list - } - - private fun parseUser(reader: JsonReader): PublicKeyCredentialUserEntity { - reader.beginObject() - var id: String? = null - var name: String? = null - var displayName = "" - while (reader.hasNext()) { - when (reader.nextName()) { - "id" -> id = reader.nextString() - "name" -> name = reader.nextString() - "displayName" -> displayName = reader.nextString() - else -> reader.skipValue() - } - } - reader.endObject() - return PublicKeyCredentialUserEntity( - id!!.decodeBase64(), - name!!, - null, // icon - displayName - ) - } - - private fun parseParameters(reader: JsonReader): List { - val parameters = mutableListOf() - reader.beginArray() - while (reader.hasNext()) { - reader.beginObject() - var type: String? = null - var alg = 0 - while (reader.hasNext()) { - when (reader.nextName()) { - "type" -> type = reader.nextString() - "alg" -> alg = reader.nextInt() - else -> reader.skipValue() - } - } - reader.endObject() - parameters.add(PublicKeyCredentialParameters(type!!, alg)) - } - reader.endArray() - return parameters - } - - private fun jsonRequestBody(body: JsonWriter.() -> Unit): RequestBody { - val output = StringWriter() - JsonWriter(output).use { writer -> - writer.beginObject() - writer.body() - writer.endObject() - } - return output.toString().toRequestBody(JSON) - } - - private fun parseUserCredentials(body: ResponseBody): List { - fun readCredentials(reader: JsonReader): List { - val credentials = mutableListOf() - reader.beginArray() - while (reader.hasNext()) { - reader.beginObject() - var id: String? = null - var publicKey: String? = null - while (reader.hasNext()) { - when (reader.nextName()) { - "credId" -> id = reader.nextString() - "publicKey" -> publicKey = reader.nextString() - else -> reader.skipValue() - } - } - reader.endObject() - if (id != null && publicKey != null) { - credentials.add(Credential(id, publicKey)) - } - } - reader.endArray() - return credentials - } - JsonReader(body.byteStream().bufferedReader()).use { reader -> - reader.beginObject() - while (reader.hasNext()) { - val name = reader.nextName() - if (name == "credentials") { - return readCredentials(reader) - } else { - reader.skipValue() - } - } - reader.endObject() - } - throw ApiException("Cannot parse credentials") - } - - private fun throwResponseError(response: Response, message: String): Nothing { - val b = response.body - if (b != null) { - throw ApiException("$message; ${parseError(b)}") - } else { - throw ApiException(message) - } - } - - private fun parseError(body: ResponseBody): String { - val errorString = body.string() - try { - JsonReader(StringReader(errorString)).use { reader -> - reader.beginObject() - while (reader.hasNext()) { - val name = reader.nextName() - if (name == "error") { - val token = reader.peek() - if (token == JsonToken.STRING) { - return reader.nextString() - } - return "Unknown" - } else { - reader.skipValue() - } - } - reader.endObject() - } - } catch (e: Exception) { - Log.e(TAG, "Cannot parse the error: $errorString", e) - // Don't throw; this method is called during throwing. - } - return "" - } - - private fun JsonWriter.objectValue(body: JsonWriter.() -> Unit) { - beginObject() - body() - endObject() - } - - private fun Response.result(errorMessage: String, data: Response.() -> T): ApiResult { - if (!isSuccessful) { - if (code == 401) { // Unauthorized - return ApiResult.SignedOutFromServer - } - // All other errors throw an exception. - throwResponseError(this, errorMessage) - } - val cookie = headers("set-cookie").find { it.startsWith(SessionIdKey) } - val sessionId = if (cookie != null) parseSessionId(cookie) else null - return ApiResult.Success(sessionId, data()) - } - - private fun parseSessionId(cookie: String): String { - val start = cookie.indexOf(SessionIdKey) - if (start < 0) { - throw ApiException("Cannot find $SessionIdKey") - } - val semicolon = cookie.indexOf(";", start + SessionIdKey.length) - val end = if (semicolon < 0) cookie.length else semicolon - return cookie.substring(start + SessionIdKey.length, end) - } - - private fun formatCookie(sessionId: String): String { - return "$SessionIdKey$sessionId" - } -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/Credential.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/Credential.kt deleted file mode 100644 index d2c9a47..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/api/Credential.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.api - -/** - * A credential registered on the server. - */ -data class Credential( - val id: String, - val publicKey: String -) diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/repository/AuthRepository.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/repository/AuthRepository.kt deleted file mode 100644 index 39053cb..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/repository/AuthRepository.kt +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.repository - -import android.app.PendingIntent -import android.util.Log -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.stringPreferencesKey -import androidx.datastore.preferences.core.stringSetPreferencesKey -import com.google.android.gms.identity.sample.fido2.api.ApiException -import com.google.android.gms.identity.sample.fido2.api.ApiResult -import com.google.android.gms.identity.sample.fido2.api.AuthApi -import com.google.android.gms.identity.sample.fido2.api.Credential -import com.google.android.gms.identity.sample.fido2.toBase64 -import com.google.android.gms.fido.fido2.Fido2ApiClient -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch -import kotlinx.coroutines.tasks.await -import javax.inject.Inject -import javax.inject.Singleton - -/** - * Works with the API, the local data store, and FIDO2 API. - */ -@Singleton -class AuthRepository @Inject constructor( - private val api: AuthApi, - private val dataStore: DataStore, - scope: CoroutineScope -) { - - private companion object { - const val TAG = "AuthRepository" - - // Keys for SharedPreferences - val USERNAME = stringPreferencesKey("username") - val SESSION_ID = stringPreferencesKey("session_id") - val CREDENTIALS = stringSetPreferencesKey("credentials") - val LOCAL_CREDENTIAL_ID = stringPreferencesKey("local_credential_id") - - suspend fun DataStore.read(key: Preferences.Key): T? { - return data.map { it[key] }.first() - } - } - - private var fido2ApiClient: Fido2ApiClient? = null - - fun setFido2APiClient(client: Fido2ApiClient?) { - fido2ApiClient = client - } - - private val signInStateMutable = MutableSharedFlow( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST - ) - /** The current [SignInState]. */ - val signInState = signInStateMutable.asSharedFlow() - - /** - * The list of credentials this user has registered on the server. This is only populated when - * the sign-in state is [SignInState.SignedIn]. - */ - val credentials = - dataStore.data.map { it[CREDENTIALS] ?: emptySet() }.map { parseCredentials(it) } - - init { - scope.launch { - val username = dataStore.read(USERNAME) - val sessionId = dataStore.read(SESSION_ID) - val initialState = when { - username.isNullOrBlank() -> SignInState.SignedOut - sessionId.isNullOrBlank() -> SignInState.SigningIn(username) - else -> SignInState.SignedIn(username) - } - signInStateMutable.emit(initialState) - if (initialState is SignInState.SignedIn) { - refreshCredentials() - } - } - } - - /** - * Sends the username to the server. If it succeeds, the sign-in state will proceed to - * [SignInState.SigningIn]. - */ - suspend fun username(username: String) { - when (val result = api.username(username)) { - ApiResult.SignedOutFromServer -> forceSignOut() - is ApiResult.Success -> { - dataStore.edit { prefs -> - prefs[USERNAME] = username - prefs[SESSION_ID] = result.sessionId!! - } - signInStateMutable.emit(SignInState.SigningIn(username)) - } - } - } - - /** - * Signs in with a password. This should be called only when the sign-in state is - * [SignInState.SigningIn]. If it succeeds, the sign-in state will proceed to - * [SignInState.SignedIn]. - */ - suspend fun password(password: String) { - val username = dataStore.read(USERNAME)!! - val sessionId = dataStore.read(SESSION_ID)!! - try { - when (val result = api.password(sessionId, password)) { - ApiResult.SignedOutFromServer -> forceSignOut() - is ApiResult.Success -> { - if (result.sessionId != null) { - dataStore.edit { prefs -> - prefs[SESSION_ID] = result.sessionId - } - } - signInStateMutable.emit(SignInState.SignedIn(username)) - refreshCredentials() - } - } - } catch (e: ApiException) { - Log.e(TAG, "Invalid login credentials", e) - - // start login over again - dataStore.edit { prefs -> - prefs.remove(USERNAME) - prefs.remove(SESSION_ID) - prefs.remove(CREDENTIALS) - } - - signInStateMutable.emit( - SignInState.SignInError(e.message ?: "Invalid login credentials") - ) - } - } - - private suspend fun refreshCredentials() { - val sessionId = dataStore.read(SESSION_ID)!! - when (val result = api.getKeys(sessionId)) { - ApiResult.SignedOutFromServer -> forceSignOut() - is ApiResult.Success -> { - dataStore.edit { prefs -> - result.sessionId?.let { prefs[SESSION_ID] = it } - prefs[CREDENTIALS] = result.data.toStringSet() - } - } - } - } - - private fun List.toStringSet(): Set { - return mapIndexed { index, credential -> - "$index;${credential.id};${credential.publicKey}" - }.toSet() - } - - private fun parseCredentials(set: Set): List { - return set.map { s -> - val (index, id, publicKey) = s.split(";") - index to Credential(id, publicKey) - }.sortedBy { (index, _) -> index } - .map { (_, credential) -> credential } - } - - /** - * Clears the credentials. The sign-in state will proceed to [SignInState.SigningIn]. - */ - suspend fun clearCredentials() { - val username = dataStore.read(USERNAME)!! - dataStore.edit { prefs -> - prefs.remove(CREDENTIALS) - } - signInStateMutable.emit(SignInState.SigningIn(username)) - } - - /** - * Clears all the sign-in information. The sign-in state will proceed to - * [SignInState.SignedOut]. - */ - suspend fun signOut() { - dataStore.edit { prefs -> - prefs.remove(USERNAME) - prefs.remove(SESSION_ID) - prefs.remove(CREDENTIALS) - } - signInStateMutable.emit(SignInState.SignedOut) - } - - private suspend fun forceSignOut() { - dataStore.edit { prefs -> - prefs.remove(USERNAME) - prefs.remove(SESSION_ID) - prefs.remove(CREDENTIALS) - } - signInStateMutable.emit(SignInState.SignInError("Signed out by server")) - } - - /** - * Starts to register a new credential to the server. This should be called only when the - * sign-in state is [SignInState.SignedIn]. - */ - suspend fun registerRequest(): PendingIntent? { - fido2ApiClient?.let { client -> - try { - val sessionId = dataStore.read(SESSION_ID)!! - when (val apiResult = api.registerRequest(sessionId)) { - ApiResult.SignedOutFromServer -> forceSignOut() - is ApiResult.Success -> { - if (apiResult.sessionId != null) { - dataStore.edit { prefs -> - prefs[SESSION_ID] = apiResult.sessionId - } - } - val task = client.getRegisterPendingIntent(apiResult.data) - return task.await() - } - } - } catch (e: Exception) { - Log.e(TAG, "Cannot call registerRequest", e) - } - } - return null - } - - /** - * Finishes registering a new credential to the server. This should only be called after - * a call to [registerRequest] and a local FIDO2 API for public key generation. - */ - suspend fun registerResponse(credential: PublicKeyCredential) { - try { - val sessionId = dataStore.read(SESSION_ID)!! - val credentialId = credential.rawId.toBase64() - when (val result = api.registerResponse(sessionId, credential)) { - ApiResult.SignedOutFromServer -> forceSignOut() - is ApiResult.Success -> { - dataStore.edit { prefs -> - result.sessionId?.let { prefs[SESSION_ID] = it } - prefs[CREDENTIALS] = result.data.toStringSet() - prefs[LOCAL_CREDENTIAL_ID] = credentialId - } - } - } - } catch (e: ApiException) { - Log.e(TAG, "Cannot call registerResponse", e) - } - } - - /** - * Removes a credential registered on the server. - */ - suspend fun removeKey(credentialId: String) { - try { - val sessionId = dataStore.read(SESSION_ID)!! - when (api.removeKey(sessionId, credentialId)) { - ApiResult.SignedOutFromServer -> forceSignOut() - is ApiResult.Success -> refreshCredentials() - } - } catch (e: ApiException) { - Log.e(TAG, "Cannot call removeKey", e) - } - } - - /** - * Starts to sign in with a FIDO2 credential. This should only be called when the sign-in state - * is [SignInState.SigningIn]. - */ - suspend fun signinRequest(): PendingIntent? { - fido2ApiClient?.let { client -> - val sessionId = dataStore.read(SESSION_ID)!! - val credentialId = dataStore.read(LOCAL_CREDENTIAL_ID) - if (credentialId != null) { - when (val apiResult = api.signinRequest(sessionId, credentialId)) { - ApiResult.SignedOutFromServer -> forceSignOut() - is ApiResult.Success -> { - val task = client.getSignPendingIntent(apiResult.data) - return task.await() - } - } - } - } - return null - } - - /** - * Finishes to signing in with a FIDO2 credential. This should only be called after a call to - * [signinRequest] and a local FIDO2 API for key assertion. - */ - suspend fun signinResponse(credential: PublicKeyCredential) { - try { - val username = dataStore.read(USERNAME)!! - val sessionId = dataStore.read(SESSION_ID)!! - val credentialId = credential.rawId.toBase64() - when (val result = api.signinResponse(sessionId, credential)) { - ApiResult.SignedOutFromServer -> forceSignOut() - is ApiResult.Success -> { - dataStore.edit { prefs -> - result.sessionId?.let { prefs[SESSION_ID] = it } - prefs[CREDENTIALS] = result.data.toStringSet() - prefs[LOCAL_CREDENTIAL_ID] = credentialId - } - signInStateMutable.emit(SignInState.SignedIn(username)) - refreshCredentials() - } - } - } catch (e: ApiException) { - Log.e(TAG, "Cannot call registerResponse", e) - } - } -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/repository/SignInState.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/repository/SignInState.kt deleted file mode 100644 index 7124046..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/repository/SignInState.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.repository - -/** - * Represents the sign in/out state of the user. Used to navigate between screens in the app. - */ -sealed class SignInState { - - /** - * The user is signed out. - */ - object SignedOut : SignInState() - - /** - * The user is signing in. The user has entered the username and is ready to sign in with - * password or FIDO2. - */ - data class SigningIn( - val username: String - ) : SignInState() - - /** - * The user sign-in failed. - */ - data class SignInError( - val error: String - ) : SignInState() - - /** - * The user is signed in. - */ - data class SignedIn( - val username: String - ) : SignInState() -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/MainActivity.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/MainActivity.kt deleted file mode 100644 index c9ff884..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/MainActivity.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui - -import android.os.Bundle -import android.widget.Toast -import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.fragment.app.commit -import androidx.lifecycle.lifecycleScope -import com.google.android.gms.identity.sample.fido2.R -import com.google.android.gms.identity.sample.fido2.repository.SignInState -import com.google.android.gms.identity.sample.fido2.ui.auth.AuthFragment -import com.google.android.gms.identity.sample.fido2.ui.home.HomeFragment -import com.google.android.gms.identity.sample.fido2.ui.username.UsernameFragment -import com.google.android.gms.fido.Fido -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect - -@AndroidEntryPoint -class MainActivity : AppCompatActivity() { - - private val viewModel: MainViewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.main_activity) - setSupportActionBar(findViewById(R.id.toolbar)) - - lifecycleScope.launchWhenStarted { - viewModel.signInState.collect { state -> - when (state) { - is SignInState.SignedOut -> { - showFragment(UsernameFragment::class.java) { UsernameFragment() } - } - is SignInState.SigningIn -> { - showFragment(AuthFragment::class.java) { AuthFragment() } - } - is SignInState.SignInError -> { - Toast.makeText(this@MainActivity, state.error, Toast.LENGTH_LONG).show() - // return to username prompt - showFragment(UsernameFragment::class.java) { UsernameFragment() } - } - is SignInState.SignedIn -> { - showFragment(HomeFragment::class.java) { HomeFragment() } - } - } - } - } - } - - override fun onResume() { - super.onResume() - viewModel.setFido2ApiClient(Fido.getFido2ApiClient(this)) - } - - override fun onPause() { - super.onPause() - viewModel.setFido2ApiClient(null) - } - - private fun showFragment(clazz: Class, create: () -> Fragment) { - val manager = supportFragmentManager - if (!clazz.isInstance(manager.findFragmentById(R.id.container))) { - manager.commit { - replace(R.id.container, create()) - } - } - } - -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/MainViewModel.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/MainViewModel.kt deleted file mode 100644 index 68b9545..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/MainViewModel.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui - -import androidx.lifecycle.ViewModel -import com.google.android.gms.identity.sample.fido2.repository.AuthRepository -import com.google.android.gms.fido.fido2.Fido2ApiClient -import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject - -@HiltViewModel -class MainViewModel @Inject constructor( - private val repository: AuthRepository -) : ViewModel() { - - val signInState = repository.signInState - - fun setFido2ApiClient(client: Fido2ApiClient?) { - repository.setFido2APiClient(client) - } - -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/auth/AuthFragment.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/auth/AuthFragment.kt deleted file mode 100644 index f7210ec..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/auth/AuthFragment.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui.auth - -import android.app.Activity -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.inputmethod.EditorInfo -import android.widget.Toast -import androidx.activity.result.ActivityResult -import androidx.activity.result.IntentSenderRequest -import androidx.activity.result.contract.ActivityResultContracts -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope -import com.google.android.gms.identity.sample.fido2.R -import com.google.android.gms.identity.sample.fido2.databinding.AuthFragmentBinding -import com.google.android.gms.fido.Fido -import com.google.android.gms.fido.fido2.api.common.AuthenticatorErrorResponse -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch - -@AndroidEntryPoint -class AuthFragment : Fragment() { - - private val viewModel: AuthViewModel by viewModels() - private lateinit var binding: AuthFragmentBinding - - private val signIntentLauncher = registerForActivityResult( - ActivityResultContracts.StartIntentSenderForResult(), - ::handleSignResult - ) - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = AuthFragmentBinding.inflate(inflater, container, false) - binding.lifecycleOwner = viewLifecycleOwner - binding.viewModel = viewModel - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - binding.inputPassword.setOnEditorActionListener { _, actionId, _ -> - if (actionId == EditorInfo.IME_ACTION_GO) { - viewModel.submitPassword() - true - } else { - false - } - } - - viewLifecycleOwner.lifecycleScope.launchWhenStarted { - launch { - viewModel.signinRequests.collect { intent -> - signIntentLauncher.launch(IntentSenderRequest.Builder(intent).build()) - } - } - launch { - viewModel.processing.collect { processing -> - if (processing) { - binding.processing.show() - } else { - binding.processing.hide() - } - } - } - } - } - - private fun handleSignResult(activityResult: ActivityResult) { - val bytes = activityResult.data?.getByteArrayExtra(Fido.FIDO2_KEY_CREDENTIAL_EXTRA) - when { - activityResult.resultCode != Activity.RESULT_OK -> - Toast.makeText(requireContext(), R.string.cancelled, Toast.LENGTH_LONG).show() - bytes == null -> - Toast.makeText(requireContext(), R.string.auth_error, Toast.LENGTH_LONG).show() - else -> { - val credential = PublicKeyCredential.deserializeFromBytes(bytes) - val response = credential.response - if (response is AuthenticatorErrorResponse) { - Toast.makeText(requireContext(), response.errorMessage, Toast.LENGTH_LONG) - .show() - } else { - viewModel.signinResponse(credential) - } - } - } - } -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/auth/AuthViewModel.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/auth/AuthViewModel.kt deleted file mode 100644 index 8d077e1..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/auth/AuthViewModel.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui.auth - -import android.app.PendingIntent -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.google.android.gms.identity.sample.fido2.repository.AuthRepository -import com.google.android.gms.identity.sample.fido2.repository.SignInState -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class AuthViewModel @Inject constructor( - private val repository: AuthRepository -) : ViewModel() { - - val password = MutableStateFlow("") - - private val _processing = MutableStateFlow(false) - val processing = _processing.asStateFlow() - - private val _errorMessage = MutableStateFlow(null) - val errorMessage = _errorMessage.asStateFlow() - - val signInEnabled = combine(processing, password) { isProcessing, password -> - !isProcessing && password.isNotBlank() - }.stateIn(viewModelScope, SharingStarted.Eagerly, false) - - private val signinRequestChannel = Channel(capacity = Channel.CONFLATED) - val signinRequests = signinRequestChannel.receiveAsFlow() - - init { - // See if we can authenticate using FIDO. - viewModelScope.launch { - val intent = repository.signinRequest() - if (intent != null) { - signinRequestChannel.send(intent) - } - } - } - - val currentUsername = repository.signInState.map { state -> - when (state) { - is SignInState.SigningIn -> state.username - is SignInState.SignedIn -> state.username - else -> "(user)" - } - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "(user)") - - fun submitPassword() { - viewModelScope.launch { - _processing.value = true - try { - repository.password(password.value) - } finally { - _processing.value = false - } - } - } - - fun signinResponse(credential: PublicKeyCredential) { - viewModelScope.launch { - _processing.value = true - try { - repository.signinResponse(credential) - } finally { - _processing.value = false - } - } - } - -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/CredentialAdapter.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/CredentialAdapter.kt deleted file mode 100644 index 8a5dc3b..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/CredentialAdapter.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui.home - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import com.google.android.gms.identity.sample.fido2.api.Credential -import com.google.android.gms.identity.sample.fido2.databinding.CredentialItemBinding - -class CredentialAdapter( - private val onDeleteClicked: (String) -> Unit -) : ListAdapter(DIFF_CALLBACK) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CredentialViewHolder { - return CredentialViewHolder( - CredentialItemBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ), - onDeleteClicked - ) - } - - override fun onBindViewHolder(holder: CredentialViewHolder, position: Int) { - holder.binding.credential = getItem(position) - } -} - -private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Credential, newItem: Credential): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame(oldItem: Credential, newItem: Credential): Boolean { - return oldItem == newItem - } -} - -class CredentialViewHolder( - val binding: CredentialItemBinding, - onDeleteClicked: (String) -> Unit -) : RecyclerView.ViewHolder(binding.root) { - init { - binding.delete.setOnClickListener { - binding.credential?.let { c -> - onDeleteClicked(c.id) - } - } - } -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/DeleteConfirmationFragment.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/DeleteConfirmationFragment.kt deleted file mode 100644 index 529e30e..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/DeleteConfirmationFragment.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui.home - -import android.app.Dialog -import android.os.Bundle -import androidx.appcompat.app.AlertDialog -import androidx.fragment.app.DialogFragment -import com.google.android.gms.identity.sample.fido2.R - -class DeleteConfirmationFragment : DialogFragment() { - - companion object { - private const val ARG_CREDENTIAL_ID = "credential_id" - - fun newInstance(credentialId: String) = DeleteConfirmationFragment().apply { - arguments = Bundle().apply { - putString(ARG_CREDENTIAL_ID, credentialId) - } - } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val credentialId = arguments?.getString(ARG_CREDENTIAL_ID) ?: throw RuntimeException() - return AlertDialog.Builder(requireContext()) - .setMessage(getString(R.string.delete_confirmation, credentialId)) - .setPositiveButton(android.R.string.ok) { _, _ -> - (parentFragment as Listener).onDeleteConfirmed(credentialId) - dismiss() - } - .setNegativeButton(android.R.string.cancel) { _, _ -> - dismiss() - } - .create() - } - - interface Listener { - fun onDeleteConfirmed(credentialId: String) - } - -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/HomeFragment.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/HomeFragment.kt deleted file mode 100644 index 7fd6c69..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/HomeFragment.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui.home - -import android.app.Activity -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.activity.result.ActivityResult -import androidx.activity.result.IntentSenderRequest -import androidx.activity.result.contract.ActivityResultContracts -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope -import com.google.android.gms.fido.Fido -import com.google.android.gms.fido.fido2.api.common.AuthenticatorErrorResponse -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential -import com.google.android.gms.identity.sample.fido2.R -import com.google.android.gms.identity.sample.fido2.databinding.HomeFragmentBinding -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch - -@AndroidEntryPoint -class HomeFragment : Fragment(), DeleteConfirmationFragment.Listener { - - companion object { - private const val TAG = "HomeFragment" - private const val FRAGMENT_DELETE_CONFIRMATION = "delete_confirmation" - } - - private val viewModel: HomeViewModel by viewModels() - private lateinit var binding: HomeFragmentBinding - - private val createCredentialIntentLauncher = registerForActivityResult( - ActivityResultContracts.StartIntentSenderForResult(), - ::handleCreateCredentialResult - ) - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - binding = HomeFragmentBinding.inflate(inflater, container, false) - binding.lifecycleOwner = viewLifecycleOwner - binding.viewModel = viewModel - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - val credentialAdapter = CredentialAdapter { credentialId -> - DeleteConfirmationFragment.newInstance(credentialId) - .show(childFragmentManager, FRAGMENT_DELETE_CONFIRMATION) - } - binding.credentials.adapter = credentialAdapter - - viewLifecycleOwner.lifecycleScope.launchWhenStarted { - viewModel.credentials.collect { credentials -> - credentialAdapter.submitList(credentials) - binding.emptyCredentials.isVisible = credentials.isEmpty() - binding.credentialsCaption.isVisible = credentials.isNotEmpty() - } - } - - // Menu - binding.appBar.replaceMenu(R.menu.home) - binding.appBar.setOnMenuItemClickListener { item -> - when (item.itemId) { - R.id.action_reauth -> { - viewModel.reauth() - true - } - R.id.action_sign_out -> { - viewModel.signOut() - true - } - else -> false - } - } - - viewLifecycleOwner.lifecycleScope.launchWhenStarted { - viewModel.processing.collect { processing -> - if (processing) { - binding.processing.show() - } else { - binding.processing.hide() - } - } - } - - // FAB - binding.add.setOnClickListener { - lifecycleScope.launch { - val intent = viewModel.registerRequest() - if (intent != null) { - createCredentialIntentLauncher.launch( - IntentSenderRequest.Builder(intent).build() - ) - } - } - } - } - - override fun onDeleteConfirmed(credentialId: String) { - viewModel.removeKey(credentialId) - } - - private fun handleCreateCredentialResult(activityResult: ActivityResult) { - val bytes = activityResult.data?.getByteArrayExtra(Fido.FIDO2_KEY_CREDENTIAL_EXTRA) - when { - activityResult.resultCode != Activity.RESULT_OK -> - Toast.makeText(requireContext(), R.string.cancelled, Toast.LENGTH_LONG).show() - bytes == null -> - Toast.makeText(requireContext(), R.string.credential_error, Toast.LENGTH_LONG) - .show() - else -> { - val credential = PublicKeyCredential.deserializeFromBytes(bytes) - val response = credential.response - if (response is AuthenticatorErrorResponse) { - Toast.makeText(requireContext(), response.errorMessage, Toast.LENGTH_LONG) - .show() - } else { - viewModel.registerResponse(credential) - } - } - } - } -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/HomeViewModel.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/HomeViewModel.kt deleted file mode 100644 index 7654fc9..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/home/HomeViewModel.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui.home - -import android.app.PendingIntent -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.google.android.gms.identity.sample.fido2.repository.AuthRepository -import com.google.android.gms.identity.sample.fido2.repository.SignInState -import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class HomeViewModel @Inject constructor( - private val repository: AuthRepository -) : ViewModel() { - - private val _processing = MutableStateFlow(false) - val processing = _processing.asStateFlow() - - val currentUsername = repository.signInState.map { state -> - when (state) { - is SignInState.SigningIn -> state.username - is SignInState.SignedIn -> state.username - else -> "(user)" - } - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "(user)") - - val credentials = repository.credentials.stateIn( - viewModelScope, SharingStarted.WhileSubscribed(), emptyList() - ) - - fun reauth() { - viewModelScope.launch { - repository.clearCredentials() - } - } - - fun signOut() { - viewModelScope.launch { - repository.signOut() - } - } - - suspend fun registerRequest(): PendingIntent? { - _processing.value = true - try { - return repository.registerRequest() - } finally { - _processing.value = false - } - } - - fun registerResponse(credential: PublicKeyCredential) { - viewModelScope.launch { - _processing.value = true - try { - repository.registerResponse(credential) - } finally { - _processing.value = false - } - } - } - - fun removeKey(credentialId: String) { - viewModelScope.launch { - _processing.value = true - try { - repository.removeKey(credentialId) - } finally { - _processing.value = false - } - } - } - -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/username/UsernameFragment.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/username/UsernameFragment.kt deleted file mode 100644 index 5ed3905..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/username/UsernameFragment.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui.username - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.inputmethod.EditorInfo -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope -import com.google.android.gms.identity.sample.fido2.databinding.UsernameFragmentBinding -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect - -@AndroidEntryPoint -class UsernameFragment : Fragment() { - - private val viewModel: UsernameViewModel by viewModels() - private lateinit var binding: UsernameFragmentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = UsernameFragmentBinding.inflate(inflater, container, false) - binding.lifecycleOwner = viewLifecycleOwner - binding.viewModel = viewModel - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - viewLifecycleOwner.lifecycleScope.launchWhenStarted { - viewModel.sending.collect { sending -> - if (sending) { - binding.sending.show() - } else { - binding.sending.hide() - } - } - } - - binding.inputUsername.setOnEditorActionListener { _, actionId, _ -> - if (actionId == EditorInfo.IME_ACTION_NEXT) { - viewModel.sendUsername() - true - } else { - false - } - } - } - -} diff --git a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/username/UsernameViewModel.kt b/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/username/UsernameViewModel.kt deleted file mode 100644 index e35416c..0000000 --- a/Fido2/app/src/main/java/com/google/android/gms/identity/sample/fido2/ui/username/UsernameViewModel.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.identity.sample.fido2.ui.username - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.google.android.gms.identity.sample.fido2.repository.AuthRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class UsernameViewModel @Inject constructor( - private val repository: AuthRepository -) : ViewModel() { - - private val _sending = MutableStateFlow(false) - val sending = _sending.asStateFlow() - - val username = MutableStateFlow("") - - val nextEnabled = combine(sending, username) { isSending, username -> - !isSending && username.isNotBlank() - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) - - fun sendUsername() { - val username = username.value - if (username.isNotBlank()) { - viewModelScope.launch { - _sending.value = true - try { - repository.username(username) - } finally { - _sending.value = false - } - } - } - } -} diff --git a/Fido2/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Fido2/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 00c25f2..0000000 --- a/Fido2/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - diff --git a/Fido2/app/src/main/res/drawable/ic_add.xml b/Fido2/app/src/main/res/drawable/ic_add.xml deleted file mode 100644 index 459764c..0000000 --- a/Fido2/app/src/main/res/drawable/ic_add.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - diff --git a/Fido2/app/src/main/res/drawable/ic_delete.xml b/Fido2/app/src/main/res/drawable/ic_delete.xml deleted file mode 100644 index 51d48f0..0000000 --- a/Fido2/app/src/main/res/drawable/ic_delete.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/Fido2/app/src/main/res/drawable/ic_launcher_background.xml b/Fido2/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 7f3a242..0000000 --- a/Fido2/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Fido2/app/src/main/res/layout/auth_fragment.xml b/Fido2/app/src/main/res/layout/auth_fragment.xml deleted file mode 100644 index 58e6054..0000000 --- a/Fido2/app/src/main/res/layout/auth_fragment.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Fido2/app/src/main/res/layout/credential_item.xml b/Fido2/app/src/main/res/layout/credential_item.xml deleted file mode 100644 index e2d9a44..0000000 --- a/Fido2/app/src/main/res/layout/credential_item.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/Fido2/app/src/main/res/layout/home_fragment.xml b/Fido2/app/src/main/res/layout/home_fragment.xml deleted file mode 100644 index fc0488a..0000000 --- a/Fido2/app/src/main/res/layout/home_fragment.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Fido2/app/src/main/res/layout/main_activity.xml b/Fido2/app/src/main/res/layout/main_activity.xml deleted file mode 100644 index 2f955e3..0000000 --- a/Fido2/app/src/main/res/layout/main_activity.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - diff --git a/Fido2/app/src/main/res/layout/username_fragment.xml b/Fido2/app/src/main/res/layout/username_fragment.xml deleted file mode 100644 index 57a2bac..0000000 --- a/Fido2/app/src/main/res/layout/username_fragment.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Fido2/app/src/main/res/menu/home.xml b/Fido2/app/src/main/res/menu/home.xml deleted file mode 100644 index 784b880..0000000 --- a/Fido2/app/src/main/res/menu/home.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - diff --git a/Fido2/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Fido2/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 71c7a0c..0000000 --- a/Fido2/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/Fido2/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Fido2/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 71c7a0c..0000000 --- a/Fido2/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/Fido2/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Fido2/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 898f3ed..0000000 Binary files a/Fido2/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/Fido2/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/Fido2/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index dffca36..0000000 Binary files a/Fido2/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/Fido2/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Fido2/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 64ba76f..0000000 Binary files a/Fido2/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/Fido2/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/Fido2/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index dae5e08..0000000 Binary files a/Fido2/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/Fido2/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Fido2/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index e5ed465..0000000 Binary files a/Fido2/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/Fido2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/Fido2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 14ed0af..0000000 Binary files a/Fido2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/Fido2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Fido2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index b0907ca..0000000 Binary files a/Fido2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/Fido2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Fido2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index d8ae031..0000000 Binary files a/Fido2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/Fido2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Fido2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 2c18de9..0000000 Binary files a/Fido2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/Fido2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Fido2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index beed3cd..0000000 Binary files a/Fido2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/Fido2/app/src/main/res/values/colors.xml b/Fido2/app/src/main/res/values/colors.xml deleted file mode 100644 index 39af33f..0000000 --- a/Fido2/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - #3F51B5 - #303F9F - #6D4C41 - diff --git a/Fido2/app/src/main/res/values/dimens.xml b/Fido2/app/src/main/res/values/dimens.xml deleted file mode 100644 index 5e3b9e4..0000000 --- a/Fido2/app/src/main/res/values/dimens.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - 8dp - 16dp - 24dp - 32dp - 64dp - diff --git a/Fido2/app/src/main/res/values/strings.xml b/Fido2/app/src/main/res/values/strings.xml deleted file mode 100644 index 5a6aae7..0000000 --- a/Fido2/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - FIDO2 - - - - Sign In - Username - For the purpose of demonstration, this app accepts any username. - Next - - - - Welcome, %s! - For the purpose of demonstration, the password you type is ignored. - Password - Cancelled - Error trying to authenticate - - - - Your registered credentials: - "You don't have any credentials.\nTap '+' to add one." - Reauth - Sign Out - Delete - Are you sure you want to delete this credential?\n\n%s - Error creating credential - Add credential - - diff --git a/Fido2/app/src/main/res/values/styles.xml b/Fido2/app/src/main/res/values/styles.xml deleted file mode 100644 index 084cd10..0000000 --- a/Fido2/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - diff --git a/Fido2/build.gradle b/Fido2/build.gradle deleted file mode 100644 index 909ecfc..0000000 --- a/Fido2/build.gradle +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -buildscript { - ext.kotlin_version = '1.5.21' - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:7.0.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1' - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/Fido2/debug.jks b/Fido2/debug.jks deleted file mode 100644 index 1e31764..0000000 Binary files a/Fido2/debug.jks and /dev/null differ diff --git a/Fido2/gradle.properties b/Fido2/gradle.properties deleted file mode 100644 index d79feb7..0000000 --- a/Fido2/gradle.properties +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright 2021 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true -# Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official - -# The URL of the server -host=https://webauthn-codelab.glitch.me diff --git a/Fido2/gradle/wrapper/gradle-wrapper.jar b/Fido2/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index f6b961f..0000000 Binary files a/Fido2/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/Fido2/gradle/wrapper/gradle-wrapper.properties b/Fido2/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 88441a2..0000000 --- a/Fido2/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright 2021 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -#Tue Nov 17 16:25:58 JST 2020 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip diff --git a/Fido2/gradlew b/Fido2/gradlew deleted file mode 100755 index 8b31a7a..0000000 --- a/Fido2/gradlew +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2021 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/Fido2/gradlew.bat b/Fido2/gradlew.bat deleted file mode 100644 index e95643d..0000000 --- a/Fido2/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/Fido2/settings.gradle b/Fido2/settings.gradle deleted file mode 100644 index 33e604e..0000000 --- a/Fido2/settings.gradle +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2021 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -include ':app'