Terraform Firebase Web Application - firebase

I have some trouble with this terraform file I wrote to define a Firebase application in my org account:
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "3.86.0"
}
}
}
provider "google-beta" {
credentials = file("service-account-credentials.json")
project = var.gcp_project_id
region = var.region
zone = var.zone
}
resource "google_project" "default" {
provider = google-beta
project_id = var.gcp_project_id
name = "Optic OTP API"
org_id = var.gcp_organization_id
}
resource "google_firebase_project" "default" {
provider = google-beta
project = google_project.default.project_id
}
resource "google_firebase_web_app" "basic" {
provider = google-beta
project = google_project.default.project_id
display_name = "Optic OTP API"
depends_on = [google_firebase_project.default]
}
data "google_firebase_web_app_config" "basic" {
provider = google-beta
web_app_id = google_firebase_web_app.basic.app_id
}
resource "google_storage_bucket" "default" {
provider = google-beta
name = "firebase-optic-storage"
}
resource "google_storage_bucket_object" "default" {
provider = google-beta
bucket = google_storage_bucket.default.name
name = "firebase-config.json"
content = jsonencode({
appId = google_firebase_web_app.basic.app_id
apiKey = data.google_firebase_web_app_config.basic.api_key
authDomain = data.google_firebase_web_app_config.basic.auth_domain
databaseURL = lookup(data.google_firebase_web_app_config.basic, "database_url", "")
storageBucket = lookup(data.google_firebase_web_app_config.basic, "storage_bucket", "")
messagingSenderId = lookup(data.google_firebase_web_app_config.basic, "messaging_sender_id", "")
measurementId = lookup(data.google_firebase_web_app_config.basic, "measurement_id", "")
})
}
I followed the official terraform plugin documentation here
I’m using a Service Account created in the company GCP org within the Firebase Service Management Service Agent role:
But when I run terraform plan I get
Error when reading or editing Storage Bucket "firebase-optic-storage": googleapi: Error 403: XXX does not have storage.buckets.get access to the Google Cloud Storage bucket.
Even if the service account’s role has it!
$ gcloud projects get-iam-policy optic-web-otp
# returns
bindings:
- members:
- serviceAccount:XXX
role: roles/firebase.managementServiceAgent
- members:
- serviceAccount:XXX
role: roles/firebase.sdkAdminServiceAgent
- members:
- serviceAccount:XXX
role: roles/firebase.sdkProvisioningServiceAgent
- members:
- user:MY-EMAIL
role: roles/owner
etag:
version: 1
(The XXX is the right service account identifier)
Do you have some hints to check what is missing from my Service Account?

If the roles that you listed are the only ones that your account has - you lack roles that allow you to access Cloud Storage. Command you used to check the roles doesn't give you correct information.
Correct solution (described in this answer) would be to run this :
gcloud projects get-iam-policy <your project name> \
--flatten="bindings[].members" \
--format='table(bindings.role)' \
--filter="bindings.members:<your account name>"
If you don't see any of these roles:
roles/storage.objectAdmin
roles/storage.admin
roles/storage.objectCreator
described here you won't be able to create any buckets/objects.
In this case add these roles to your service account and try again.
For example:
gcloud projects add-iam-policy-binding optic-web-otp \
--member=user:my-user#example.com --role=roles/roles/storage.admin

Related

How to access google forms created by Forms API

I'm exploring the possibilities of using the Google Forms API to create Forms dynamically from my Node Express service.
After some trail and error I'm able to do a basic conversion of assessments from another system into Google forms.
But now I want to manually change the created forms and I don't know how I can access these. It's created using a service account. Can I give permissions to email addresses or something?
Accessing the Form
If you are not using Google Workspace you would need to manually share the file with the Gmail account.
Depending on which Version of Drive API you are using. You would need to use:
permissions:create
or
permissions:insert
This will allow you to create an option to directly share the file owned by the service account to another user with access to the Drive UI.
There is also a guide on how to utilize the "permission" for Drive V3 with a sample code for Node, it might give you an insight on how to follow it up:
/**
* Batch permission modification
* #param{string} fileId file ID
* #param{string} targetUserEmail username
* #param{string} targetDomainName domain
* #return{list} permission id
* */
async function shareFile(fileId, targetUserEmail, targetDomainName) {
const {GoogleAuth} = require('google-auth-library');
const {google} = require('googleapis');
// Get credentials and build service
// TODO (developer) - Use appropriate auth mechanism for your app
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/drive',
});
const service = google.drive({version: 'v3', auth});
const permissionIds = [];
const permissions = [
{
type: 'user',
role: 'writer',
emailAddress: targetUserEmail, // 'user#partner.com',
},
{
type: 'domain',
role: 'writer',
domain: targetDomainName, // 'example.com',
},
];
// Note: Client library does not currently support HTTP batch
// requests. When possible, use batched requests when inserting
// multiple permissions on the same item. For this sample,
// permissions are inserted serially.
for (const permission of permissions) {
try {
const result = await service.permissions.create({
resource: permission,
fileId: fileId,
fields: 'id',
});
permissionIds.push(result.data.id);
console.log(`Inserted permission id: ${result.data.id}`);
} catch (err) {
// TODO(developer): Handle failed permissions
console.error(err);
}
}
return permissionIds;
}
Reference
https://developers.google.com/drive/api/v2/reference/permissions/insert
https://developers.google.com/drive/api/v3/reference/permissions/create
Guide on how to manage sharing files: https://developers.google.com/drive/api/guides/manage-sharing#node.js

