Difference between using Cloud Endpoints vs using authenticated requests for data? - google-app-engine

I would like to make authenticated requests to a an App Engine app. I am having trouble setting up Cloud Endpoints(I have failed to follow the tutorial many times.) so I am thinking about an alternative to retrieve user data from the datastore. Can I just return a json object after a user makes a request to a specific URL?
For example:
I have a webapp called Library of Books. It's an awesome app that simply allows authenticated users to keep track of a list of their favourite books. This is the Book Entity:
The user can access his list of fav books by visiting /my-fav-books. When visiting this url, the user sends his cookies which would validate his authenticated session and the server could then return the user's list of books via a json object.
Could this work?

Related

How can I retrieve a user ID from Firestore via Flask backend (React frontend)?

I'm a newbie with authentication! I'm building a web app where users can log in, and the data shown in the web app is different for each user. I'm using Reactjs as my frontend with Firebase authentication. After a user logs into my web app, I'm storing their user ID (UID) and other information into Firestore. I have a collection usersCollection where each document is labelled with the UID. For the backend, I'm using Flask as mostly a REST API with a Postgres database, but I am not storing user credentials there (UID, password, etc.).
For some of my backend functions I need to change the output based on which user is signed in, but I'm not sure how to retrieve the current user's UID. I'm able to make an axios request to send the current user's UID from the frontend to the backend, so I've tried 2 methods with that:
Saving the axios request output as a global variable - this has led to Flask errors like runtimeerror: working outside of application context. and I don't think this is the best solution.
With each GET request that the frontend is making to the backend (every time there's a function whose output changes based on user), I am passing the UID as a parameter, which causes latency problems.
What is the simplest way for me to request the current UID from Firestore from the backend?
Is structuring our frontend, backend, database, and authentication like this recommended? Or is there a simpler way or better system for our situation (JWT?)? We selected Firebase authentication in the first place because we are using a React MUI template that already set up Firebase for us.
Thank you in advance! Happy to provide more information if needed!
I don't know reactjs, but I have the same setup with flutter (iOS / Android apps).
What I did and what worked out well is:
authenticate your client against firebase (which it looks you already achieved)
extract the idToken from the firebase response
send the idToken to your flask backend, which verifies the id token (see below)
in flask backend, log in the user with login_user() from flask_login. This creates a cookie session which is sent back to the client in the response headers
the reactjs client stores the cookie and needs to attach it to every subsequent API request to flask (this might come out of the box for reactjs, but for flutter I needed some custom code for that)
As for the token validation you can…
use the python sdk
use a jwt library such as pyjwt, see documentation
There is flask-firebase which does a good job for the token validation. I wrote a blog post which gives an example how you would use this.

IdentityServer4: How to set a role for Google user?

I have 3 applications:
An IdentityServer4 API which provides Google authentication and also provides an access token to authorize the resource API.
A simple Resource API which provides some data from DB.
A simple Client in React which have 4 buttons:
Login, for Google auth
Logout
Get data - a simple request with the access token to the Resource API and gets the data from Db
Get user data - returns user profile and token (for debug purpose)
I didn't put any sample code because my problem is not code related, it's knowledge that I'm missing and I ask for guidance.
The workflow is working just fine: the user press the Login button, it is redirected to IdentityServer4 API for Google Auth. From there it is redirected to a Callback Page from the Client and from there to the Index page. I receive the user data and the token, I can request data from the Resource API and it's working.
My problem is: How do I give a Role to the Google Users ?
I don't have users saved in DB. I want three types of Users: SuperAdmin, Admin, Viewer and each of these roles have limited Endpoints which can access.
For limiting their access I saw that I can use Claims-based authorization or Role-based authorization.
So, my question is how ca I give a Google User who wants to login in my app, a specific Claim/Role ? What is the workflow ? I must save it first in DB ? Or there exists a service from Google where I can add an email address and select a Role for that address ?
Thank you very much !
After you get the response from Google in your callback you can handle the user and do what ever you want to do with it. Below are the some typical tasks that you can do in callback that I took from documentation page of identityserver4 link:
Handling the callback and signing in the user
On the callback page your typical tasks are:
inspect the identity returned by the external provider.
make a decision how you want to deal with that user. This might be
different based on the fact if this is a new user or a returning
user.
new users might need additional steps and UI before they are allowed
in.
probably create a new internal user account that is linked to the
external provider.
store the external claims that you want to keep.
delete the temporary cookie
sign-in the user
What I would do is creating an new internal user account that is linked to the external provider and add a role to that user.
If you don't want to save users in db, you can add an extra claim to user in callback method and use that claim in token. and i think this link will help with that.

Do you need firebase admin sdk when creating admin web?

I'm currently working on a small project using firebase. My team member is working on IOS and android while I'm trying to build a custom admin page using React.
In the app, users can signup with their phone and send a request for permission by attaching few documents.
I have to build an admin page to approve or deny these documents. For that I need to get list of all user from User Collection and view all the documents that was submitted and be able update user field 'isApproved' to true or false.
I was thinking of simply creating a new admin account directly in firebase and use that account to signin to admin page and perform the following actions (manipulate normal user info field). But I found out about firebase admin SDK. Do I need to use this in my case?
I may need to send push notifications to all users signed up and create user, update user, delete user account later on.
Give the situation should I use firebase admin SDK?
Can someone give me advice on how to set up the overall structure?
First things first, you should not use the Admin SDK on frontend. The Admin SDK has privileged access to all Firebase resources and does not follow any security rules either. You should always use Admin SDK in a secure environment like Firebase Cloud Functions or your own server.
I am not entirely sure what actions you need to perform while accepting/rejecting the documents. If you need to read/write a specific part of a database (which only a admin can access) then you can use Firebase security rules. You would have to add a Custom Claim to the admin user or store their UID in a database.
But if you need to do multiple things (maybe sending an email to user, doing some actions using 3rd party API), I'll recommend using a Cloud Functions with the Admin SDK.
How will that work?
You will have to create a Cloud Functions to accept/reject the documents.
When the admin accepts/rejects a document, you can pass details of that user (userID, document info and if the docs were accepted to the
cloud function) to the cloud function and process it over there.
The callable function may look like:
exports.verifyDocs = functions.https.onCall((data, context) => {
const {uid, token} = context.auth
if (!uid) return "Unauthorized"
if (!token.admin) return "Forbidden"
//The user is an admin
//Do database updates
//Any third party APIs
});
If you use callable functions, Firebase will automatically add auth info of the user calling that function. In the example above, I've assumed the user will have an admin custom claim but if you want to keep things simple based on UIDs you can do so by:
const adminUIDs = ["uid1", "uid2"]
if (!adminUIDs.includes(context.auth.uid)) return "Forbidden"
To call the function from your React app:
const verifyDocs = firebase.functions().httpsCallable('verifyDocs');
verifyDocs({ userID: "userID", text: messageText })
.then((result) => {
// Read result of the Cloud Function.
});
Any thing you pass in the function above will be available in your cloud functions in the 'data' parameter.

Firebase Auth, after logging a user from react client, how do I verify user is legitimate within my other api?

I'm trying to use Firebase authentication to sign up and login users for my react website, but after that, how do I ensure that actions made from my nodejs api (for instance creating/modifying articles) are from that logged-in user. Here's a situation:
User logs in on my website, the firebase.auth().signInWithEmailAndPassword() method is called directly by the client within react (I can't use that method on my api since it asks for the raw password and I don't want to be sending that across the web, though I could save a salt on my db and hash the password, etc. but the reason I'm using firebase auth is to avoid having to be hashing passwords and maintaining salts on my db)
User is confirmed logged in
User starts to create an article
They submit the created article, react verifies they are logged in with firebase.auth().onAuthStateChanged()
Article data is sent to my api, for instance POST somehost.com/myapi/article/create/ with the article data in the body
My api receives the request and saves the article to my database
The problem I see here is that I don't see a way to send credentials to somehost.com/myapi/article/create/ in order to verify the user before entering the article into my db, since all signup/login is done within react and firebase's auth functions don't return anything I can send to my api to verify, so essentially anyone can call that endpoint and flood my database with junk.
I would like to be able to login the user within react, but then verify the user is legit within my api for all calls the user makes to it before it sends anything to the db. How can I do this?
If your Firebase client app communicates with a custom backend server, you might need to identify the currently signed-in user on that server. To do so securely, after a successful sign-in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity and authenticity of the ID token and retrieve the uid from it. You can use the uid transmitted in this way to securely identify the currently signed-in user on your server.
See https://firebase.google.com/docs/auth/admin/verify-id-tokens

GAE Calling Servlet with user authenticated through gapi.auth.authorize

I have a Google Cloud Endpoint which I access from an HTML page through JavaScript and the Google JavaScript client Library.
I authenticate with OAuth2.0 by using the standard
gapi.auth.authorize({client_id: CLIENT_ID, scope: SCOPES, immediate: mode}, callback);
Everything works correctly and I am able to read/write data from/to the underlying Datastore.
In the same AppEngine project I have a servlet that generates a PDF based on data that is in the Datastore.
I would like to be able to call this Servlet from my HTML page using the same user that was authenticated through the api.auth.authorize() method.
And in the servlet, get the User through
UserService userService = UserServiceFactory.getUserService();
and query the datastore for the data of this user and then generate a PDF showing this data.
I have no idea how to call this url (servlet) with the credentials of the OAuth autheticated user.
Can you help me please??
Thanks in advance!
Note that the same question was asked some months ago but without a "complete" answer: GAE User API with OAuth2
You should look into bucket/object ACLs. When your API endpoint gets the User object, it can use the user's email to set the ACL on the PDF which is generated. That way, you can serve the PDF file to the user simply using its URL. You could also check with an endpoints API call whether the user is indeed authenticated as the person who is allowed to access the requested PDF (having stored a Datastore entry, perhaps, parallel to the object), and generate a signed URL once this is confirmed.

Resources