Firebase Custom Authentication: Security of Firebase Secret in JavaScript - javascript

I would like to use Firebase Custom Authentication in my Angular app. This action is realy simple:
var FirebaseTokenGenerator = require("firebase-token-generator");
var tokenGenerator = new FirebaseTokenGenerator("<YOUR_FIREBASE_SECRET>");
var token = tokenGenerator.createToken({ uid: "uniqueId1", some: "arbitrary", data: "here" });
But there is a warning about security of Firebase Secret in the doc page:
Firebase JWTs should always be generated on a trusted server so that
the Firebase app secret which is needed to generate them can be kept
private.
I am wondering how can I keep my Firebase Secret private if everyone can view my JavaScript source code and read the Firebase Secret there? Am I missing something or there is no possibility to do this in JavaScript?

The code you quote is to be run on the your nodejs server (hence - server-side javascript).
The server component FirebaseTokenGenerator takes care for generating the token and sending it back to the JS client, after the client has authenticated to your server, with whatever method you want. That's why it's named custom authentication.

Related

Single flow: sign user in via Google oAuth AND grant offline/server access?

I'm trying to implement Google sign-in and API access for a web app with a Node.js back end. Google's docs provide two options using a combo of platform.js client-side and google-auth-library server-side:
Google Sign-In with back-end auth, via which users can log into my app using their Google account. (auth2.signIn() on the client and verifyIdToken() on the server.)
Google Sign-in for server-side apps, via which I can authorize the server to connect to Google directly on behalf of my users. (auth2.grantOfflineAccess() on the client, which returns a code I can pass to getToken() on the server.)
I need both: I want to authenticate users via Google sign-in; and, I want to set up server auth so it can also work on behalf of the user.
I can't figure out how to do this with a single authentication flow. The closest I can get is to do the two in sequence: authenticate the user first with signIn(), and then (as needed), do a second pass via grantOfflineAccess(). This is problematic:
The user now has to go through two authentications back to back, which is awkward and makes it look like there's something broken with my app.
In order to avoid running afoul of popup blockers, I can't give them those two flows on top of each other; I have to do the first authentication, then supply a button to start the second authentication. This is super-awkward because now I have to explain why the first one wasn't enough.
Ideally there's some variant of signIn() that adds the offline access into the initial authentication flow and returns the code along with the usual tokens, but I'm not seeing anything. Help?
(Edit: Some advice I received elsewhere is to implement only flow #2, then use a secure cookie store some sort of user identifier that I check against the user account with each request. I can see that this would work functionally, but it basically means I'm rolling my own login system, which would seem to increase the chance I introduce bugs in a critical system.)
To add an API to an existing Google Sign-In integration the best option is to implement incremental authorization. For this, you need to use both google-auth-library and googleapis, so that users can have this workflow:
Authenticate with Google Sign-In.
Authorize your application to use their information to integrate it with a Google API. For instance, Google Calendar. 
For this, your client-side JavaScript for authentication might require some changes to request
offline access:
$('#signinButton').click(function() {
auth2.grantOfflineAccess().then(signInCallback);
});
In the response, you will have a JSON object with an authorization code:
{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}
After this, you can use the one-time code to exchange it for an access token and refresh token.
Here are some workflow details:
The code is your one-time code that your server can exchange for its own access token and refresh token. You can only obtain a refresh token after the user has been presented an authorization dialog requesting offline access. If you've specified the select-account prompt in the OfflineAccessOptions [...], you must store the refresh token that you retrieve for later use because subsequent exchanges will return null for the refresh token
Therefore, you should use google-auth-library to complete this workflow in the back-end. For this,
you'll use the authentication code to get a refresh token. However, as this is an offline workflow,
you also need to verify the integrity of the provided code as the documentation explains:
If you use Google Sign-In with an app or site that communicates with a backend server, you might need to identify the currently signed-in user on the server. To do so securely, after a user successfully signs in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity of the ID token and use the user information contained in the token
The final function to get the refresh token that you should persist in your database might look like
this:
const { OAuth2Client } = require('google-auth-library');
/**
* Create a new OAuth2Client, and go through the OAuth2 content
* workflow. Return the refresh token.
*/
function getRefreshToken(code, scope) {
return new Promise((resolve, reject) => {
// Create an oAuth client to authorize the API call. Secrets should be
// downloaded from the Google Developers Console.
const oAuth2Client = new OAuth2Client(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// Generate the url that will be used for the consent dialog.
await oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope,
});
// Verify the integrity of the idToken through the authentication
// code and use the user information contained in the token
const { tokens } = await client.getToken(code);
const ticket = await client.verifyIdToken({
idToken: tokens.id_token!,
audience: keys.web.client_secret,
});
idInfo = ticket.getPayload();
return tokens.refresh_token;
})
}
At this point, we've refactored the authentication workflow to support Google APIs. However, you haven't asked the user to authorize it yet. Since you also need to grant offline access, you should request additional permissions through your client-side application. Keep in mind that you already need an active session.
const googleOauth = gapi.auth2.getAuthInstance();
const newScope = "https://www.googleapis.com/auth/calendar"
googleOauth = auth2.currentUser.get();
googleOauth.grantOfflineAccess({ scope: newScope }).then(
function(success){
console.log(JSON.stringify({ message: "success", value: success }));
},
function(fail){
alert(JSON.stringify({message: "fail", value: fail}));
});
You're done with the front-end changes and you're only missing one step. To create a Google API's client in the back-end with the googleapis library, you need to use the refresh token from the previous step.
For a complete workflow with a Node.js back-end, you might find my gist helpful.
While authentication (sign in), you need to add "offline" access type (by default online) , so you will get a refresh token which you can use to get access token later without further user consent/authentication. You don't need to grant offline later, but only during signing in by adding the offline access_type. I don't know about platform.js but used "passport" npm module . I have also used "googleapis" npm module/library, this is official by Google.
https://developers.google.com/identity/protocols/oauth2/web-server
https://github.com/googleapis/google-api-nodejs-client
Check this:
https://github.com/googleapis/google-api-nodejs-client#generating-an-authentication-url
EDIT: You have a server side & you need to work on behalf of the user. You also want to use Google for signing in. You just need #2 Google Sign-in for server-side apps , why are you considering both #1 & #2 options.
I can think of #2 as the proper way based on your requirements. If you just want to signin, use basic scope such as email & profile (openid connect) to identify the user. And if you want user delegated permission (such as you want to automatically create an event in users calendar), just add the offline access_type during sign in. You can use only signing in for registered users & offline_access for new users.
Above is a single authentication flow.

