Recently I started testing my go server, which uses firebase admin sdk, with firebase local emulator suite. It works perfectly with firestore, but with the authentication not so much.
I tried many different approaches, including firebase demo project, my own demo project, and a simple test project. Nothing of those did the job; I kept receive the same error message, project not found.
Firebase Emulator
I use the firebase admin SDK, and I followed the insructions in the
official firebase article.
To configure the emulator, I ran firebase init and firebase init emulators in terminal. Those resulted in the files in the next section. Then the emulator starts with the command firebase emulators:start, and I added --project test to set the project ID.
Simplified Code
firebase.json
{
"emulators": {
"auth": {
"port": 9099
}
}
}
Simple JSON which describes the port for auth service.
.firebaserc
{}
This is empty because it doesn't have any project set, but I also tried adding projects here. Anyway, the project ID is set to "test" in both terminals, the one that runs the emulator, and the one that runs the golang app.
main.go
package main
import (
"context"
firebase "firebase.google.com/go"
"firebase.google.com/go/auth"
)
func main() {
ctx := context.Background()
config := &firebase.Config{ProjectID: "test"}
app, _ := firebase.NewApp(ctx, config)
Auth, _ := app.Auth(ctx)
params := (&auth.UserToCreate{}).
DisplayName("Some Name").
Email("example#gmail.com").
Password("password")
_, err := Auth.CreateUser(ctx, params)
if err != nil {
panic(err)
}
}
On the final line it panics with error status 400 Bad Request, and message PROJECT_NOT_FOUND. It looks like the admin SDK is looking at a public project with project ID "test", and I'm trying to emulate a local project.
Firestore emulator works, Auth does not.
I haven't found a single solution for this in golang. In node.js admin SDK the local auth emulator works, but I need the golang admin SDK.
How can I tell the go admin SDK for firebase auth emulator to look for a local project?
Related
Does anyone know why Firebase admin auth in node.js doesn't use ADC (Application Default Credentials)? I always have to set GOOGLE_APPLICATION_CREDENTIALS to a credentials file to get auth to work. Everything else (firestore, compute, storage etc.) works fine with ADC.
For instance, this code works only when GOOGLE_APPLICATION_CREDENTIALS is set to a valid credentials file, even though I'm logged into my Firebase project and my gcloud project:
import * as admin from 'firebase-admin'
admin.initializeApp()
async function listAllUsers(users: any[], matchRegex: RegExp, nextPageToken?: string) {
// List batch of users, 1000 at a time.
const listUsersResult = await admin.auth().listUsers(1000, nextPageToken)
.catch(function (error) {
console.log('Error listing users:', error);
});
if (listUsersResult) {
listUsersResult.users.forEach(function (userRecord) {
if (matchRegex.test(userRecord.email || '<none>') ||
matchRegex.test(userRecord.displayName || '<none>') ||
matchRegex.test(userRecord.uid))
users.push(userRecord.toJSON())
});
if (listUsersResult.pageToken) {
// List next batch of users.
console.log(`next batch...`)
listAllUsers(users, matchRegex, listUsersResult.pageToken);
}
}
}
If that env var is not set, I get this error:
Error listing users: FirebaseAuthError: Failed to determine project ID for Auth.
Initialize the SDK with service account credentials or set project ID as an app option.
Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.
But setting GOOGLE_CLOUD_PROJECT is not enough either. When I do that, I get:
Error listing users: FirebaseAuthError: //cloud.google.com/docs/authentication/. Raw server response: "{"error":{"code":403,"message":"Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the identitytoolkit.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/.","errors":[{"message":"Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the identitytoolkit.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/.","domain":"usageLimits","reason":"accessNotConfigured","extendedHelp":"https://console.developers.google.com"}],"status":"PERMISSION_DENIED"}}"
at FirebaseAuthError.FirebaseError [as constructor] (/c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/utils/error.js:43:28)
at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/utils/error.js:89:28)
at new FirebaseAuthError (/c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/utils/error.js:148:16)
at Function.FirebaseAuthError.fromServerError (/c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/utils/error.js:187:16)
at /c/dss/Product/Horizon/horizon/packages/renderer/node_modules/firebase-admin/lib/auth/auth-api-request.js:1490:49
As I said though, all other Firebase admin features seem to work fine with ADC; they automatically pick up the current project and my logged in account.
Brief description
Hi everyone,
I am working with Golang, developing a software to run locally at home but also synchronize with some cloud data (individually secured per user login). For that, I decided to use Google Cloud Service - Firebase (IAP, Hosting, Functions, and Firestore). Let me tell you, I am loving to develop and learn about these.
However, here comes the problem: I get really confused with the Golang tools (libraries) provided by Google to work on their service. They seem to have well-defined Golang libraries to work with their services IF your code is running in a trusted machine since you have to export credentials to it.
In my case, the software I am developing will run on the client machine and so I need a client login to IAP, etc.
Can anyone help me with that?
Issue
none of the tool available seem to be specific for Golang client (not-admin)
Google provides many, MANY repositories, and links to different tools that seem to do similar things, which one of them is for Admin usage, which one uses their RPC server, which one could I use for my client?
cloud.google.com/go/
firebase.google.com/go
https://github.com/googleapis/google-cloud-go
google.golang.org/api/ (Deprecated, but still referenced in their documentation)
Google has very similar names for different versions of the same things;
Documentation does not seem to be clear enough to me, and if they have a specific library or not for my case.
What I achieved
Using the REST API I managed to get the tokenID (login) with one user account (created inside my IAP project);
With its tokenID, I managed to use the REST API to retrieve some document that he has access;
With the following code, I manage to use one of the Golang libraries (supposedly for admin credentials) to retrieve the same documents using a private IAP user account of my project.
package main
import (
"context"
"fmt"
"cloud.google.com/go/firestore"
"golang.org/x/oauth2"
"google.golang.org/api/option"
)
func main() {
ctx := context.Background()
token := oauth2.Token{
AccessToken: "TOKEN_ID-FROM-IAP",
TokenType: "Bearer",
RefreshToken: "REFRESH_TOKEN-from-IAP",
}
oauthClient := &oauth2.Config{}
tSource := oauthClient.TokenSource(ctx, &token)
client, err := firestore.NewClient(ctx, "ProjectID", option.WithTokenSource(tSource))
if err != nil {
// TODO: Handle error.
}
defer client.Close()
iter := client.Doc("docPath").Snapshots(ctx)
defer iter.Stop()
for {
docsnap, err := iter.Next()
if err != nil {
// TODO: Handle error.
}
dataMap := docsnap.Data()
fmt.Println(dataMap)
}
}
Final questions
From the perspective of a non-admin client account of a private Firebase project:
Can anyone explain/clarify these many libraries?
I used oauth2 with the IAP token (acquired with the REST API), will this package work fine from now on?
Can I use oauth2 package to do more with IAP service? Like, renew the token, make login?
I appreciate thoughts and suggestions,
Thank you in advance!
I have a app built with vue and firebase/firestore. I use the firebase emulator to local development and am trying to integrate my dev workflow with cypress. But i get a error in cypress that do not occur if i access the app from browser.
Firebase CLI version is 7.9.0 and Cypress version is "^3.8.0"
My npm scripts to load everything are below:
"start": "firebase emulators:exec --only firestore \"npm run dev:appandtest\"",
"dev:appandtest": "concurrently -n \"app,test\" -c \"bgYellow.black,bgWhite.black\" \"npm:dev:app\" \"npm:dev:test\"",
"dev:app": "webpack-dev-server --config build/webpack.dev.js",
"dev:test": "npx cypress open",
The local server runs on port 9000 and the firebase emulator on port 8080.
After things are running, if i access the app from a normal browser everything is fine as this screen shows.
normal
Then i tried to run a basic cypress test with this code
describe('The Home Page', function () {
it('successfully loads', function () {
cy.visit('/');
});
});
And i got the errors messages below:
[2019-12-14T15:29:24.725Z] #firebase/firestore: Firestore (6.6.2): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
error.ts:166 Uncaught (in promise) FirebaseError: Failed to get document because the client is offline.
at new FirestoreError (http://localhost:9000/bundle.js:11739:149)
at Object.next (http://localhost:9000/bundle.js:16734:8)
at next (http://localhost:9000/bundle.js:16725:4704)
at http://localhost:9000/bundle.js:16430:411
I took a screenshot also:
buggy
I tried to research answers but wasn't able to find one. Thanks in advance for any help.
The solution to this problem, at least for now, is to enable experimentalForceLongPolling, like this:
// NOTE: do NOT put this in production.
firebase.firestore().settings({ experimentalForceLongPolling: true })
Important: this is an experimental feature and you should put it in some conditional checks with environment variables. You should not use this in production environment.
The reason for this is best described here:
The default behavior of Firestore's web SDK is to make use of WebChannel's streaming mode. The client makes what looks like an XHR, but then the server will hold the response open for 60 seconds and send as many server-initiated responses as it can during that time window.
The experimentalForLongPolling option forces the server to send only a single response per request.
And here:
That is the same workaround we are using in cypress. I think the underlying problem is that Cypress is intercepting all network traffic so it can monitor and sometimes mock. However, the webchannel protocol used by firestore has multiple replies over the same http request. The Cypress code cannot handle this and will only forward the first reply and ignore the rest.
In the v9 new API you can't rely on
provideFirebaseApp(() => initializeApp(environment.firebase)),
where environment.firebase includes { experimentalAutoDetectLongPolling: true }.
Instead you have to explicitly do this in the provideFirestore method.
provideFirestore(() => {
let firestore;
if (environment.useEmulators) {
// bug: experimentalAutoDetectLongPolling not picked up via `getFirestore`
const app = initializeApp(environment.firebase)
firestore = initializeFirestore(app, {
experimentalAutoDetectLongPolling: true
})
connectFirestoreEmulator(firestore, 'localhost', 8080)
} else {
firestore = getFirestore();
}
return firestore;
}),
After running the following line in node-js:
import * as admin from "firebase-admin";
import * as serviceAccount from "../../firebase_service_account_key.json";
const app = admin.initializeApp({
credential: admin.credential.cert(serviceAccount as any),
databaseURL: "https://my-app-path.firebaseio.com"
});
admin.messaging().send({
token: "known-good-token",
notification: {
title: "Test Push Note",
body: "Here is some text"
}
});
I'm getting the error:
Error: Auth error from APNS or Web Push Service
Raw server response:
"{
"error":{
"code":401,
"message":"Auth error from APNS or Web Push Service",
"status":"UNAUTHENTICATED",
"details"[
{
"#type":"type.googleapis.com/google.firebase.fcm.v1.FcmError",
"errorCode":"THIRD_PARTY_AUTH_ERROR"
},
{
"#type":"type.googleapis.com/google.firebase.fcm.v1.ApnsError",
"statusCode":403,
"reason":"InvalidProviderToken"
}
]
}
}"
I've added an "APNs Authentication Key" to my ios project under the Settings > Cloud Messaging section of Firebase. I've also properly downloaded and imported my service account json file.
In terms of research, I've tried looking up the errors.
For the InvalidProviderToken error, this answer seems to indicate I'm using an old token. This is totally possible, but the logs on my app and database appear to match, so it seems off.
As for the THIRD_PARTY_AUTH_ERROR, google gave me no hits. The closest thing I found was this, and the following text might be the culprit (EDIT: it's not the issue):
auth/unauthorized-domain
Thrown if the app domain is not authorized for OAuth operations for your Firebase project. Edit the list of authorized domains from the Firebase console.
Does anyone have anymore details on this error which might help me get to the bottom of it?
This error arises if your app setup for iOS has an error in any one of the following:
Found in Settings > General > Your Apps > iOS Apps:
App Store ID
Bundle ID
Team ID
When adding an APNs key (Uploading to Cloud Messaging > APNs Authentication Key):
Team ID (should auto set based off ios app info above)
Key Id (often is in the name of the key, best to grab when creating it)
Everything worked for me the other day, so all setup was fine. But today I got this error.
Here's what solved it for me:
Revoked APNs Key
Created new one and downloaded it
Deleted old one and Uploaded it to Firebase Dashboard / Settings /
Cloud Messaging
Gone to Settings / Service Accounts and generated new private key
Added it to my Cloud Functions project (renamed it to
service-account.json in my case)
Saved the files and deployed the functions: firebase deploy --only
functions
Did you call admin.initializeApp()?
There are many official samples.
See:
https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js#L20
https://github.com/firebase/functions-samples/tree/master/fcm-notifications
https://github.com/firebase/functions-samples
https://github.com/firebase/quickstart-js/tree/master/messaging
I had the same issue. The culprit was lowercase APNs teamId. Changing it to capital solved it.
Double check if you have uploaded your apns key on firebase console. That was my case.
I'm currently working on a project using the Firebase Admin Go SDK to handle auth and to use the real time database. The project works correctly when I run it locally (by just running go run main.go). When I run it in Minikube via a docker image (or GKE, I've tested both) I get this error whenever I try to make any Firestore calls:
transport: authentication handshake failed: x509: certificate signed by unknown authority
Here is the code I'm using on the server to make the call to the DB:
// Initialize the app
opt := option.WithCredentialsFile("./serviceAccountKey.json")
app, err := firebase.NewApp(context.Background(), nil, opt)
// This is the first call I attempt to make, and where the error is thrown
// Create the client
client, err := app.Firestore(context.Background())
iter := client.Collection("remoteModels").Documents(context.Background())
snaps, err := iter.GetAll()
if err != nil {
logger.Log.Warn("Error getting all remoteModels")
fmt.Println(err)
return err
}
And here is my Dockerfile that adds the service account key Firebase provided me from the console:
FROM scratch
ADD main /
ADD serviceAccountKey.json /
EXPOSE 9090
ENTRYPOINT ["/main", "-grpc-port=9090", "-http-port=9089", "-env=prod"]
I can't find anything in the documentation about running in Kubernetes.
Is there anything I need to do to be able to connect to Firestore from Kubernetes?
If you are using alpine based images try running apk add ca-certificates it looks like a tls error.
Install ca certificates, it should resolve the issue