I have been trying to integrate AppCheck with my Android app, but I can't seem to make a valid request.
As for test purposes, I have been using the following code:
Android Code
class Application : MultiDexApplication() {
override fun onCreate() {
FirebaseApp.initializeApp(this)
val appCheck = FirebaseAppCheck.getInstance()
if (BuildConfig.DEBUG) appCheck.installAppCheckProviderFactory(DebugAppCheckProviderFactory.getInstance(), true)
else appCheck.installAppCheckProviderFactory(SafetyNetAppCheckProviderFactory.getInstance(), true)
super.onCreate()
}
}
class TestActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
val data = "This is a test!"
Firebase.functions.getHttpsCallable("test").call(data).addOnCompleteListener {
if (it.isSuccessful) {
val result = it.result.data
print(result)
}
else {
val exception = it.exception
print(exception)
}
}
}
}
Function Code
const functions = require("firebase-functions")
exports.test = functions.https.onCall((data, context) => {
if (context.app == undefined) {
functions.logger.error("Test - Failed validating device integrity!")
throw new functions.https.HttpsError("failed-precondition", "The function must be called from an App Check verified app")
}
return data
})
I have added the DebugAppCheckProviderFactory token to the Firebase console, but no matter what I do, if it is an emulator or physical device, when I call the function, the failed-precondition exception is always thrown.
Checking the function logs, I can see that the app is missing:
I have already read the documentation multiple times and I can't seem to be missing any step. Am I missing something or is there anything I can do to find the root cause for this?
Thank you for your time!
EDIT:
As for Martin suggestion, I created a new OnRequest function and added the X-Firebase-AppCheck header. I received the token correctly and was able to validate it successfully with:
firebaseAdmin.appCheck().verifyToken(appCheckToken)
So, my guess is that Android is not adding the X-Firebase-AppCheck automatically to the OnCall function like it should.
I ran the code and made some breakpoints through the code and noticed the following. The call method from Firebase is adding the Firebase-Instance-ID-Token but I can't seem to find the X-Firebase-AppCheck header anywhere. Maybe I am not supposed to see this value, or maybe I just can't find where it is being added. Or maybe it is not added at all, thus I can't validate my context.app at all.
It may be required to obtain an AppCheckToken:
FirebaseAppCheck
.getInstance()
.getAppCheckToken(false)
.addOnSuccessListener(new OnSuccessListener<AppCheckToken>() {
#Override
public void onSuccess(#NonNull AppCheckToken tokenResponse) {
String appCheckToken = tokenResponse.getToken();
...
}
});
Which has to be passed along, with the HTTP request (the example shows Retrofit, which could also be used instead of HttpsCallable:
#GET("yourExampleEndpoint")
Call<List<String>> exampleData(
#Header("X-Firebase-AppCheck") String appCheckToken,
...
);
Where FirebaseFunctions.getInstance().getHttpsCallable().call() doesn't tell how or if one has to explicitly set that X-Firebase-AppCheck header. Cloud Functions should usually receive an X-Firebase-AppCheck header - with the previously retrieved AppCheck token:
const appCheckToken = req.header('X-Firebase-AppCheck');
if (!appCheckToken) {
res.status(401);
return next('Unauthorized');
}
try {
const appCheckClaims = await firebaseAdmin.appCheck().verifyToken(appCheckToken);
// If verifyToken() succeeds, continue with the next middleware function in the stack.
return next();
} catch (err) {
res.status(401);
return next('Unauthorized');
}
One can also issue own tokens ...and checking for context-app is also valid.
Update: The Protocol specification for https.onCall reads:
Optional: X-Firebase-AppCheck: <token>
The Firebase App Check token provided by the client app making the request. The backend automatically verifies this token and decodes it, injecting the appId in the handler's context. If the token cannot be verified, the request is rejected.Available for SDK >=3.14.0
To install the minimum required NodeJS dependencies:
npm install firebase-functions#">=3.14.0"
npm install firebase-admin#">=9.8.0"
And last, but not least ...there's even a debug helper for that:
dependencies {
debugImplementation "com.google.firebase:firebase-appcheck-debug:16.0.0-beta02"
}
After testing everything with no success, in a desperate move, I tried to look to unrelated files and found the solution.
In my build.gradle I was importing all the Firebase dependencies with the bom:
implementation platform("com.google.firebase:firebase-bom:27.0.0")
implementation "com.google.firebase:firebase-crashlytics-ktx"
implementation "com.google.firebase:firebase-analytics-ktx"
implementation "com.google.firebase:firebase-firestore-ktx"
implementation "com.google.firebase:firebase-functions-ktx"
implementation "com.google.firebase:firebase-auth-ktx"
So I thought: "Can the firebase-bom be importing an outdated functions-ktx dependency?"
And guess what? As soon as I imported the dependencies without the bom, I started to see the X-Firebase-AppCheck being added and the context.app being valid.
I ended up with the following build.gralde:
implementation "com.google.firebase:firebase-crashlytics-ktx:18.2.1"
implementation "com.google.firebase:firebase-analytics-ktx:19.0.0"
implementation "com.google.firebase:firebase-firestore-ktx:23.0.3"
implementation "com.google.firebase:firebase-functions-ktx:20.0.0"
implementation "com.google.firebase:firebase-auth-ktx:21.0.1"
After a little more investigation, I found out that I was using an outdated firebase-bom version. Android Studio used to let me know if there was a new version for the dependencies, but it did not notice me for the firebase-bom. Turns out, that I just need to update my firebase-bom dependency.
TL;DR
Check your Android Firebase libraries version!
Related
MSALs java methods of ISingleAccountPublicClientApplication
signIn(activity, loginHint, scopes, authenticationCallback) and acquireTokenSilent(scopes, authority) is deprecated, in favour of build methods.
So I changed the signIn to use SignInParameters.builder() like this:
(below in kotlin)
From:
...signIn(
signInActivity,
null, //loginHint
arrayOf(accessScope),
object : AuthenticationCallback { /*omitted*/ },
)
To:
...signIn(
SignInParameters.builder(
.withActivity(signInActivity),
.withScopes(arrayOf(accessScope).toMutableList())
.withCallback(
object : AuthenticationCallback { /*omitted*/
})
.build()
)
So far, the sign in process seems to go well, no errors, but the problems occures when I call the new acquireTokenSilent method with AcquireTokenSilentParameters used.
From:
val iAuthenticationResult = singleAccountApp.acquireTokenSilent(
arrayOf(accessScope),
MICROSOFT_ACCESS_AUTHORITY
)
To:
val iAuthenticationResult = singleAccountApp.acquireTokenSilent(
AcquireTokenSilentParameters(
AcquireTokenSilentParameters.Builder()
.withScopes(arrayOf(accessScope).toMutableList())
.fromAuthority(MICROSOFT_ACCESS_AUTHORITY)
)
)
Now I get following error message in response when acquiring token:
The signed in account does not match the provided account which further causes 401 Unauthorized response.
Rewinding back to use the old method just for acquireTokenSilent does not produce any error and the token is provided.
Now I am stuck in new for signIn method, but an old one for the acquireTokenSilent method.
Is there anything I have skipped or forgotten using the newer one ?
I have the current workflow for my authentication
User signs in via google OAuth2
User is then given a server_auth_code which they send to my backend authentication
The auth code is validated on the back end and users is sent a JWT
I had this all working in raw Java with the Android SDK, but Flutter seemed a lot nicer. But now when using the google_sign_in plugin on android, I am unable to retrieve the serverAuthCore anymore, this plugin just wants to return null the entire time.
I Am using the client ID that's specified for Android, however, I tested the WebApplication that's auto-generated by google too but that's the same issue (Null serverAutHCode)
This is the code that I am currently using:
/// Provides the `GoogleSignIn` class
import 'package:google_sign_in/google_sign_in.dart';
class GoogleLoginPage extends StatefulWidget {
final String name = "Logging in with google.";
late GoogleSignIn _googleSignIn;
GoogleLoginPage() : super() {
_googleSignIn = GoogleSignIn(
scopes: ['https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile'],
serverClientId: "XX-XXX.apps.googleusercontent.com"
);
}
Future<void> fetchAuthToken() async {
try {
var response = await _googleSignIn.signIn();
log(response?.serverAuthCode ?? "No server auth code");
_googleSignIn.signOut();
} catch (error) {
print("ERR");
print(error);
}
}
#override
State<GoogleLoginPage> createState() => GoogleLoginState();
}
The output of this code is: [log] No server auth code
The question:
Am I doing something wrong? As mentioned this works 100% on my java project using the google play services SDK so I know it's nothing to do with my google console configurations.
Okay so I figured out the issues:
It appears that by default the google login plugin for flutter comes on an older version (If I remember correctly it was 20.0.5)
I Simply changed the version to the latest version:
'com.google.android.gms:play-services-auth:20.2.0'
You can do this by editing the project's build.gradle (In IntelliJ you open build.gradle and click "Open for editing in the android studio" in the top right, from there you need to find the gradle file for google_sign_in, and change the import there, remember to click sync in the top right of android studio before you close out of it)
And I began to receive my serverAuthCode as normal, cheers!
I am trying to use AWS Amplify's REST API with my android app. I did exactly as it was written in the docs, but I am still getting this error:
ApiException{message=AWSApiPlugin depends on AWSCognitoAuthPlugin but it is currently missing, cause=java.lang.IllegalStateException: Tried to get a plugin but that plugin was not present. Check if the plugin was added originally or perhaps was already removed., recoverySuggestion=Before configuring Amplify, be sure to add AWSCognitoAuthPlugin same as you added AWSApiPlugin.}
These are the AWS dependencies in my Gradle file:
dependencies {
// ...
implementation 'com.amplifyframework:aws-api:1.6.4'
implementation 'com.amplifyframework:core:1.6.4'
implementation 'com.amazonaws:aws-android-sdk-apigateway-core:2.3.2'
implementation 'com.amazonaws:aws-android-sdk-cognito:2.3.2'
// ...
}
This is my Application class:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
try {
Amplify.addPlugin(AWSApiPlugin())
Amplify.configure(applicationContext)
Log.i("MyAmplifyApp", "Initialized Amplify.")
} catch (error: AmplifyException){
Log.e("MyAmplifyApp","Could not initialize Amplify.",error)
}
}
}
This error happens because you need to call Amplify.addPlugin before you make use of it.
It can also be there was an error initializing the plugin, so check the suggestion in the error when catching the exception.
So call whatever plugin you are using, preferrably in your Application class (MyApp.java) code:
public class MyAmplifyApp extends Application {
String tag = "xxx";
#Override
public void onCreate() {
super.onCreate();
//xxx
try {
Amplify.addPlugin(new AWSDataStorePlugin());
Amplify.addPlugin(new AWSCognitoAuthPlugin());
Amplify.configure(getApplicationContext());
Log.i(tag, "Initialized Amplify");
} catch (AmplifyException error) {
Log.e(tag, "Could not initialize Amplify", error);
}
}
}
How are you attempting to authenticate with your API? What is the authType listed in your app/src/main/res/raw/amplifyconfiguration.json?
If it is something involving Cognito, you need to add the Auth category. Please follow the Getting Started guide for Auth.
If you are not trying to use Cognito, update your API to use a different auth type using the CLI:
amplify update api
amplify push
For example, you might select API Key authorization, which essentially makes your API public to anyone who knows the key.
According to official Doc Add This line to my configuration function solved my problem
Amplify.addPlugin(new AWSApiPlugin());
So it become look like this
try {
Amplify.addPlugin(new AWSDataStorePlugin());
Amplify.addPlugin(new AWSApiPlugin());
Amplify.configure(getApplicationContext());
Log.i("MyAmplifyApp", "Initialized Amplify");
} catch (AmplifyException error) {
Log.e("MyAmplifyApp", "Could not initialize Amplify", error);
}
I had the same issue and the previous answers pointed me in the right direction. I was missing the line below from my initialize function, as soon as I added this in everything worked as planned.
Amplify.addPlugin(AWSCognitoAuthPlugin())
To begin with, I'm working on a Unity Game where I'm authenticating user when the game starts. My build environment is android. I'm using Firebase authentication for Google Play Games Services to authenticate user.
When the game starts in my android device or emulator, it is able to authenticate Play Games Services as well as able to connect with Firebase (I'm getting analytics data). However, when I pass the PlayGames AuthCode into Firebase.Auth Credentials, it stops executing the code (I've debug log for it). It does not throw any error in LogCat except
Firebase | server_auth_code
I tried searching web for different issues, but nothing. I checked my keys in player setting, firebase settings, OAuth 2.0 credentials on my Google API console and even check keys from my Google Play Console (which I'm not using at this stage). I have even checked my test users email addresses in Game Services and tried multiple google play games account. But issue still persist.
I'm using similar script in my other unity project where authentication works like a charm. I tried to use same script here and ended up with this issue: here. However, I solved it by removing all the packages and re-importing them into unity and changed my call functions in the script. Now, I'm stuck at this issue.
Here is cs file:
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using UnityEngine.SocialPlatforms;
using System.Threading.Tasks;
public class SetFirebase : MonoBehaviour
{
string authCode;
void Start()
{
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder().
RequestServerAuthCode(false /* Don't force refresh */).Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.Activate();
Social.localUser.Authenticate((bool success) =>
{
if (success)
{
authCode = PlayGamesPlatform.Instance.GetServerAuthCode();
Debug.Log("PlayGames successfully authenticated!");
Debug.Log("AuthCode: " + authCode);
}
else
{
Debug.Log("PlayGames SignIn Failed");
}
});
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
{
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available)
{
Debug.Log("Firebase Ready!!!");
RunFirebase();
}
else
{
Debug.LogError(System.String.Format("Could not resolve all Firebase dependencies: {0}", dependencyStatus));
}
});
}
private void RunFirebase(){
Firebase.Auth.FirebaseAuth auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
Debug.Log("init firebase auth ");
Firebase.Auth.Credential credential = Firebase.Auth.PlayGamesAuthProvider.GetCredential(authCode);
Debug.Log(" passed auth code ");
auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("SignInOnClick was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("SignInOnClick encountered an error: " + task.Exception);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("SignInOnClick: User signed in successfully: {0} ({1})", newUser.DisplayName, newUser.UserId);
});
}
}
My LogCat executes everything till "init firebase auth" but does not execute "passed auth code" so I know there is some issue in passing the credentials. It also does not run anything inside auth.SignInWithCredentialAsync(credential).
Any help or suggestion would be highly appreciated. Thank you.
There are two things I may suggest:
1) Replace ContinueWith with ContinueWithOnMainThread. This is a Firebase Extension that will guarantee that your logic runs on the main Unity thread (which tends to resolve many Unity specific issues). I go into more detail about that here.
2) Your logic may have a race condition between the Authenticate callback and the CheckAndFixDependenciesAsync continuation. These will not necessarily run in the order that you see them in your logic.
If I were building this system, I might prefer using Coroutines and a custom yield instruction:
class Authenticate : CustomYieldInstruction
{
private bool _keepWaiting = true;
public override bool keepWaiting => _keepWaiting;
public Authenticate(Social.ILocalUser user) {
user.Authenticate((bool success)=>{
/* old authentication code here */
_keepWaiting = false;
});
}
}
Then in a coroutine have something like:
private IEnumerator InitializeCoroutine() {
/* old authentication code */
// I'm ignoring error checking for now, but it shouldn't be hard to figure in.
// I'm mostly going from memory now anyway
// start both authentication processes in parallel
var authenticate = new Authenticate(Social.localUser);
var firebaseDependenciesTask = FirebaseApp.CheckAndFixDependenciesAsync();
// wait on social
yield return authenticate;
// wait on Firebase. If it finished in the meantime this should just fall through
yield return new WaitUntil(()=>firebaseDependenciesTask.IsComplete);
RunFirebase();
}
This way my logic looks roughly synchronous whilst still maintaining the asynchronosity (spell check claims that I made up that word) of the systems you're depending on and you avoid threading related issues that arise when using ContinueWith.
Let me know if that helps!
--Patrick
I'm setting up the package react-native-moengage in my react-native app. Followed the guidelines here for react-native all the way to tracking events. Yet calling trackEvent on my code does not publish anything and does not throw an error at JS level.
Running on react-native:0.59and react-native-moengage:^3.0.0
Have not done anything weird beyond what is in the documentation, set up a service to call ReactMoE.trackEvent from sagas. I set up logs and indeed trackEvent is getting called
Pretty much this is it
import ReactMoE from 'react-native-moengage'
class MoengageService {
...
constructor() {
ReactMoE.isExistingUser(true);
}
setUser(userId: string, email: string, name: string) {
ReactMoE.setUserUniqueID(userId);
ReactMoE.setUserEmailID(email);
ReactMoE.setUserAttribute('inAppName', name)
}
unsetUser() {
ReactMoE.logout()
}
logEvent = (event: string, data = {}) => {
const timestamp = moment().valueOf()
const dataToLog = {
...data,
timestamp,
}
ReactMoE.trackEvent(event, dataToLog)
}
...
}
export const instance = new MoengageService()
I also succesfully set up moengage natively thanks to a previous SO question, or so I believe.
I would expect something to appear in the dashboard, it's blank with the big text up top saying I have not received anything in the last hour
How do I get it to push to dashboard?
EDIT
Just for clarification, I am not interested (yet) in push notifications or any of the like, which is why I skipped setting up firebase and everything
After Checking the logs and thinking out loud, I noticed the issue was that the dashboard was being misused, we were checking for debug logs on live instead of test.
If you followed the docs and see nothing, try to check which environment are you reading. Test for debug apps, Live for signed apps.
A helpful thing to check logs, adb logcat MoEngage_v${Your moengage native version here}