How to Get a valid access token for my API and Microsoft Graph from Azure Active Directory?

I am trying to set up a spa javascript app which logs the user into our azure active directory tenant and then retrieves profile information from microsoft graph and calls an azure function written in c# core (my API).
I have separate application registrations set up for my website and the api in azure active directory.
I'm using the MSAL.js library in the javascript spa website and I'm using the newer microsoft identity / v2.0 endpoints.
The SPA app signs into active directory as expected and is able to use the access token to make the call to graph for the profile information. In my azure function I validate the token and this fails with the error "IDX10511: Signature validation failed. Keys tried: ....."
Now if I remove Microsoft graph from the scopes when requesting a token I get a token that when passed to the azure function validates perfectly well but I can no longer retrieve profile data in the spa app?
How do I get both to work?
Its also worth noting that ive tested the tokens with jwt.io and it is also unable to verify the signature when graph is used.
Heres how I'm getting my token:
var msalConfig = {
auth: {
redirectUri: window.location.origin, // forces top level instead of specific login pages - fixes deep link issues.
clientId: "Client ID of the website app", //This is your client ID
authority:
"https://login.microsoftonline.com/my-tennant-guid" //This is your tenant info
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
const msalUserAgent = new Msal.UserAgentApplication(msalConfig);
var requestObj = {
scopes: ["User.Read", "api://MyApi/Access"]
};
//when the spa starts up I login using redirects
msalUserAgent.loginRedirect(requestObj);
//then before calling an api I request a token using this method
acquireTokenSilent() {
var promise = msalUserAgent.acquireTokenSilent(requestObj);
return promise;
},
Try specifying the scopes as scopes: ["User.Read"] in the acquireTokenSilent() function.
Since an access token is only valid for one API.
If you need two, call acquireTokenSilent twice with different scopes.
It's okay to specify scopes for two APIs when signing in, but not when getting tokens. A token has an audience that specifies the target API. So you can't use a token for one API against another. And that's why it's only valid for one API.

How long to crack HMAC sha256 digest private key given data

We've built a javascript module which can be embedded in third-party webpages. When this client module is initialized, it executes a transaction within our web application via a cross-site request.
The transaction consists of an external uuid, an email, and some additional meta properties. This message is signed with an HMAC sha256 digest, using our partner's private API key.
Ruby example:
data = {
uuid: "ABCAFAFDS",
email: "email#gmail.com",
meta: {}
}
private_key = "Qd9fe0y2ezqfae4Qj6At"
signature = OpenSSL::HMAC.hexdigest(
OpenSSL::Digest.new("sha256"),
private_key,
data.to_json
)
Within the third-party webpage, the javascript client is then initialized with the signature and the data:
new Client(signature, data).execute();
Initially, our plan was to allow the client to create a partial / incomplete transaction within our system and then require a subsequent back-end request via our REST API to confirm / finalize the transaction. Assuming that we can secure the front-end, however, it would be preferential to remove the back-end confirmation requirement.
Can we reasonably secure the client code using signed messages in this fashion? If the data and the signed message is available in the client, how difficult would it be for a bad actor to brute force the API private key length, given the length above?
most internet traffic has signed tokens on the client these days. All your gmail logins, facebook logins, etc do this so it is fairly standard. I'd recommend using an existing standard (and 3rd party library) rather than roll your own token though. This will let you leverage other people's expertise in this area.
JWT (json web token) is in common use and there are many libraries for working with JWT's. See https://jwt.io for more information.

Securely store a db password without setting up a service

Currently making an web application for our users to access and edit a database
We only have one connection to this database and can't make more, but we also can't give this one credential to any of our web app future users.
The idea is to create another security layer in the application using windows accounts
Pseudo code :
db = "dbconnection1"
dbuser = "admin1"
dbpassword = "123456"
enabledWinUsers = { "winUsr1", "winUsr2", "winUsr3" }
winusr = aplication.getWinUser()
if winusr in enabledWinUsers :
db.connect( db, dbuser, dbpassword )
The database logic and connection will all be done in Python, for now, and the app in Javascript/Jquery
Question is, is it possible to secure this code or the db credentials in any way without setting up a service like Node.js?
I do have full access to the server machine running the web app
Well you can encrypt the user credentials in your source code and decrypt them from your source code. Another option is to set the credentials as environment variables

passing javascript to python in GAE

I am planning to run an app in GAE that initializes the Facebook login sequence in Javascript and passes the access token to a Python script to do server-side work. The javascript looks something like this
// Additional init code here
FB.login(
function(response) {
if (response.authResponse) {
var access_token = FB.getAuthResponse()['accessToken'];
console.log('Access Token = '+ access_token);
What would be the easiest way to pass access_token to a Python class (say named fbProcessor) that is imported in main.py?
Since the javascript is executed on the front end (client's browser) you can't really "pass" the access_token to python. You COULD make a http (AJAX) request from javascript to your python app with the access_token.
I think that if you are making a request to python you could just use facebook's python SDK to retrieve the information (including access token) for the current FB authenticated user... IDK the benefits of either way
the python facebook SDK has google app engine example's
If you are using the module within a web application with the
JavaScript SDK, you can also use the module to use Facebook for login,
parsing the cookie set by the JavaScript SDK for logged in users. For
example, in Google AppEngine, you could get the profile of the logged
in user with:
user = facebook.get_user_from_cookie(self.request.cookies, key, secret)
if user:
graph = facebook.GraphAPI(user["access_token"])
profile = graph.get_object("me")
friends = graph.get_connections("me", "friends")
You can see a full AppEngine example application in
examples/appengine.

Categories

Resources