RPC datastore error when calling calling firebase golang library - firebase

I was following the tutorial for firestore in golang when the code gave me a weird error. It seems like I need to switch to native mode. https://cloud.google.com/datastore/docs/firestore-or-datastore this doc says I can if I have no writes to the database, but I did not find any documentation how to switch.
package main
import (
"context"
"fmt"
"log"
firebase "firebase.google.com/go"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)
func main() {
// Use a service account
ctx := context.Background()
conf := &firebase.Config{ProjectID: "secret"}
sa := option.WithCredentialsFile("../secret.json")
app, err := firebase.NewApp(ctx, conf, sa)
if err != nil {
log.Fatalln(err)
}
client, err := app.Firestore(ctx)
if err != nil {
log.Fatalln(err)
}
iter := client.Collection("jobs").Documents(ctx)
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("Failed to iterate: %v", err)
}
fmt.Println(doc.Data())
}
}
2020/08/26 21:46:38 Failed to iterate: rpc error: code = FailedPrecondition desc = The Cloud Firestore API is not available for Datastore Mode projects.
exit status 1

The Firestore API is not supported on Firestore in Datastore Mode. You have the following options:
Change the application to use the Datastore API.
If the application has not written to the store, then switch the store mode to Firstore in Native Mode. To switch modes, go to https://console.cloud.google.com/datastore/stats?project=projectID where projectID is your project ID and click the Switch to Native Mode button.
Move to a new project and select Firstore in Native Mode.

Related

How to get any data from my real-time database in firebase using Go

