Multiple Auth Providers with AppEngine, Webapp2, and Cloud Endpoints Proto Datastore - google-app-engine

I'm working on a webapp that will allow users to authenticate using simpleauth. For now I will be supporting Google and Facebook. Other than logging in and out (using webapp2), the webapp will consist of Cloud Endpoint APIs. The clients will be web, Android, and iOS.
My questions is, using Endpoints Proto Datastore, can I have user_required=True and call endpoints.get_current_user() to get my user from an #Model.method if the user's auth provider is Facebook (or any of the other supported OAuth2 providers? If it is not possible, does that mean I should not have user_required=True, and should instead get a permanent user id from the provider given the OAuth2 token and keep it in the datastore, generate my own auth token for that user, and then pass that token to each request?
EDIT: Instead of passing the auth token around, would it make sense to have an authenticated user request an "API token" that they can pass to the API methods? Would this token have to be included in the POST or GET body, or can it be put in a header/cookie (I saw some questions elsewhere on SO regarding headers and cookies with Cloud Endpoints, but it's been some time since then). This is all assuming that non-Google auth won't work.

This answer is not going to directly answer your question but should give you a good idea how you can implement authentication in a safe way. I've implemented something similar recently and spent quite some time figuring out which is the best way to do authentication with Google AppEngine environment.
Google supports OpenId Connect protocol. Facebook's implementation should be very similar according to Getting Started with OAuth 2.0 book. I will focus more on Google's implementation, as I am more familiar with it but the concepts should be very similar when using other OAuth providers.
OpenId Connect will give you an id_token, after successfully authenticating the user. It will also give you an access token. Access token is something that should be kept a secret. Never send it over the wire. Id token on the other hand is very handy. It contains some information about your user, and it's encrypted so it doesn't expose any information about the user "just like that". You'd have to crack the id_token to find out anything about user. In the unlikely event that it gets cracked it doesn't expose anything critical about the user. What you can do you can store it as a cookie and then use it in all subsequent requests to verify it the user by checking if there's an access token that matches the id_token. The only drawback is that id_token is quite long - it takes around 650bytes. That would mean that every http request carries that payload. If sending that much information is too much for your use case you can send only first few characters, maybe 12 or so and then match just the first part. The id_token has can also be useful when analysing your data. It will show up when analysing http requests but will not reveal any information about the user and you can still differentiate requests that came from different users.
Also on a side note, don't try using AppEngine's users service as it doesn't work very well with any kind of custom authentication.
Hope this gives you an idea and puts you on the right track.

Related

AWS Cognito Identity Service Provider appears to store access token in local storage. Is this safe?

We are developing an application that uses a React front end website hosted on AWS using Amplify. This communicates with a .NET Core 3.1 Web API running on EC2 / Elastic Beanstalk. Cognito is used for user authentication with the Web API configured to use JWT tokens.
It works OK, but we have noticed that the Cognito provider stores the JWT access token in the browser local storage. This is what we see using F12 in Chrome and inspecting local storage.
From what we have read, storing access tokens in local storage is not advised as it makes the application susceptible to XSS attacks. Strange then, that the Cognito identity provider chooses to store sensitive information here.
If this approach is not considered safe, can the provider be configured to store this information elsewhere, such as cookies?
Alternatively, as we control both front and back ends, is there an alternative method that can be used to secure the API that does not involve tokens? Obviously the API needs to know which user is logged on to the web application in order to perform authorization checks. [Note authorization in the application is record level and defined in database tables, so it goes beyond simple user profile attributes.]
Many thanks in advance for your advice.
Doug
Security is a spectrum not a feature so it really depends on your appetite for risk vs effort. Amplify is not a particularly nice codebase, it has 500+ issues and if you look at the code you might be fairly shocked at the quality of it.
If you are using Hosted-UI then you can write code to manage the tokens yourself rather than using amplify, although you will need to learn a bit about OAuth grants and OIDC.
Be aware that the Hosted UI lacks a huge amount of features, so if you are going to use it make sure you are happy with it. Off the top of my head
no silent refresh capability in the hosted UI, so no safe way to store the refresh token.
no support for custom auth flow in the hosted UI
no passwordless support in the hosted UI
no ability to pre-populate a field in the hosted UI (e.g. username)
no ability to customise the plethora of obscure error messages in the custom UI
fixed now, but for years the email addresses were case sensitive!
An alternative is also to just use the AWS SDK to get tokens directly using cognito-idp but this also has a bunch of issues:
no code/PKCE/nonce capability so insecure in a mobile authsession
no ability to set oauth scopes, so can't use them
consequently not possible to use for OIDC
the SRP implementation is bananas and so far off spec
if you make device registration mandatory, it will deliver a working access tokens before the device is registered! (allows invisible devices for malicious logins)
We were using auth0 which was leagues ahead but we had to move to Cognito because of SMS OTP cost (min $25k per year at auth0).
I have been using AWS for over a decade now, Cognito is by far the worst service I have used, and I have used a lot! If you can avoid it, do so.
To answer the original question, yeah it's insecure. The best you can probably do is keep them in memory. If you wanted to you could probably put the hosted UI behind a cloudfront and use an lambda#edge to transform the token into a cookie instead. This has now opened you up to CSRF attacks though.
answering the original question: no, it is not safe at all.
Storing refreshtoken in any local storage accessable to any local app/script is not secure. So, the best way would be to store the refreshoten (and also the access token) in an httponly cookie or even better to store a one-time session token in httponly secure cookie could be used to get new access and refresh cookies - similarly as it is made by cognito hosted ui with XSRF-TOKEN.
See below how I would solve (and plan to solve) this issue:
Some background:
Due to GDPR regulations I think I can not use the cognito hosted ui - I have to make sure users read and accept the general terms and conditions (giving clear and auditable consent) and can review and accept cookie policies as well before they type in any user data for sign up. Nevertheless the built in hosted ui design is quite outdated and unflexible. I have an SPA website where I want to manage users, secure endpoints, etc.
So I have the following idea which is still not super secure but I think it is more secure one if you want to use js and ampify sdk and which also might answer your question:
I'll use amplify javascript sdk to let users sign up, change psw and log in (get tokenid, access token and refresh token), will make my own "hosted ui". I'll store the access token in memory only (not in local cookies and not in localstorage for sure). Access token will be used in header (bearer) to access apiGW endpoints. Access tokens will have very short expire dates. (I'd also use httponly secure cookies sent back by the apigw, as well as in the body.., then compare at BE side..)
And here comes the trick: I'd cut the refresh token into two. (Don't forget it is just a string.) I'd store the first part of the string in a local cookie (javascript can read it, if browser is closed and opened again it will be still there) and will send the other half of the refresh token to an apiGW endpoint (accessable without authentication) which will store it in a dynamoDB table (with TTL) and will send back an httponly secure cookie to the browser with a randomly generated "storagetoken" in it (which will be a key in dynamodb). There will be another unauthenticated apigw endpoint which will be called by the client whenever the client needs the full refresh token. Calling this endpoint the browser will send in the httponly secure cookie as well (same domain), so the backend will get it. As it is issued by the BE and available only in the given browser it can not be stolen so the backend will send back the stored half refreshtoken. The other half part of refreshtoken is stored in a simple cookie in the browser.
If the browser is closed and opened again client checks if there is any valid accesstoken and if not it checks if there is a half refeshtoken stored as cookie. Then ask the other part of refreshtoken assuming there is a httponlycookie also stored and it will get back the other part of the refreshtoken from the BE. In case of success the client tries to use the full refreshtoken to renew/get access token from cognito, in case of failure it will pop up the login screen.
Whenever refreshtoken is not in use it is deleted from the memory.
I know this is still not supersecure but might be a better solution than storing refresh token in localstorage.
Alternatively, as we control both front and back ends, is there an alternative method that can be used to secure the API that does not involve tokens?
I don't know anything of Amplify but in AWS Cognito what you describe is the Implicit grant OAuth flow. In AWS Cognito it is possible to use Authorization code grant where you instead of the token get a code which you in the backend can exchange for a user pool token.
https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-app-integration.html

Authenticate with Google Drive API in AngularJS

I created this web app completely in go-lang, which uses Google Drive API to authenticate users. Once user authenticated, it saves the token in a <user-email>_token.json file so the app can operate for 24 hours without the users involvement. It works fine. But now I want to separate the front-end from (Go-Lang)back-end and convert it to AngularJS.
So I have this problem with authentication. Because I should keep the authentication in server-side. But then how would Angular know that the user is authenticated or not? Because I cannot use sessions.
Should I need to use JWT to this? If it is, then how should I do it?
Your token does not have to be on the server side.
Why? Because if you have many clients connected to your server, it would mean that all of these clients are sharing the same token and therefore have access to the Google Drive linked to this token. It does not make sense.
The token has to be on the client side. You should save the token as a cookie, maybe by using JWT, I let you read the documentation of JWT to know why it would be interesting to use it in your case or not.
Then on your Angular, you have to say something like "hey, this client has a cookie called "my-google-drive-token", let's check if that is a good one... Mmmmh, okay seems to be good, I display the Google Drive content".
Think about using good practices about security (using an encrypted token in your cookie, making the connection between the front and the back safe, keep your API key safe...).
Your backend is only a gateway between your front-end and the Google Drive API.
Also, check the usefulness of your server. I think that in your case, a simple frontend connected to the Google API is enough.

Is it good practice to store google tokens in local storage

I am bit confused after reading few articles, basically assume you have a web app which uses gmail login for authentication. Is it a good practice to store the google access token in the local storage and send it via headers to verify in back end APIs ? or should I use separate mechanism to handle api level access ?
It does not matter where will you save tokens at front-end, because even in a case when you will be exchanging them witch back-end - you will need to send them with the request.
(all data which is placed at front-end is unsecured)
So everyone will have access to them inside the inspector network tab.
When a question is going about security, the first rule:
Research possibility to implement that at back-end by using environment variables.
P.S: I just opened Authorizing Your App with Gmail and there are says:
Get started:
To get started, see Implementing Server-side Authorization.
So you need to handle all of the Authorization actions at back-end where all the tokes will be secured.

Securing a React frontend and with Python API using AWS Cognito

I'm considering using AWS Cognito as a user management system for a single page web app I'm building using React along with a Python REST API backend (Pyramid). I'm struggling to see how all the pieces fit together for my architecture (the docs don't seem to be helping me). There are many great examples of how to implement authentication into the frontend using JS. My issue is how to integrate this authentication into my backend REST API.
In my current hand rolled user management system, the frontend calls the REST API on sign-in and is given a token which is passed to API again for every subsequent request. I'm then able to use ACL's on my API functions, check permissions to access resources, etc. If I were to use Cognito and perform the authentication on the frontend (as many examples do) how will my backend know if the token is valid when it receives it with a request? Surely I wont have to call Coginto from the backend to verify this for every request? Also how can I perform checks for information such as 'is this user in the admin group' if that group is defined within Cognito? Again, calling out to Cognito for every request seems very heavyweight and cumbersome.
I did see one example where a list of valid tokens was exported from Cognito as a JSON file and kept on the backend. This seems horribly static when users could be added and removed regularly.
Is Cognito really suitable for my use case? Some high level guidance and pointers to any relevant examples and docs would be greatly appreciated!
When authenticating with Cognito, the user can have 3 tokens:
Refresh
Access
ID
For python, boto3 can interface now with Cognito. There's also this python lib wrapper: warrant, to make it easier.
Once you have the token, it is possible to pass it to the API (eg: access) and it can be checked on the server side with python-jose, as per AWS docs
To pass the token, an example pyramid /login implementation can keep the information in the session before setting the request response:
request.session['my_token'] = str(a_token)
The default cookie session factory works, though it warns that the token is not sent encrypted.

