Google OAuth Always Showing Consent Screen - qt

I'm building an installed application that will have features requiring the Google Drive REST API using Qt and C++. I understand Qt is now releasing new libraries to support OAuth flows but let's assume I'm a student and learning to use OAuth at this layer is a requirement for this project.
In my application, I have a working OAuth flow for installed applications that ends with an Access Token and Refresh Token being stored using QSettings (I'm open to input on whether this is a disastrously bad idea too). The application requires no authentication/login for its own sake/data, but it does need authentication to Google for calling API's using an Access Token. This application has no associated web backend being hosted; its simple and should be deployable completely locally (I've written and included a simple TCP server that will receive the authorization redirect_uri and will run and close when called from within the application).
As such, I'm curious about the best way to make sure that, when a user opens my application and wants to use the Google Drive features, they are appropriately authenticated on Google's end. Say, if I maintain an access token in the registry, and this access token is granted per-user/per-application basis (right?), then how can I make sure only the user the token belongs to is able to make calls to the API with it?
Here's my understanding and approach; feel free to correct me or educate me if I've got the wrong interpretation.
If an Access Token is found, perform the following:
Open a browser page to a Google login domain and have the user authenticate there (this could prohibit a user from being able to use a cached login session that would have access to a token they otherwise shouldn't have access to)
If user has correctly authenticated with a Google account, return control to the application and make a test call to an API using the stored token.
If the call fails (responds with an invalid_credentials) I should be able to be sure its because the access token has expired and the application will go through the flow to renew an Access Token from a Refresh Token.
If no Access Token is initially found:
Start a normal OAuth installed application flow
Get the tokens and store them so that when the user opens the application next time the former procedure is used
My issue then is the first two steps if the Access Token is found. Nominally this could be done by the typical OAuth flow but it appears that when using a localhost as the redirect uri, Google will always prompt for consent, regardless of settings for prompt and access_type authorization query parameters.
What can be done to accomplish these first two steps in a way that my application can control (i.e. not a solution that relies on a backend server being hosted somewhere)?
If this question is too open-ended for SO requirements I can make some more restrictions/assumptions to limit the problem domain but I'd rather not do that yet in case I unknowingly rope off a good viable solution.
Thanks for reading! Sorry if its a verbose; I wanted to ensure my problem domain was fully fleshed out!

If you are using an installed application, I wouldn't recommend using or storing refresh tokens. Storing refresh tokens on the client side means that if an intruder gains access to the client's application, they have infinite access to the user's application without ever having to enter the user's credentials. If you do insist on having a refresh token, ensure you follow the Google's installed app flow, with the code_verifier parameter included in your requests.
If the access token is found, you should try to verify it, and if verified then use it at the google api, otherwise force the user to login again (or refresh it if you chose to still use refresh tokens).
If no access token is found, your flow sounds fine.
A few notes on loggin in with Google:
Google will only return a refresh token if you specify access_type=offline in your auth request.
Google will only return a refresh token on the user's first authorization request, unless you always specify prompt=consent in your query params.
In my experience, when leaving out the prompt query param, the user is not prompted for their consent again. If they are logged in to google, you will get a new access token, but no refresh token, unless you have prompt=consent.
I think the idea is you use prompt=consent if you have no record of the user ever using your application. Otherwise if they have used it before, you may prefer to use prompt=select_account to allow the user to select which account he wants to use in case he has more then one, or you can just use prompt=none.
This is just my understanding of it all.

My approach I ended up using was just to deploy with an SQLite db that will be stored in the AppData roaming directory. The db schema includes a field for the user's Name (from the OpenID IDToken field if it exists), the user's picture URL (again from IDToken if it exists), the refresh and access token strings (will be stored as encrypted strings when I get around to it), the user's UID/sub string, and a field for a user name and password.
These latter two fields are authentication fields for within my own application, which, again, I wanted to avoid but it seems impossible to do so. So the user will be prompted to enter a username and password into a form, and these credentials will be checked against the existing SQLite db file mentioned previously.
If they exist and are correct, the user gets logged in and will have access to their respective access and refresh token.
If the user has forgotten their password, they'll be asked for reconsent (going through the installed app flow again), and whatever password they provided during initial login will be used as the reset password. It is considered, for my purposes, that logging into Google for the installed app flow is proof enough that the user account belongs to them and they should have authorization to reset the password.
If the user is a new user and doesn't have a record in the local SQLite db file, then they can also click a button to "Create New Account" - which effectively goes through the authorization flow as well but this time a whole new record is posted to the SQLite db with the appropriate fields filled.
There's still more optimization that could be done but at least I am getting closer to the level of security and control of access to Google user accounts that I want.
I'm not marking this as an answer because I feel like this solution is still not desired and that there should be an easier way. So if someone has evidence or experience of providing an equivalent level of authentication control without needing to maintain a local user account database then I would be more than happy to mark such a method as the solution!
Thanks again!

Related

SvelteKit: How to access Firebase authentication state from the server

I'm managing authentication state in a readable store which is grouped with a promise that resolves when the auth state is known (either signed in or out). The store is set internally via onAuthStateChange.
I'm trying to access this auth state from the server (+layout.server.ts and +page.server.ts) so that I can redirect the user to a sign-in page if they aren't authenticated, or load data from the database if (and only if) they are. No matter what I try, whenever I access this store from the server, its value is always null. I think this is because Firebase is only supposed to run on the client, although I'm not sure. Is there any way I can access this store from the server, or change my implementation so that Firebase runs in the server and passes auth state to the client? This blog post explains pretty much exactly what I want to do, but the solution here seems more complicated than it needs to be.
I've tried moving Firebase initialization code to the server (in both hooks.server.ts and +layout.server.ts), but there's no way for me to pass the auth object to the client because it can't be serialized (I get an error explaining this when I try to return it from a load function in +page.server.ts). I've also tried to handle authentication only using client-side code, but the server is responsible for loading data from the database, so in this case there's no way for me to verify a valid authentication state from the server.
author of the blog post you linked here.
You are correct in that the firebase client is only supposed to run on the client. If you want to access the Firebase services server-side you're supposed to use Firebase admin.
In my post I'm working around these limitations that firebase sets and unfortunately you do need that much code.
You are forced to do all the authentication in the frontend (firebase-client) and only afterwards you can inform the backend of the user info. (through the cookie)
Before I wrote this the ideal solution I had in my head was this:
Send the user to a login page that lists all the possible login providers.
The user chooses one and logs in.
the user gets redirected to a callback page where we can do something with the auth data in the backend.
I do think that something like this flow is possible if you're using custom tokens. But I'm sure that will be a whole load of even more complicated code.

Detect whether user is logged in to site A from site B, using Firebase Auth

Our new site, example.pro, uses Firebase authentication. A small subset of our old example.com users will pay to subscribe to the new site but most will continue to use the old site anonymously.
Firebase Auth makes it easy to remain logged in to example.pro, but these privileged users might visit the old site for various reasons, including by mistake, and from different devices. So for these users I would like to detect whether they are logged in and redirect them as transparently as possible offer them a link to the new site that they can ignore and continue to use the old site, if they wish.
I have considered placing an iframe from example.pro on example.com that would trigger a dialog. Would that be safe?
Do you have a better idea?
[I've edited my question to clarify that the old site does not use any authentication or means of identifying the user, and to clarify that I want users to be able to choose to remain on the old site]
Firebase Authentication does not support multi domain authentication or something like SSO. The best you can do you implement JWT based custom auth that primarily relies on Firebase Auth. I've done that in a couple of ways and the auth flow mentioned below works best for me:
Select a domain for Firebase auth (this will be the domain where users will be logged in via Firebase directly)
When user visits one of your subdomain, let's say app.domain.com for this example, you check if there is any token present in the localStorage of browser (or any local storage of the respective platform). If yes, that means they are logged in. (I'll come back to the token later)
If the user ain't logged in on the subdomain, go to the domain where Firebase Auth works, make a call your server with the Firebase IdToken, verify that and generate a temporary token and return it. Make sure you store it in your database on server side. After that, redirect user back to the subdomain where they were trying to log in with the new token in query param. For example, your URL may look like: https://app.domain.com/login?temp_token=thatTempTokenGeneratedOnServerSide1234.
Make another request containing that temp token to your server from the subdomain and validate it (like check the UID and if it is expired and maybe if the IP of user is same when the token was created).
Generate another JWT (preferably one with long life) (You might want to look at Rolling Token Auth for better security) and return it to the client and store it on client. This JWT ideally would contain only the UID of that user. So whenever the user makes any subsequent requests to the server from that subdomain, add that token in request header (or keep it in cookies as per your convenience) then verify it on server side for processing the data.
If the token is expired, repeat the auth flow.
I've been using this for a while and found no issues. Just make sure you read about the access tokens and refresh tokens about how that works. I'll try to add a flowchart asap meanwhile feel free to ask any questions.

Meteor Restivus: keep user logged in if he goes to the main website

I have a Chrome extension that communicates with my Meteor app through a REST API created with the Restivus package.
The user authenticates to the REST API and then uses authenticated tokens to make any further requests.
So far, everything works fine, as long as he stays within the extension. However, from the chrome extension, I'd like to redirect the user to his profile page on my main website. When that happens, he's no longer authenticated, and must re-sign-in to access the profile page.
I figure this is because the REST API session and the webpage session are two completely different sessions on the server (even though both the API and the webpage run from the same server). My question is, is there a way to maintain the user's logged-in state as he moves from the extension to the main website?
I figure there are a few options:
I'm using the standard meteor accounts package. Is there a way to push whatever standard cookie / data that the accounts package uses, to the user's browser, so that when he goes to the website, he'll be considered logged in?
Push a custom cookie to the user, which I then check for and log him in when he first comes to the website. However, I don't know how to push a cookie through a REST API or generate one in the Chrome extension
Use DDP to communicate with the second session and transfer the login credentials.
I don't know if these are the best options (or even how to implement them if they are...). Has anyone figured out a way to do this already? Thanks!
I would suggest you to develop your own flow of authentification using a token as an URL parameter. You should achieve a similar experience that slack provides with magic authentification links
The idea is to generate a token and add it to the Meteor.users collection for the user logged in your chrome extension.
Then, redirect your user to an url with the token as a parameter. The app checks which user is linked with this token and log him in.
You can get inspiration on what is done in the account package to handle enrollment and reset links, or in the passwordless package

Is there anyway to create playlists on Spotify using a command line tool?

I want to create a playlist through the command line, but I appear to need to use the "Authorization Code Flow" method of authentication to be able to do this. The only way I can think to do that, is with a full web app through the browser, but I just want to make a command line tool without any of that hassle.
Is there a way to deal with this?
You do indeed need to use the Authorization Code Flow. However, that doesn't mean you need to build a web app.
Method 1: Prompt from command line, a la Spotipy
Check out the way Spotipy, a 3rd party Python library implements their authentication: https://github.com/plamere/spotipy. It uses the command line to prompt for authentication, then gets users to copy the URL back. This could be an easy workaround if you don't want to fully implement an authorization flow yourself. I recommend going through their quick start to get an idea of a non- web app implementation.
(They even have an example for "Create a playlist", maybe you can just use and build off that? https://github.com/plamere/spotipy/blob/master/examples/create_playlist.py)
Method 2: Get an access token for your account only
The Authorization Guide states the following:
Accessing your data without showing a login form
I want to interact with the web API and show some data on my website.
I see that the endpoints I need authorization, but I don’t need/want a
login window to pop-up, because I want to grant my own app access to
my own playlists once. Is there any way of doing this?
You basically need an access token and a refresh token issued for your
user account. For obtaining a pair of access token / refresh token you
need to follow the Authorization Code Flow (if you need a certain
scope to be approved) or Client Credentials (if you just need to sign
your request, like when fetching a certain playlist). Once you obtain
them, you can use your access token and refresh it when it expires
without having to show any login form.
So, if you only need access for your own account, grab any of the simple tutorials from the internet, follow it, and get an access token. You can then use that access token to make calls.

How to provide the Write Access in Firebase without giving Read Access?

I am having a strange situation in my app where Unauthorised users can send me the messages. whereas It will be only one user who can have the read access to those messages.
I am using the $bindto to write on the database (with my google logged in and google authentication enabled) and reading it through email password authentication method .
I want that user could send the message without any authentication process.
Is this possible?
There is no way we can stop unautheticated user to get the data from firebase database. For this situation that I faced I was worried about that if a user download the JS file he can get the access of all my database. But there is a catch :
Since you whitelist your IP/domain name no one other than that domain can take the access.So, even if they download your file they will no be able to get the access as request will not be from authenticated IP/domain name.
The other way is using the anonymous authentication method so that u can anyhow authenticate user and by that you can atleast have the trail of who are working or modifying your data (IP tracking access log is also useful).

Resources