I have simple test database with one row (key - value) and I can't get any data from database, although I use docs for Go (admin sdk - https://firebase.google.com/docs/database/admin/start?authuser=0#go). I tried search but info how to use by Go is very small
Here my code.
Here my json-file.
In playground don't work, need use json-file and execute from code editor/IDE.
All I found was a couple of video tutorials from the firebase guys themselves where they show how to connect, but the same thing does not work for me. All other information is about how to use firebase via android, iphone and web (js).
package main
import (
"context"
"fmt"
"log"
firebase "firebase.google.com/go"
"google.golang.org/api/option"
)
type Data struct {
TypeClient string `json:"typeClient,omitempty"`
}
var responseData structs.Data
func main() {
ctx := context.Background()
// Initialize the app with a custom auth variable, limiting the server's
access
ao := map[string]interface{}{"uid": "my-service-worker"}
conf := &firebase.Config{
DatabaseURL: "https://test-v06f06-default-rtdb.firebaseio.com",
AuthOverride: &ao,
}
// Fetch the service account key JSON file contents
opt := option.WithCredentialsFile("./test-v06f06-firebase-adminsdk-1ze0m-
bbf3b57ef3.json")
app, err := firebase.NewApp(ctx, conf, opt)
if err != nil {
log.Fatalln("Error initializing app:", err)
}
// fmt.Printf("%T\n", app)
client, err := app.Database(ctx)
if err != nil {
log.Fatalln("Error initializing database client:", err)
}
// fmt.Printf("%T\n", client)
// The app only has access as defined in the Security Rules
ref := client.NewRef("/admin_permission")
// fmt.Printf("%T\n", ref)
// Get data
if err := ref.Get(ctx, &responseData); err != nil {
log.Fatalln("Error reading from database:", err)
}
fmt.Println("Client Type -", responseData)}
In last row where print I get empty variable. The data is simply not written to the variable.
Help if you can. Thanks in advance.
screenshot tiny db

How to verify if Google Firebase admin SDK credentials are correct

Is there any way to verify if Firebase Admin SDK Credentials is correct when initializing app with below code?
ctx := context.Background()
opt := option.WithCredentialsFile("path/to/firebase-admin-sdk-cred.json")
app, err := firebase.NewApp(ctx, nil, opt)
if err != nil {
return nil, err
}
Because I seem to not get any error when I tried to intentionally put the wrong credentials. I check the implementation of the firebase.NewApp() but it seems it only throw error when there is no config. Below is the code of firebase.NewApp()
func NewApp(ctx context.Context, config *Config, opts ...option.ClientOption) (*App, error) {
o := []option.ClientOption{option.WithScopes(internal.FirebaseScopes...)}
o = append(o, opts...)
if config == nil {
var err error
if config, err = getConfigDefaults(); err != nil {
return nil, err
}
}
pid := getProjectID(ctx, config, o...)
ao := defaultAuthOverrides
if config.AuthOverride != nil {
ao = *config.AuthOverride
}
return &App{
authOverride: ao,
dbURL: config.DatabaseURL,
projectID: pid,
serviceAccountID: config.ServiceAccountID,
storageBucket: config.StorageBucket,
opts: o,
}, nil
}
so Is there any way to check if the credentials is valid during the initialization of Firebase Admin(app) instance because it seems catching error isn't the solution here?
I think the answer is in the documentation:
Some use cases require you to create multiple apps at the same time.
For example, you might want to read data from the Realtime Database of
one Firebase project and mint custom tokens for another project. Or
you might want to authenticate two apps with separate credentials. The
Firebase SDK allows you create multiple apps at the same time, each
with their own configuration information.
Source
I assume the only way to check credentials is invoke an Auth method for example:
client, err := app.Auth(context.Background())
I ended up using google.golang.org/api/transport to force the validation and fail fast
// Check if credential is correct
_, err = transport.Creds(ctx, opt)
if err != nil {
return nil, err
}

How to verify Firebase ID token on App Engine in Go

The Firebase documentation, Verify ID Tokens, explains how to Verify ID tokens using the Firebase Admin SDK.
But first the Firebase Admin SDK must be setup. After installing the Go Admin SDK, with go get firebase.google.com/go it must be initialized (I assume this code is placed in the HTTP server main function):
app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
Question #1: I have no context in my App Engine app because in Migrating from the App Engine Go SDK it states:
Use request.Context() or your preferred context instead of using
appengine.NewContext.
AFACT the request.Context() is available from within an HTTP Handler Function. See the HandleFunc example on golang.org. Using the request context would require initializing the Firebase SDK for every HTTP request!
Question #2: How can I get a reference to my Firebase app from within an HTTP handler?
func main() {
// ------> Initialize the Firebase Go SDK. <------ \\
app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
// My usual boilerplate for App Engine.
http.HandleFunc("/api/foo", fooHandler)
http.HandleFunc("/api/bar", barHandler)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
log.Printf("Defaulting to port %s", port)
}
log.Printf("Listening on port %s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal(err)
}
}
There are some very useful looking functions in the firebase/firebase-admin-go repo, but without a context (for the HTTP request?) and no way to share app with my HTTP handlers, I can't work-out how to use them:
// ==================================================================
// https://firebase.google.com/docs/auth/admin/verify-id-tokens
// ==================================================================
func verifyIDToken(ctx context.Context, app *firebase.App, idToken string) *auth.Token {
// [START verify_id_token_golang]
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
token, err := client.VerifyIDToken(ctx, idToken)
if err != nil {
log.Fatalf("error verifying ID token: %v\n", err)
}
log.Printf("Verified ID token: %v\n", token)
// [END verify_id_token_golang]
return token
}
As treethought pointed out, context is of little importance for you. You can use context.Background().
The context you are passing to firebase API is not the context you are interested in. That's the context used to goroutines management.
What you need in fact is a context that is provided by the http server you are using. You use that server's context to get the data from the request, parse it, and pass it as idToken.
For example, if you use gin, this is usually achieved by adding middleware that has a contract:
return func(c *gin.Context) {
....
c.Next()
}
Inside it you use your firebase app/client to call verifyIDToken and then decide if you want to chain the request further to your handlers(c.Next()) or abort(c.Abort())
You need to encapsulate the state, in our case firebase client, inside that middleware so you create it as an object/struct with a method that returns a function with the signature specified above.
Here's an example.
I didn't need the flexibility provided by a separate unAuthorized function, and also I store the token inside the cookie, so in my case it became:
package middleware
import (
"bytes"
"context"
"encoding/json"
firebase "firebase.google.com/go/v4"
"firebase.google.com/go/v4/auth"
"github.com/gin-gonic/gin"
"io/ioutil"
"log"
"net/http"
"strings"
)
type firebaseAuthMiddleware struct {
client *auth.Client
}
func CreateFirebaseMiddleware() *firebaseAuthMiddleware {
app := initFirebaseAppDefault(context.Background())
authClient := createAuthClient(context.Background(), app)
return &firebaseAuthMiddleware{
client: authClient,
}
}
func (fam *firebaseAuthMiddleware) FirebaseAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if strings.HasSuffix(c.Request.URL.Path, "/query") {
idTokenCookie, err := c.Request.Cookie("FIREBASE_ID_TOKEN")
if idTokenCookie == nil || err != nil {
buildUnauthorizedResponse(c)
return
}
var objmap graphQLMessage
var bodyBytes []byte
if c.Request.Body != nil {
bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
}
// Restore the io.ReadCloser to its original state
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
json.Unmarshal(bodyBytes, &objmap)
//stringBody := string(body)
token, err := fam.client.VerifyIDToken(context.Background(), idTokenCookie.Value)
if err != nil {
buildUnauthorizedResponse(c)
return
}
if objmap.Variables["userId"] != token.UID {
buildUnauthorizedResponse(c)
return
}
}
c.Next()
}
}
func buildUnauthorizedResponse(c *gin.Context) {
c.JSON(http.StatusUnauthorized, gin.H{
"status": http.StatusUnauthorized,
})
c.Abort()
}
type graphQLMessage struct {
Variables map[string]string
Query string
OperationName string
}
func initFirebaseAppDefault(ctx context.Context) *firebase.App {
app, err := firebase.NewApp(ctx, nil)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
return app
}
func createAuthClient(ctx context.Context, app *firebase.App) *auth.Client {
client, err := app.Auth(ctx)
if err != nil {
log.Fatalf("error getting Auth client: %v\n", err)
}
return client
}
And then register the middleware in main:
package main
import (
"context"
"errors"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
mw "github.com/wnd-engineering/storeback/middleware"
"log"
"runtime/debug"
)
func main() {
r := gin.Default()
r.Use(mw.CreateFirebaseMiddleware().FirebaseAuthMiddleware())
r.POST("/query", graphqlHandler())
r.GET("/", playgroundHandler())
....
}
I think you should be able to just create a new context context.Background() for example.

