How to achieve the Dropbox equivalent of long-lived token now that they're gone (dropbox-sdk-js, Meteor, React) - authentication

For a while now I've been using dropbopx-sdk-js in a Meteor application without any trouble.
My Meteor app simply uses Dropbox to fetch images to be used in product cards. These files are synced now and then and that's it. By synced what I mean is they are scanned, shared links created or obtained, and some info is then saved in Mongo (name, extension, path, public link)
End users do not remove nor add files, nor are the files related to an end user specific account.
To achieve this, I created (in the far past) an App in the Dropbox App Console, generated a permanent token, and used that token in my Meteor app to handle all the syncing.
Now I've tried to replicate that very same thing in a new similar project, but found that the permanent tokens have been recently deprecated and are no longer an option.
Now, checking Dropbox's Authentication Types it seems to me like "App Authentication"
"This type only uses the app's own app key and secret, and doesn't
identify a specific user or team".
is what I'm after. I can safely provide app key and secret in the server exclusively, as the client will never need those. The question is how do I achieve such kind of authentication? Or for that matter, how do I achieve an equivalent of the long-lived token for my app, ultimately meaning that end users don't actually need to know Dropbox is behind the scenes in any way (and they surely don't need dropbox accounts to use this app nor should be prompted with any Dropbox authentication page)
In the js-sdk examples repo, I only found this example using app key and secret. Yet afterwards it goes through the oauth process in the browser anyways. If I don't do the oauth part, I get an error
"error": {
"name": "DropboxResponseError",
"status": 409,
"headers": {},
"error": {
"error_summary": "path/unsupported_content_type/...",
"error": {
".tag": "path",
"path": {
".tag": "unsupported_content_type"
}
}
}
}
as a result of calling
dbx.filesListFolders({ path: '', recursive: true }):
If I replace the initialization of the dbx object with a generated token everything works out. However eventually the token expires and I'm back in square one.
Any ideas what may I be missing?

The short answer is:
You need to obtain a refresh-token. You can then use this token for as long as you want. But in order to get it is necessary to go through at least one oauth flow in the browser. Then capturing the generated refresh-token in the backend. Then store it and use it to initialize the API. So it's kind of "hacky" (IMO).
For example, you can use the mentioned example code, and log/store the obtained refresh token in this line (as per Greg's accepted answer in the forum). Then use that value as a constant to immediately call the setRefreshToken method (as done in that very same line) upon initialization.
The long answer is:
ClientId + Client secret are not enough to programmatically generate a refresh token.
Going through the oauth flow at least once is mandatory to obtain a refresh token
If you want to hide such flow from your clients, you'll need to do what the short answer says.
The intended flow of usage according to Dropbox is: each user access his own files. Having several users accessing a single folder is not officially supported.
The longer answer is:
Check out the conversation we had in the dropbox forum
I suggested to replace the "Generate Access Token" button in the console for a "Generate Refresh Token" button instead. At least it made sense to me according to what we discussed. Maybe if it gets some likes... ;).

Related

Trello OAuth only working with one Google Apps Script

Background
I am able to create Trello cards from Google Apps Script via the
Trello API using the OAuth 1.0 library. The principle is proven/code
works.
I have two distinct Google Apps Scripts projects that need to be able to create Trello cards.
The code in the two different Apps Scripts/Projects is identical - including the same API key/secret.
Only one Apps Script will create a Trello card. This is my problem.
If I reauthorise the other Apps Script, that script will work and the other will give me an API return of "invalid token" and vise-versa. Only one works at a time, but I need both to work.
My thoughts
I think that Trello, via OAuth, see each Apps Script is its own distinct project.
I think that because of this it won't let both apps use the same API key/secret to work with my Trello account. Only one project appears to be able to use the key/secret.
If this is the case I don't know how to make each Apps Script its own project for the Trello API to work for both simultaneously.
Help needed
Does anyone know how to make this work? I need both scripts to be able to create Trello cards. I have a feeling that each apps needs to identify itself uniquely, but I honestly have no idea.
This is really an OAuth logic issue, it's a feature, not a bug. In OAuth, your application exchanges refresh tokens for access tokens. The access tokens only have a limited life span.
When you use a refresh token to generate a new access token, you also get a new unique refresh token and your script stores this for future use, the old refresh token is no longer valid. Similarly, when you re-authorize the application, you get fresh tokens, and any previously generated tokens are rendered invalid.
So when you authorise one script using the same Client ID and Client secret as the other script, you get a new access token and refresh token, and the old credentials, stored by the other script, become invalid.
As a result, the other script can no longer exchange the refresh token it has stored for new access tokens, and it no longer works. Once you re-authorize this copy, the refresh token and access token in the other copy are invalidated in the same way. So you end up going in circles.
You have two options:
Set up a separate OAuth Client (with different Client ID and Client Secret) for each script.
Modify your scripts to use the same storage location for the OAuth Access Token and Secret.
The first approach is going to give you the most reliable consistent results. If you try the second approach, you could still have cases where the scripts run at the exact same time, and one has valid tokens while the other tries to use the now invalid ones. (race conditions).

Auth0: Specific questions about token storage and flow for mobile app

I’m building a react native app that will interact with APIs that I also write/manage. I have found Auth0 documentation for implementing this flow, but I’m not sure on where/when to save the tokens. I want to be sure I nail this step, because I feel like it has the potential to reduce the security of the flow by a great deal if I don’t do it correctly.
Here is the flow as I understand it (no error handling, only happy-path for sake of brevity):
A user enters the app for the first time, or is not already logged in
They log in using the Auth0 web-login-thingy
I receive a token
I can use the token to authenticate with my API
Questions:
Do I store that token? I don’t want my users to have to log in every time they use the app. If I do store the token, where do I store it?
If I’m not storing it, what do I do? Do I ping an authentication/authorization endpoint with Auth0 every time they open the app and get a new token?
Say I am storing the tokens, if I'm using the ID token for user data, should I be hitting the API again regularly to keep it up to date? Only when the user opens the app again? Not until they trigger a change in the app?
Instead of using the ID token for user data, should I just use that to get the user's ID and ping my database for user data?
I have the basics of this flow, and I'm able to sandbox it, but I want to start applying production-ready app logic to this flow and that's where I'm stuck. I’m a little lost here, so any help is good help.
Thanks!!
Here's a brief answer to your questions when using Auth0:
Yes! you store it, the most secure way to store the token is in your device's local storage, that way it is not kept either in application's state or in a global variable.
2&3. See above, but to add more information, you can configure your tokens to have an expiry length. in theory you would convert this 'expiry time from inception' to a date object, and can do one of two things; you can request a new token using the Refresh Token (that comes with the original) once the expiry has been reached, or force the user to re-log in and re issue a new token at this time (i prefer the latter, prevents people from just renewing their tokens forever as long as they remain logged in)
Use the auth token to request user information after login, this can be stored in app state/global variables/wherever. You then want to use the auth token in the Authorization Header for each API call, along with whatever data you are sending. this ensures that even once someone is INSIDE the application, they need to have a valid token to actually do anything involving data (imagine someone back-dooring into your app and skipping the authorization, or using something like postman to just hammer your API with garbage). it would work something like this: GET userData { Header: auth token } -> GET userProfile (by sending your user ID returned from GET userData) PLUS {Header: auth token }
I can give more in depth examples if you wish, and i apologize if i misunderstood any of the question and gave redundant/incorrect answers
Edit: Resources about using secure storage for keys
Document for when to use in-memory storage Vs persistent storage. The TL;DR is use in-memory if the key is expected to expire before a standard session duration, and persistent for storing a key between sessions
https://hackernoon.com/mobile-api-security-techniques-682a5da4fe10
link to Keychain Services doc
https://developer.apple.com/documentation/security/keychain_services#//apple_ref/doc/uid/TP30000897-CH203-TP1
link to SharedPreferences doc
https://developer.android.com/reference/android/content/SharedPreferences.html
AsyncStorage is a simple, unencrypted, asynchronous, persistent,
key-value storage system that is global to the app. [1]
You could store it in your AsyncStorage, but thats not necessarily a secure location itself (e.g. not encrypted, accessible on rooted devices...). Typically clients will issue access tokens that last anywhere from several hours to a couple days and these will provide their owner access to your API-resources. If there is sensitive data behind your login screen, you're probably better off simply re-doing the auth-flow and invalidate older access tokens on login.

How is the access_type=online oauth mode (no refresh token) supposed to work?

This question is has a lot in common to the previous question Google OAuth: can't get refresh token with authorization code (and I won't be offended if it's considered a duplicate) but there are some differences: that question uses the Javascript and PHP libraries, and I'm using neither of those. That question wants to know how to get a refresh token, and I want to know if I should want a refresh token, or how the mode with no refresh tokens is intended to work.
I'm following this guide:
https://developers.google.com/identity/protocols/OAuth2WebServer
The goal is to allow users to upload files from Google Drive to my web application.
I'm not using one of Google's favored programming languages, so I don't have a library abstracting away all the interaction with Google. I need to know what the HTTP requests should actually should look like.
One of the parameters in the authorization request is access_type. The description says
Set the value to offline if your application needs to refresh access tokens when the user is not present at the browser.
I won't need to do that (I'll only want to retrieve a file on my server immediately after the user selects it) so in the spirit of not asking for more privileges than you really need, I used access_type=online. This gives me an access token and no refresh token. I've successfully used the access token to make some requests to Google Drive.
The user comes back the next day and tries to upload another file. While processing this request from the user, I make a request to Google Drive. The access token is expired, so I get a 401. What's supposed to happen next?
My guess is I should pretend this is a completely new user and send them through the full authorization process again. That would mean I have to abort whatever the user was trying to do, redirect them to https://accounts.google.com/o/oauth2/auth with all the parameters (scope, client_id, etc.) and embed enough information in the state parameter that I can resume the original request when the user gets back from their detour.
This seems rather difficult (in particular the part about saving and resuming the state of my application at some arbitrary point). It's a big enough obstacle that it should be explained somewhere. But the description of the access_type parameter didn't say anything about needing to insert authorization redirects everywhere. It just said the user must be "present".
You are using the right implementation. You don't need offline access if you aren't going to make requests when the user is not using the application. The thing is that access tokens expire in 1 hour. So you need to generate new access tokens if a user leaves the application and come back later.
If users have authorized your application, calling this URL with your configuration should return a new valid access token:
https://accounts.google.com/o/oauth2/v2/auth?
scope=scopes&
include_granted_scopes=true&
state=state_parameter_passthrough_value&
redirect_uri=http://oauth2.example.com/callback&
response_type=token&
client_id=client_id