Amazon Sage Maker: How to authenticate AWS SageMaker End-Point Request

I have an aws sagemaker end-point which need to be called from .Net core client, I have used the AWS SDK that deals with SageMaker and provided the required credentials however, always it keeps saying :
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
var requestBody = "{'url':'"+"https://cdn.pixabay.com/photo/2018/05/28/22/11/message-in-a-bottle-3437294_960_720.jpg" + "'}";
var request = new Amazon.SageMakerRuntime.Model.InvokeEndpointRequest()
{
EndpointName = "CG-model-v1-endpoint",
ContentType = "application/json;utf-8",
Body = new MemoryStream(Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(requestBody)))
};
var awsClient = new AmazonSageMakerRuntimeClient(awsAccessKeyId: "XXXX", awsSecretAccessKey: "XXX", region: RegionEndpoint.EUCentral1);
try
{
var resposnse = await awsClient.InvokeEndpointAsync(request);
}
catch (Exception ex)
{
return ApiResponse<bool>.Create(false);
}
I found the error , it was simply because of the request content-type,it had to be application/json instead of application/json;utf-8

Using Firebase REST API with custom token failed with 403 forbidden [duplicate]

I'm migrating to the new database and 3.0 client libs. I'm updating the part which generates a custom auth token (on our server) to do a PATCH to update a resource in the Firebase DB.
These PATCH requests used to be made by our server to Firebase using admin claims based on this: https://www.firebase.com/docs/rest/guide/user-auth.htm
For the new DB, I'm generating the JWT token (using ruby-jwt) like this:
payload = {
aud: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
claims: custom_claims.merge({ admin: true }),
exp: now_seconds + (60 * 60), # Maximum expiration time is one hour
iat: now_seconds,
iss: service_account_email,
sub: service_account_email,
uid: uid
}
JWT.encode(payload, private_key, "RS256")
A PATCH request with this token to the Firebase DB fails with: Missing claim 'kid' in auth header.
In the new Firebase you need to directly use a Service Account to create administrative access credentials. Here is a Node.js snippet that shows how to make a REST call to the Database:
// key.json is a service account key downloaded from the Firebase Console
var key = require('./key.json');
var google = require('googleapis');
var request = require('request');
var DATABASE_URL = 'https://<databaseName>.firebaseio.com';
var jwtClient = new google.auth.JWT(key.client_email, null, key.private_key, [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/firebase.database'
]);
jwtClient.authorize(function(err, tokens) {
request({
url: DATABASE_URL + '/.json',
method: 'GET',
headers: {
'Authorization': 'Bearer ' + tokens.access_token
}
}, function(err, resp) {
console.log(resp.body);
});
});
To do the same in Ruby, you might take a look at the googleauth gem for fetching the access token using Service Account credentials.
Here is the equivalent of Michael Bleigh's answer using the ruby googleauth module:
require 'googleauth'
scopes = [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/firebase.database']
auth = ::Google::Auth.get_application_default(scopes)
auth_client = auth.dup
auth_client.sub = "service-account-email-here#yourapp.iam.gserviceaccount.com"
token = auth_client.fetch_access_token!
You will also need to set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your service account JSON file. the value for auth_client.sub comes from client_email in this JSON file.
Of course, as above, this is only valid in a server application you control.
Also, making the request to the firebase REST API is still an exercise for the reader.
references
https://developers.google.com/api-client-library/ruby/auth/service-accounts#authorizingrequests
https://developers.google.com/identity/protocols/application-default-credentials#whentouse

Firebase - create user on Node.js server

We have a large SPA using Firebase v2. We would like to upgrade to the new API, but we experience the following problem:
As the app is quite large, we have developed many integration tests, and for these tests we always need to reset the database and initialize it to a state, where some users exist. However, we found out there really is no such thing as creating a user on server anymore ( Firebase createUserWithEmailAndPassword method is undefined in node.js ), and we are quite unsure, how to upgrade the API and yet be able to reset and initialize the database from server.
Moreover, we are quite forced to do this upgrade, because we noticed that the Firebase v2, is still using the deprecated Graph API v2.0 for Facebook OAuth, and is not recommended for use after 8.8.2016. We understand that the Firebase v2 will probably not upgrade the calls to the Graph API, as the v2 is legacy. This, however, leaves us quite cornered for now.
Any help on this topic, please?
As of Firebase v3.3.0 you are able to create user accounts using Node, but the documentation isn't great on how to expose these methods.
In order to use the user management methods, you need to initialize an application in node using your Web API key, and not the Service Account config that is walked through in the setup guide.
// The Usual Service Account Init
// This will not contain any user management methods on firebase.auth()
this.app = firebase.initializeApp(
{
serviceAccount: 'path/to/serviceaccount/file.json',
databaseURL: 'https://mydbfb.firebaseio.com'
},
'MyAppName');
// Web Client Init in Node.js
// firebase.auth() will now contain user management methods
this.app = firebase.initializeApp(
{
"apiKey": "my-api-key",
"authDomain": "somedomain.firebaseapp.com",
"databaseURL": "https://mydbfb.firebaseio.com",
"storageBucket": "myfbdb.appspot.com",
"messagingSenderId": "SomeId"
},
'MyAppName');
You can grab your client api key from your Firebase console from the Web Setup guide
https://firebase.google.com/docs/web/setup
This is the only reference I could find that explicitly referenced the need to init with api key to get this to work.
https://groups.google.com/forum/#!msg/firebase-talk/_6Rhro3zBbk/u8hB1oVRCgAJ
Given below is a working example of creating Firebase user through Node.js
exports.addUser = function(req, res) {
var wine = req.body;
var email = req.body.email;
console.log(req.body);
var password = req.body.password;
var name = req.body.name;
console.log(“Creating user for -“+email+”-“+password);
var defaultAuth = admin.auth();
admin.auth().createUser({
email: email,
emailVerified: false,
password: password,
displayName: name,
disabled: false
})
.then(function(userRecord) {
console.log(“Created Firebase User successfully with id :”, userRecord.uid);
var wine = req.body;
wine.userId = userRecord.uid;
wine.timestamp = Date.now();
delete wine.password;
status = “201”;
var reply = JSON.stringify(wine);
db.collection(‘collname’, function(err, collection) {
collection.insert(wine, {safe:true}, function(err, result) {
if (err) {
wine.status = “200”;
wine.message = “An error occured”;
reply.set(‘status’,”201″);
res.status(201).send(wine);
} else {
console.log(‘Success: ‘ + JSON.stringify(result[0]));
status= “200”;
wine.status = “200”;
wine.message = “Account created Successfully”;
res.status(200).send(wine);
}
});
});
})
.catch(function(error) {
wine.message = “An error occured—“;
wine.status = “201”;
console.log(“User Creation onf Firebase failed:”, error);
res.status(201).send(wine);
});
}
For details you can see the following blog post
http://navraj.net/?p=53
Thanks

Using Custom Tokens to make REST requests to FB DB as an admin

I'm migrating to the new database and 3.0 client libs. I'm updating the part which generates a custom auth token (on our server) to do a PATCH to update a resource in the Firebase DB.
These PATCH requests used to be made by our server to Firebase using admin claims based on this: https://www.firebase.com/docs/rest/guide/user-auth.htm
For the new DB, I'm generating the JWT token (using ruby-jwt) like this:
payload = {
aud: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
claims: custom_claims.merge({ admin: true }),
exp: now_seconds + (60 * 60), # Maximum expiration time is one hour
iat: now_seconds,
iss: service_account_email,
sub: service_account_email,
uid: uid
}
JWT.encode(payload, private_key, "RS256")
A PATCH request with this token to the Firebase DB fails with: Missing claim 'kid' in auth header.
In the new Firebase you need to directly use a Service Account to create administrative access credentials. Here is a Node.js snippet that shows how to make a REST call to the Database:
// key.json is a service account key downloaded from the Firebase Console
var key = require('./key.json');
var google = require('googleapis');
var request = require('request');
var DATABASE_URL = 'https://<databaseName>.firebaseio.com';
var jwtClient = new google.auth.JWT(key.client_email, null, key.private_key, [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/firebase.database'
]);
jwtClient.authorize(function(err, tokens) {
request({
url: DATABASE_URL + '/.json',
method: 'GET',
headers: {
'Authorization': 'Bearer ' + tokens.access_token
}
}, function(err, resp) {
console.log(resp.body);
});
});
To do the same in Ruby, you might take a look at the googleauth gem for fetching the access token using Service Account credentials.
Here is the equivalent of Michael Bleigh's answer using the ruby googleauth module:
require 'googleauth'
scopes = [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/firebase.database']
auth = ::Google::Auth.get_application_default(scopes)
auth_client = auth.dup
auth_client.sub = "service-account-email-here#yourapp.iam.gserviceaccount.com"
token = auth_client.fetch_access_token!
You will also need to set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your service account JSON file. the value for auth_client.sub comes from client_email in this JSON file.
Of course, as above, this is only valid in a server application you control.
Also, making the request to the firebase REST API is still an exercise for the reader.
references
https://developers.google.com/api-client-library/ruby/auth/service-accounts#authorizingrequests
https://developers.google.com/identity/protocols/application-default-credentials#whentouse

Resources