Initializing Firebase Admin in App Engine standard Golang

I'm on golang app engine standard. I keep getting a 403 error when I'm using a firebase. Here is the code I'm using for passing credentials for firebase. What is this api key? What am I doing wrong?
Error:
googleapi: Error 403: The request is missing a valid API key., forbidden
credJSON := []byte("{...json from firebase console...}")
creds, err := google.CredentialsFromJSON(ctx, credJSON, "https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/datastore",
"https://www.googleapis.com/auth/devstorage.full_control",
"https://www.googleapis.com/auth/firebase",
"https://www.googleapis.com/auth/identitytoolkit",
"https://www.googleapis.com/auth/userinfo.email")
if err != nil {
return err
}
ops = append(ops, option.WithCredentials(creds))
fbApp, err := fb.NewApp(ctx, &fb.Config{ProjectID: projectID}, ops...)
Turns out if you pass nil for config. The library figures out credentials on Google Cloud. So, here is the code:
fbApp, err := fb.NewApp(ctx, nil, ops...)
if err != nil {
return nil, err
}

How to authenticate to Google Cloud Printing

I'm making an app that needs to send printing jobs through Google Cloud Printing to two printers owned by me (i.e., the printers are always the same one and does not belong to the user). I've set up the printers with Google Cloud Printing and it is now accessible from my Google Account.
Now, how can I access this account's printers through the API? I have found some documentation here that says that I need to authenticate myself when making requests. It seems to me that the authentication should be done with OAuth2. But the instructions on how to do that are lacking for a beginner. I've gotten so far as to getting my OAuth client ID and secret (step 1 in the OAuth link). But for step 2, I have no idea what to do.
It says:
Before your application can access private data using a Google
API, it must obtain an access token that grants access to that API. A
single access token can grant varying degrees of access to multiple
APIs.
But doesn't explain how to obtain this access token. I looked at this SO question where OP seems to have been able to get this access token, but I can't understand how he did it.
Could someone please explain how to get an access token to use with Google Cloud Printing? Or a good resource which explains how?
PS. The printing functionality is triggered by a firebase function. Would this help us get the access token, considering firebase is also made by Google?
I ran into the same issue and came up with this two-step solution:
Create an OAuth2 client in your Google Cloud Console as described here
and download its client credentials from the console and copy & past its json content to credJSON in the code snippet below.
Run the code below.
Follow the auth link and authorize your OAuth2 client to access Googel Cloud Printers with your Google account.
Copy & paste the auth code to the script
Once you obtained a refresh token make sure to store it in the variable refreshToken
Don't forget to update the proxy name.
package main
import (
"context"
"fmt"
"log"
"github.com/google/cloud-print-connector/gcp"
"github.com/google/cloud-print-connector/lib"
"github.com/google/uuid"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
var (
credJSON = ``
refreshToken = ""
// Find the proxy in the Advanced Details of your printer at https://www.google.com/cloudprint#printers
proxy = "HP"
)
func main() {
// Obtain the OAuth config
config, err := google.ConfigFromJSON([]byte(credJSON), gcp.ScopeCloudPrint)
if err != nil {
log.Fatalf("Failed to obtain OAuth config: %v", err)
}
// If no request token is present, obtain a new one
if refreshToken == "" {
// Get the auth link
authLink := config.AuthCodeURL(uuid.New().String(), oauth2.AccessTypeOffline)
log.Printf("Follow the link to obtain an auth code: %s", authLink)
fmt.Printf("Paste your auth code here: ")
var code string
fmt.Scanln(&code)
// Get a token form the auth code
token, err := config.Exchange(context.Background(), code, oauth2.AccessTypeOffline)
if err != nil {
log.Fatalf("Failed to obtain OAuth token: %v", err)
}
if token.RefreshToken != "" {
refreshToken = token.RefreshToken
} else {
refreshToken = token.AccessToken
}
log.Printf("Refresh token: %s", refreshToken)
}
// Connect to Google Cloud Print
jobCh := make(chan *lib.Job)
client, err := gcp.NewGoogleCloudPrint(lib.DefaultConfig.GCPBaseURL, refreshToken, refreshToken, proxy, config.ClientID, config.ClientSecret, config.Endpoint.AuthURL, config.Endpoint.TokenURL, lib.DefaultConfig.NativeJobQueueSize, jobCh, true)
if err != nil {
log.Fatalf("Failed to connect to GCP: %v", err)
}
// List all printers
printers, _, err := client.ListPrinters()
if err != nil {
log.Fatalf("Failed to list printers: %v", err)
}
for _, p := range printers {
log.Printf("Name: %s UUID: %s", p.Name, p.UUID)
}
}
Please refer to following documentation:
https://developers.google.com/identity/protocols/OAuth2ServiceAccount?authuser=1
I followed the same steps specified in the doc and was able to obtain the access token. First make Google Service Account, select furnish new private key. You ll have service account email addresss and private key. Using these credentials, you can obtain your access token. Below is the source code in Golang , this ll surely help you.
package main
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"net/http"
"encoding/json"
"bytes"
)
type MyCustomClaims struct {
Scope string `json:"scope,omitempty"`
jwt.StandardClaims
}
type Toke struct {
Access string `json:"access_token,omitempty"`
Type string `json:"token_type,omitempty"`
Expire string `json:"expires_in,omitempty"`
}
func main() {
key := []byte("<your private key>")
key1, _ := jwt.ParseRSAPrivateKeyFromPEM(key)
claims := MyCustomClaims{
"https://www.googleapis.com/auth/cloudprint",
jwt.StandardClaims{
IssuedAt: <currrent-epoch-time>, // eg 1234566000
ExpiresAt: <currrent-epoch-time + 3600>, // 3600 secs = 1hour, so expires in 1 hour, eg 1234569600
Issuer: "<your service account email>",
Audience: "https://www.googleapis.com/oauth2/v4/token",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
ss, err := token.SignedString(key1)
if err != nil {
fmt.Println(err)
}
fmt.Println(ss)
url := "https://www.googleapis.com/oauth2/v4/token"
any := "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + ss
a := []byte(any)
b := bytes.NewBuffer(a)
var tok Toke
req, err := http.NewRequest("POST", url, b)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
} else {
json.NewDecoder(resp.Body).Decode(&tok)
}
fmt.Println("----------- Access Token -----------------")
fmt.Println("Access: ", tok.Access)
}

Resources