How to enable basic authentication for web server using gulp browser-sync? - basic-authentication

Our web application is consuming a REST API that requires Basic Authentication.
I want to write the browser-sync gulp task like this:
gulp.task('browser-sync', function() {
browserSync({
server: {
baseDir: './'
},
authenticate: 'authentication-token-here-djfhjsdfjsgdf'
});
});
How might this be configured?

You can do it easily with the middleware option that processes each request:
gulp.task('browser-sync', function() {
browserSync({
server: {
baseDir: './'
},
middleware: [
function(req, res, next) {
const user = 'user';
const pass = 'pass';
let authorized = false;
// See if authorization exist in the request and matches username/password
if (req.headers.authorization) {
const credentials = new Buffer(req.headers.authorization.replace('Basic ', ''), 'base64').toString().split(/:(.*)/)
if (credentials[0] === user && credentials[1] === pass) {
authorized = true;
}
}
if (authorized) {
// Proceed to fulfill the request
next();
} else {
// Authorization doesn't exist / doesn't match, send authorization request in the response header
res.writeHead(401, {'WWW-Authenticate': 'Basic realm="Authenticate"'})
res.end();
}
}
]
});
});

Related

cloud run api service response broken when I use firebase rewrites

The firebase Sveltekit client app and server api use a google cloud run hosting container. This works fine when I use the cloud run url: https://app...-4ysldefc4nq-uc.a.run.app/
But when I use firebase rewriting the client works fine using: https://vc-ticker.web.app/... but receives 502 and 504 responses from the API service. The cloud run log does not show any errors, receives the client fetch POST request and returns a Readablestream response.
But this API service response stream never arrives when using rewrites.
firebase.json
{
"hosting": {
"public": "public", !! NOT used, cloud run hosts the app
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"run": {
"serviceId": "vc-ticker-app",
"region": "us-central1"
}
}
]
}
}
+page.svelte client API request:
const logging = true;
const controller = new AbortController();
let reader = null;
const signal = controller.signal;
async function streamer(params) {
console.log("stream with logging:", logging, JSON.stringify(params));
try {
const response = await fetch("api/my-ticker", {
method: "POST",
body: JSON.stringify(params),
headers: {
"content-type": "application/json",
},
signal: signal,
});
const stream = response.body.pipeThrough(new TextDecoderStream("utf-8"));
reader = stream.getReader();
while (true) {
const { value, done } = await reader.read();
if (done || response.status !== 200) {
console.log("done response", response.status, done, value);
await reader.cancel(`reader done or invalid response: ${response.status}`);
reader = null;
break;
}
// response ok: parse multi json chunks => array => set store
const quotes = {};
JSON.parse(`[${value.replaceAll("}{", "},{")}]`).forEach((each, idx) => {
quotes[each.id] = [each.price, each.changePercent];
console.log(`quote-${idx}:`, quotes[each.id]);
});
positions.set(quotes);
}
} catch (err) {
console.log("streamer exception", err.name, err);
if (reader) {
await reader.cancel(`client exception: ${err.name}`);
reader = null;
}
}
}
$: if ($portfolio?.coins) {
const params = {
logging,
symbols: Object.values($portfolio.symbols),
};
streamer(params);
}
onDestroy(async () => {
if (reader) await reader.cancel("client destroyed");
controller.abort();
console.log("finished");
});
I use the Sveltekit adapter-node to build the app.
With rewrite rules, you can direct requests that match specific patterns to a single destination.Check your firebase.json file and verify if the rewrite configuration in the hosting section has the redirect serviceId name same as that from the deployed container image,as per below example
"hosting": {// ...
// Add the "rewrites" attribute within "hosting"
"rewrites": [ {
"source": "/helloworld",
"run": {
"serviceId": "helloworld", // "service name" (from when you [deployed the container image][3])
"region": "us-central1" // optional (if omitted, default is us-central1)
}
} ]
}
It is important to note that Firebase Hosting is subject to a 60-second request timeout. If your app requires more than 60 seconds to run, you'll receive an HTTPS status code 504 (request timeout). To support dynamic content that requires longer compute time, consider using an App Engine flexible environment.
You should also check the Hosting configuration page for more details about rewrite rules. You can also learn about the priority order of responses for various Hosting configurations.
I made it work with an external link to the cloud run api service (cors).
But I still do not understand why It can't be done without cors using only firebase rewrites.
+page.svelte client API request update:
Now using GET and an auth token to verify the api request on the endpoint server
const search = new URLSearchParams(params);
const apiHost = "https://fs-por....-app-4y...q-uc.a.run.app/api/yahoo-finance-streamer";
const response = await fetch(`${apiHost}?${search.toString()}`, {
method: "GET",
headers: {
"auth-token": await getIdToken(),
},
signal: signal,
});
And a handle hook to verify the auth token and handle cors:
const logging = true;
const reqUnauthorized = { status: 403, statusText: 'Unauthorized!' };
/** #type {import('#sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
let response;
if (event.request.method !== "OPTIONS") {
if (event.url.pathname.startsWith('/api')) {
const authToken = event.request.headers.get("auth-token")
const { error = null, decodedToken } = await decodeIdToken(logging, authToken)
if (error) return new Response(error.message, reqUnauthorized);
if (verifyUser(logging, decodedToken) === false) {
return new Response(`user auth failed for: ${decodedToken.email}`, reqUnauthorized);
}
}
response = await resolve(event);
} else { // handle cors preflight OPTIONS
response = new Response("", { status: 200 });
}
response.headers.append('Access-Control-Allow-Headers', "*");
response.headers.append('Access-Control-Allow-Origin', "*");
return response;
}
From firebase support:
I got an answer from the engineering team. Unfortunately Firebase Hosting does not support streaming responses at the moment. I’ve created a feature request so they will consider implementing it.
Please be informed that submitting a feature request doesn’t guarantee that it will be implemented. Keep an eye on the release notes.
I realize that this is not the answer you expected from me, but unfortunately there is nothing I can do about it.

403 The caller does not have permission for Firebase Management API addFirebase

I want to add Firebase project through Firebase Management Api. So for that. I made project on Google Cloud Platform console. And created service account with permission as a owner.
I tried to read and create project throw google api explorer for addFirebase and it works. But when i try to do the same through my code it read availableProject successfully and give output as
{
"projectInfo": [
{
"project": "projects/firebase-api-238012",
"displayName": "Firebase-Api"
}
]
}
but when i try to add project it give me this error
{
"error": {
"code": 403,
"message": "The caller does not have permission",
"status": "PERMISSION_DENIED"
}
}
I don't know why its is not creating project. What other permission it needs. And why it allowed to me read available projects first.
here is how i am trying to add my project.
jwt.js
const { google } = require('googleapis');
var serviceAccountJwt = require('./Firebase-Api-b0e41b85ad44.json');
exports.connect = async () => {
return new Promise((resolve, reject) => {
// scope is based on what is needed in our api
const scope = ['https://www.googleapis.com/auth/firebase', 'https://www.googleapis.com/auth/cloud-platform'];
// create our client with the service account JWT
const { client_email, private_key } = serviceAccountJwt;
const client = new google.auth.JWT(client_email, null, private_key, scope, null);
// perform authorization and resolve with the client
return client.authorize((err) => {
if (err) { reject(err) }
else {
resolve(client)
};
});
});
}
index.js file
const { google } = require('googleapis');
const request = require('request');
const { connect } = require('./jwt');
const availableProjects = 'https://firebase.googleapis.com/v1beta1/availableProjects';
async function getAccessToken() {
let client = await connect();
let accessToken = await client.getAccessToken();
let res = await getProjects(accessToken.token)
}
getAccessToken().catch(err => {
console.log(JSON.stringify(err))
})
const bodys = {
"timeZone": "America/Los_Angeles",
"locationId": "asia-south1",
"regionCode": "US"
}
async function getProjects(accesstoken) {
let options = {
url: availableProjects,
headers: {
'Authorization': 'Bearer ' + accesstoken,
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}
return request(options, async function (err, res) {
if (err) {
console.error(err + " error");
} else {
//here it gives successful output
console.log("response")
console.log(res.body);
let bodyJson = JSON.parse(res.body);
let projectName = bodyJson.projectInfo[0].project;
console.log(projectName)
await addProject(accesstoken, projectName)
return res.body;
}
});
}
async function addProject(accesstoken, projecctID) {
fbUrl = getAddFBUrl(projecctID);
let options = {
url: fbUrl,
headers: {
'Authorization': 'Bearer ' + accesstoken,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body:JSON.stringify(bodys)
}
return request.post(options, function (err, res) {
if (err) {
console.error(err + " error");
} else {
//here in response out put as permission denied 403
console.log("response")
console.log(res.body);
console.log(JSON.stringify(res));
return res.body;
}
});
}
function getAddFBUrl(projectId) {
return 'https://firebase.googleapis.com/v1beta1/' + projectId +
':addFirebase';
}
i found one similar question to this. But it didn't helped me to resolve my issue which is here
AskFirebase
From the Firebase REST reference: Method: projects.addFirebase
To call projects.addFirebase, a member must be an Editor or Owner for
the existing GCP Project. Service accounts cannot call
projects.addFirebase.
Update:
To call projects.addFirebase, a project member or service account must have the following permissions (the IAM roles of Editor and Owner contain these permissions): firebase.projects.update, resourcemanager.projects.get, serviceusage.services.enable, and serviceusage.services.get.
https://firebase.google.com/docs/projects/api/reference/rest/v1beta1/projects/addFirebase
I'm not sure if my answer will be helpful for author of this question, but this if first two things all should check when facing 403 Error with Google Cloud APIs
0) Check configuration with gcloud
1) As mentioned before the first thing is to check the role of service account. You need Editor/Owner usually.
https://cloud.google.com/iam/docs/understanding-roles
https://console.cloud.google.com/iam-admin
2) The second one is to check if API enabled for project at all.
Also when creating a key check it for correct service account.
For someone who's just get started like me, this thing maybe helpful. When I seted up database, I choose Start in locked mode instead of Start in test mode. Therefore, I can't read or write :((. For beginner, just set everything in test mode. Hope it helpful.
https://i.stack.imgur.com/nVxjk.png
Your problem means that your project is not linked with your firebase account which means you have to login with your firebase account. Than you will have the permission
type cd functions in your firebase project directory
type firebase login
login with the Gmail which is connected with your firebase account
It'll work

S3 bucket with credentials error

I'm having trouble using the meteor slingshot component with the S3 with temporary AWS Credentials component. I keep getting the error Exception while invoking method 'slingshot/uploadRequest' InvalidClientTokenId: The security token included in the request is invalid.
Absolutely no idea what I'm doing wrong. If I use slingshot normally without credentials it works fine.
import { Meteor } from 'meteor/meteor';
import moment from 'moment';
const cryptoRandomString = require('crypto-random-string');
var AWS = require('aws-sdk');
var sts = new AWS.STS();
Slingshot.createDirective('UserProfileResumeUpload', Slingshot.S3Storage.TempCredentials, {
bucket: 'mybuckname', // change this to your s3's bucket name
region: 'ap-southeast-2',
acl: 'private',
temporaryCredentials: Meteor.wrapAsync(function (expire, callback) {
//AWS dictates that the minimum duration must be 900 seconds:
var duration = Math.max(Math.round(expire / 1000), 900);
sts.getSessionToken({
DurationSeconds: duration
}, function (error, result) {
callback(error, result && result.Credentials);
});
}),
authorize: function () {
//Deny uploads if user is not logged in.
if (!this.userId) {
const message = 'Please login before posting files';
throw new Meteor.Error('Login Required', message);
}
return true;
},
key: function () {
return 'mydirectory' + '/' + cryptoRandomString(10) + moment().valueOf();
}
});
Path: Settings.json
{
"AWSAccessKeyId": "myAWSKEYID",
"AWSSecretAccessKey": "MyAWSSeceretAccessKey"
}
I've done it in server side like this :
Slingshot.createDirective("UserProfileResumeUpload", Slingshot.S3Storage, {
AWSAccessKeyId: Meteor.settings.AWS.AccessKeyId,
AWSSecretAccessKey: Meteor.settings.AWS.SecretAccessKey,
bucket: 'mybuckname', // change this to your s3's bucket name
region: 'ap-southeast-2',
acl: 'private',
...
}
and in settings.json
{
"AWS": {
"AccessKeyId": "myAWSKEYID",
"SecretAccessKey": "MyAWSSeceretAccessKey"
}
}

Meteor HTTP.POST call on same machine (for testing)

I have created a server side route (using iron-router). Code is as follows :
Router.route( "/apiCall/:username", function(){
var id = this.params.username;
},{ where: "server" } )
.post( function(req, res) {
// If a POST request is made, create the user's profile.
//check for legit request
console.log('post detected')
var userId = Meteor.users.findOne({username : id})._id;
})
.delete( function() {
// If a DELETE request is made, delete the user's profile.
});
This app is running on port 3000 on my local. Now I have created another dummy app running on port 5000. Frrom the dummy app, I am firing a http.post request and then listening it on the app on 3000 port. I fire the http.post request via dummy app using the below code :
apiTest : function(){
console.log('apiTest called')
HTTP.post("http://192.168.1.5:3000/apiCall/testUser", {
data: [
{
"name" : "test"
}
]
}, function (err, res) {
if(!err)
console.log("succesfully posted"); // 4
else
console.log('err',err)
});
return true;
}
But I get the following error on the callback :
err { [Error: socket hang up] code: 'ECONNRESET' }
Not able to figure out whats the problem here.
The server side route is successfully called, but the .post() method is not being entered.
Using meteor version 1.6
192.168.1.5 is my ip addr
Okay so if I use Router.map function, the issue is resolved.
Router.map(function () {
this.route("apiRoute", {path: "/apiCall/:username",
where: "server",
action: function(){
// console.log('------------------------------');
// console.log('apiRoute');
// console.log((this.params));
// console.log(this.request.body);
var id = this.params.username;
this.response.writeHead(200, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
});
if (this.request.method == 'POST') {
// console.log('POST');
var user = Meteor.users.findOne({username : id});
// console.log(user)
if(!user){
return 'no user found'
}
else{
var userId = user._id;
}
}
});
});
It looks like the content type is not set the application/json. So you should do that...
Setting the "Content-Type" header in HTTP.call on client side in Meteor

How to Programmatically execute a node.js file from vb.net

I attempting to use ASP and vb.net to run a node.js file that returns a port number from Bungie's API. The issue I am having is that I can use this application locally, but not if I were to publish and deploy the code. The way I do this in Visual Studio is open the Package Manager Console, type in "node proxy.js," and copy the returned value (proxy.js is the node file I'm executing). Then I'd take that port number and add it into my code.
Obviously, this isn't the ideal way to execute this file. So, my question is: is there a way I can execute this file from the code behind in VB.NET? Or, is there a way that I can take my node file and execute it in VB? Here is the proxy.js file I'm working with:
var https = require('https');
var http = require('http');
var BUNGIE = {
host: 'www.bungie.net',
port: 443
};
function copyHeaderFrom(source) {
return function (target, k) {
if (typeof target[k.toLowerCase()] === 'undefined') {
target[k] = source[k];
}
return target;
};
}
http.createServer(function (req, res) {
var outboundData = {
method: req.method,
host: BUNGIE.host,
port: BUNGIE.port,
path: req.url,
headers: req.headers
};
console.log('req.headers.cookie:', req.headers.cookie);
outboundData.headers.host = BUNGIE.host;
console.log('outbound request ========================');
console.log(outboundData);
if (outboundData.method === 'OPTIONS') {
res.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': outboundData.headers['access-control-request-headers'] || ''
});
return res.end();
}
https.request(outboundData, function (bungieRes) {
var initialHeaders = {
'access-control-allow-origin': '*',
'origin': outboundData.headers.origin
};
res.writeHead(
bungieRes.statusCode,
Object.keys(bungieRes.headers).reduce(copyHeaderFrom(bungieRes.headers), initialHeaders)
);
bungieRes.pipe(res);
}).end();
}).listen(process.argv[2], function () {
console.log('Bungie Proxy Server running on port %s', this.address().port);
});

Resources