Google Cloud Pub/Sub Publishing from Browser - How does Auth work?

I have a requirement to use Google Cloud Pub/Sub API directly from Browser ( similar to GA script). I was wondering how can in handle Auth without requiring going through a back-end server.
I want to invoke the Cloud Pub/Sub API directly from the browser. I tried and it says i need to authenticate first , my issue is how to secure the Auth Token.
Is there any javascript library that is available which i can use in Browser ( not backend) to invoke the Google Pub/Sub API.
Thanks in advance
The general approach in Javascript for authorizing and making authorized requests to Google APIs is shown at https://developers.google.com/api-client-library/javascript/samples/samples#AuthorizingandMakingAuthorizedRequests -- it's not specific to the Cloud Pubsub API, but it should work for all Google APIs. Similarly, https://developers.google.com/api-client-library/javascript/start/start-js for general Javascript access to Google APIs.
This is quite an old topic, but I've been recently assessing if it's possible. The simple answer is - yes, it is possible to send messages into PubSub topics directly from a browser application. The solution is as follows:
You need to post a message via REST API using fetch()
You need to send the Authorization header
Authorization header has to contain oAuth2.0 token identifying the user; it can be an anonymous authenticated user or fully authenticated, using firebase authentication library for example.
To have all three above working perfectly, you'd have to write a lot of code. It is not practical at all and architecturally not nice. Unless you absolutely need to do it that way, not another (I can't see why though), the simplified but involving a bit more components solution is as follows:
Authenticate user in-browser via firebase - can be either anonymous or full user
Do simple GET or POST to your cloud/firebase function with the required payload
In function validate the incoming request which will have authenticated user token
If validation is good then publish message into the topic
This way it's still secure, much more maintainable and clearly separated into functional components. Less code, a bit more infrastructure.

Resources