Facebook Login without JSSDK, how to get token if already authorized previously

So I am updating an older desktop app (written in VB, .net 4.0) with facebook integration and followed the guide found here, and have been able to successfully get a token (by parsing the uri of the embedded webview if it contains "token="). Now my problem is if I try to login with a facebook account that has already approved the app in a prior session, the webview just gets redirected to https://www.facebook.com/connect/login_success.html without any token information.
Do I HAVE to log all of the tokens I generate manually (ie on successful token generation, I can call their profile info, use their FB ID as key and save the token)? Even if I do, since the email and password is input directly into the facebook login window, how do I check if the user already has a token?
Thanks in advance
The access token can change any time, you need to get it everytime. After getting the token, I immediately get the user information https://graph.facebook.com/me?access_token=??? and use that ID to find their database information.
I couldn't quickly find facebook information but on google's oauth information it says "The access token is also associated with a limited scope that define the kind of data the your client application has access to (for example "Manage your tasks"). An important goal for OAuth 2.0 is to provide secure and convenient access to the protected data, while minimizing the potential impact if an access token is stolen."
https://code.google.com/p/google-api-php-client/wiki/OAuth2
Ok so I finally figured it out myself. My mistake was apparently requesting the access_token directly (ie https://www.facebook.com/dialog/oauth?response_type=token...) to try and save time.
I fixed it by making a request for a 'code' instead (ie https://www.facebook.com/dialog/oauth?response_type=code), which I then use to make a second request to retrieve an access token as documented here: https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/, "Exchanging code for an access token" section a bit lower on the page.
Hope this helps someone in the future, this was very frustrating on my part.
Regards,
Prince

Replace "via Graph API Explorer" label by my application name

I am quite new in Facebook application development. I was playing the all day with "how to post a message on the wall of a page?". I finally succeeded but each message got "via Graph API Explorer". I tried to find how to change it to my application name without success. I tried to see if I could force the value of application in the api command but it did not take it into account. Maybe I miss something :( If someone can help, that would be great!
I am still quite confused. Let me try to explain what I want to do: I would like to automatically publish on a page (as the page) some event that are defined on a website (in a kind of agenda). What I miss, I think, is how everything is working together on Facebook side:
1. the login process: as the application will run in a cron, this should not display a login dialog box.
2. the access token: application or page one?
3. the permissions: from my understanding, I need manage_pages (and publish_stream) but not clear how this should be set.
Thx for any clarification and maybe a clear example :o)
You need the user to authorise your own App using one of the Login flows and grant you one of the publishing Permissions -
If it says 'via Graph API Explorer' on the posts your app makes you're using the access token you retrieved when you were testing the API using the Graph API Explorer tool, not one produced by your own app
OK I think I have finally found the way to do it. I needed a page access code and not an application access code. The token must be generated outside the application as a long live one.
Get a code using:
https://www.facebook.com/dialog/oauth?client_id={app_id}&redirect_uri={my_url}&scope=manage_pages,publish_stream
app_id is your application ID
my_url is your application URL
scope is the permission you want to be granted
In the redirected URL, you will have a code parameter. Copy it.
Generate the user access code using:
https://graph.facebook.com/oauth/access_token?client_id={app_id}&redirect_uri={my_url}&client_secret={app_secret}&code={code}
app_secret is your application secret key
code is the code from step 1
You will get as output the user access token. This one is a short live one.
convert the short live to a long live user access token using:
https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id={app_id}&client_secret={app_secret}&fb_exchange_token={short live access token}
Replace the "short live access token" by the one you got on step 2
You will get as output the infinite user access token.
Get the page access token (this one will be infinite access token as
the user access token is now an infinite access token too):
https://graph.facebook.com/me/accounts?access_token={infinite user access token}
Replace the "infinite user access token" with the value you got on step 3.
This command will list all the pages you administer. The output contains the page access token you need in field "access_token". You can so use this token in any API command in your application.
The best of the best is to do all those steps via a server side program (PHP for me) as the application secret key should remain "secret".