Get a refresh token for Dropbox API using rdrop2 and drop_auth() - dropbox-api

I'm trying to create a shiny app which links to my dropbox using the package rdrop2.
I have successfully managed to deploy the app and it runs as planned for around 4 hours. However, I need long lasting offline access. Dropbox help pages say that I'll need a 'refresh token'.
Currently to get my token I am using:
library(rdrop2)
token <- drop_auth() # gets credentials
saveRDS(token, "droptoken.rds") # saves credentials
token<-readRDS("droptoken.rds") # read in credentials
drop_auth(new_user = FALSE,
cache = TRUE,
rdstoken = "droptoken.rds")
ui <- # some UI code
server <- function(input, output,session) {
# some server code
}
shinyApp(ui = ui, server = server)
This creates a token that has a 'sl.' access token (short lived) which expires_in 14400, which is 4 hours. After 4 hours, the app no longer works due to an 'Unathorised HTTP 401' error.
Could anyone provide me help on how to adapt this code to obtain a refresh token?
NB: dropbox no longer allow tokens with no expiry (as of September 2021) so I need to go down the refresh token route.

I don't know if you still need help but I was recently tasked to address this issue in my internship program and managed to find a solution.
Currently, rdrop2's drop_auth() function will generate a token with these credentials: <credentials> access_token, token_type, expires_in, uid, account_id. Since access tokens last only 4 hours now, we will need a refresh token as a part of our credentials so that we may get a new access token every time it expires.
To generate a token object with a refresh token, we will need to modify the authorization endpoint inside the drop_auth() function. This will look something like this:
library(rdrop2)
.dstate <- new.env(parent = emptyenv())
drop_auth_RT <- function (new_user = FALSE, key = "mmhfsybffdom42w", secret = "l8zeqqqgm1ne5z0", cache = TRUE, rdstoken = NA)
{
if (new_user == FALSE & !is.na(rdstoken)) {
if (file.exists(rdstoken)) {
.dstate$token <- readRDS(rdstoken)
}
else {
stop("token file not found")
}
}
else {
if (new_user && file.exists(".httr-oauth")) {
message("Removing old credentials...")
file.remove(".httr-oauth")
}
dropbox <- httr::oauth_endpoint(authorize = "https://www.dropbox.com/oauth2/authorize?token_access_type=offline",
access = "https://api.dropbox.com/oauth2/token")
# added "?token_access_type=offline" to the "authorize" parameter so that it can return an access token as well as a refresh token
dropbox_app <- httr::oauth_app("dropbox", key, secret)
dropbox_token <- httr::oauth2.0_token(dropbox, dropbox_app,
cache = cache)
if (!inherits(dropbox_token, "Token2.0")) {
stop("something went wrong, try again")
}
.dstate$token <- dropbox_token
}
}
refreshable_token <- drop_auth_RT()
This will generate a token with these credentials: <credentials> access_token, token_type, expires_in, refresh_token, uid, account_id. The new token will now have a refresh token that the HTTP API can use to automatically refresh the access token.
It is very important that you specify the new token when calling rdrop2 functions, otherwise it will not work. Example:
> drop_dir()
Error in drop_list_folder(path, recursive, include_media_info, include_deleted, :
Unauthorized (HTTP 401).
> drop_dir(dtoken = refreshable_token)
# A tibble: 4 x 11
.tag name path_lower path_display id client_modified server_modified rev size
<chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <int>
1 folder Screensh~ /screensh~ /Screenshots id:_~ NA NA NA NA
Hope this helps!

Related

authlib.jose.errors.InvalidClaimError: invalid_claim: Invalid claim "iss"

I'm building an oauth2 client with Flask and Authlib. My code to register the oauth is:
google = oauth.register(
name='google',
client_id='',
client_secret="",
access_token_url="https://accounts.google.com/o/oauth2/token",
access_token_params=None,
authorize_url="https://accounts.google.com/o/oauth2/auth",
authorize_params=None,
api_base_url="https://www.googleapis.com/oauth2/v1/",
client_kwargs={'scope': 'openid email'},
server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
)
And my /authorize endpoint looks like this:
#app.route('/authorize')
def authorize():
google = oauth.create_client('google')
token = google.authorize_access_token()
resp = google.get('userinfo')
resp.raise_for_status()
userinfo = resp.json()
return str(userinfo)
But I am getting the error
authlib.jose.errors.InvalidClaimError: invalid_claim: Invalid claim "iss"
I had this issue and removing the openid value from scope fixed it. I guess my google config didn't accomodate it,

Connecting to Facebook's API

I am facing this issue connecting with Facebook's API using httr package, while testing on 'me' node I came along the following problem.
I was under the impression that me node does not require special permissions.
Testing on the browser with 'https://graph.facebook.com/me' gave the same results, it would be great if some one could provide an explanation.
# Define keys
app_id = 'my_app_id'
app_secret = 'my_app_secret'
# Define the app
fb_app <- oauth_app(appname = "facebook",
key = app_id,
secret = app_secret)
# Get OAuth user access token
fb_token <- oauth2.0_token(oauth_endpoints("facebook"),
fb_app,
scope = 'public_profile',
type = "application/x-www-form-urlencoded",
cache = TRUE)
response <- GET("https://graph.facebook.com",
path = "/me",
config = config(token = fb_token))
# Show content returned
content(response)
$error
$error$message
[1] "An active access token must be used to query information about the current user."
$error$type
[1] "OAuthException"
$error$code
[1] 2500
$error$fbtrace_id
[1] "ARRnb93rZHmWLlXK_MMJlfi"
Noting that I have signed in using the app.

SignatureDoesNotMatch with Google's serviceAccounts.signBlob API

I am trying to generated a signed URL to an object stored on Google Cloud Storage (GCS).
Attempt 1: try the API using the API Explorer
For this, I am trying to sign the blob/object as defined in the following:
GET
<expiration time>
/<bucket name>/<object/blob name>
I first tried Google's serviceAccounts.signBlob API as discussed in the following page:
https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signBlob
A base64-encoded string.
Note, as mentioned in the API documentation on the above-linked page, I pass a base64 representation of the blob I want to sign, to the API.
The API's response has the following structure where it contains the signedBlob key:
{
"keyId": "...",
"signedBlob": "..."
}
then I generated a signed URL using the obtained signed blob as the following:
encoded_signedBlob = base64.b64encode(signedBlob)
signed_url = "https://storage.googleapis.com/{}/{}?" \
"GoogleAccessId={}&" \
"Expires={}&" \
"Signature={}".format(
bucket_name, blob_name,
service_account_email,
expiration,
encoded_signedBlob)
and when I paste that signed URL in the browser to download the blob, I get the following error:
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.
</Message>
<StringToSign>GET <bucket name> <blob/object name></StringToSign>
</Error>
Attempt 2: try python libraries
Then I tried to implement it in python as the following, but still getting the same error.
# -------------
# Part 1: obtain access token using the authorization flow discussed at:
# https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials
# -------------
client_service_account = "..."
access_token = build(
serviceName='iamcredentials',
version='v1',
http=http
).projects().serviceAccounts().generateAccessToken(
name="projects/{}/serviceAccounts/{}".format(
'-',
service_account_email),
body=body
).execute()["accessToken"]
credentials = AccessTokenCredentials(access_token, "MyAgent/1.0", None)
# -------------
# Part 2: sign the blob
# -------------
service = discovery.build('iam', 'v1', credentials=credentials)
name = 'projects/.../serviceAccounts/...'
encoded = base64.b64encode(blob)
sign_blob_request_body = {"bytesToSign": encoded}
request = service.projects().serviceAccounts().signBlob(name=name, body=sign_blob_request_body)
response = request.execute()
keyId = response["keyId"]
signedBlob = response["signature"]
# -------------
# Part 3: generate signed URL
# -------------
encoded_signedBlob = base64.b64encode(signedBlob)
signed_url = "https://storage.googleapis.com/{}/{}?" \
"GoogleAccessId={}&" \
"Expires={}&" \
"Signature={}".format(
bucket_name, blob_name,
service_account_email,
expiration,
encoded_signedBlob)
You could take a look to the implementation of this using the client libraries, you could give it a try, remember to include google-cloud-storage in your requirements.txt and a service account as specified in the code sample for python

getting NoAuthorization Header Missing Exception while using flask-jwt-extended

When I try this example and if the jet token is not provided by header I get error:
{
"msg": "Missing cookie \"access_token_cookie\""
}
example:
from flask import Flask, jsonify, request
from flask_jwt_extended import (
JWTManager, jwt_required, create_access_token,
jwt_refresh_token_required, create_refresh_token,
get_jwt_identity, set_access_cookies,
set_refresh_cookies, unset_jwt_cookies
)
from flask_jwt_extended.config import config
# NOTE: This is just a basic example of how to enable cookies. This is
# vulnerable to CSRF attacks, and should not be used as is. See
# csrf_protection_with_cookies.py for a more complete example!
app = Flask(__name__)
# Configure application to store JWTs in cookies. Whenever you make
# a request to a protected endpoint, you will need to send in the
# access or refresh JWT via a cookie.
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
# Set the cookie paths, so that you are only sending your access token
# cookie to the access endpoints, and only sending your refresh token
# to the refresh endpoint. Technically this is optional, but it is in
# your best interest to not send additional cookies in the request if
# they aren't needed.
app.config['JWT_ACCESS_COOKIE_PATH'] = '/api/'
app.config['JWT_REFRESH_COOKIE_PATH'] = '/token/refresh'
# Disable CSRF protection for this example. In almost every case,
# this is a bad idea. See examples/csrf_protection_with_cookies.py
# for how safely store JWTs in cookies
app.config['JWT_COOKIE_CSRF_PROTECT'] = False
# Set the secret key to sign the JWTs with
app.config['JWT_SECRET_KEY'] = 'super-secret' # Change this!
jwt = JWTManager(app)
# Use the set_access_cookie() and set_refresh_cookie() on a response
# object to set the JWTs in the response cookies. You can configure
# the cookie names and other settings via various app.config options
#app.route('/token/auth', methods=['POST'])
def login():
# username = request.json.get('username', None)
# password = request.json.get('password', None)
# if username != 'test' or password != 'test':
# return jsonify({'login': False}), 401
# print dir(config)
# Create the tokens we will be sending back to the user
access_token = create_access_token(identity="test")
refresh_token = create_refresh_token(identity="test")
# Set the JWT cookies in the response
resp = jsonify({'login': True, "cookie_key": config.access_cookie_name, "cooke_value": access_token})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp, 200
# Same thing as login here, except we are only setting a new cookie
# for the access token.
#app.route('/token/refresh', methods=['POST'])
#jwt_refresh_token_required
def refresh():
# Create the new access token
current_user = get_jwt_identity()
access_token = create_access_token(identity=current_user)
# Set the JWT access cookie in the response
resp = jsonify({'refresh': True})
set_access_cookies(resp, access_token)
return resp, 200
# Because the JWTs are stored in an httponly cookie now, we cannot
# log the user out by simply deleting the cookie in the frontend.
# We need the backend to send us a response to delete the cookies
# in order to logout. unset_jwt_cookies is a helper function to
# do just that.
#app.route('/token/remove', methods=['POST'])
def logout():
resp = jsonify({'logout': True})
unset_jwt_cookies(resp)
return resp, 200
# We do not need to make any changes to our protected endpoints. They
# will all still function the exact same as they do when sending the
# JWT in via a header instead of a cookie
#app.route('/api/example', methods=['GET'])
#jwt_required
def protected():
username = get_jwt_identity()
return jsonify({'hello': 'from {}'.format(username)}), 200
if __name__ == '__main__':
app.run(debug=True)
But in my office I have similar setup except I am not calling
username = get_jwt_identity()
I get NoAuthorization exception get raised.
how does this work ...
It's mean you not login and flask-jwt can't find any token on your cookies.
Do you login before call this resource?
check your cookie that returned from app.
In my case it was CORS error, I was using a different api address from the website

Facebook API extend personal token

I'm currently using facebook GraphApi (imported with facepy) to build a crawler that extracts information from a group that is not mine. Since facebook's API v2.5 doesn't support extracting information from a group that is not mine, I'm forced to used an older version. My question is, how can I extend my personal token lifetime, taking into account that I can't create an app because it forces me to use the most recent API version. I'll drop the code below:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import facebook
from facepy import GraphAPI
from facepy import get_extended_access_token
import json
import schedule
import time
group_id = "the group id" #grupo operações stop coimbra https://www.facebook.com/groups/operacaostopcoimbra/
access_token = "access token i took from facebook developers"
class Crawler():
def __init__(self, access_token):
self._access_token = access_token
def crawl(self):
graph = GraphAPI(self._access_token) #aceder à GraphAPI
extended_token = get_extended_access_token(access_token, APP ID, APP SECRET) #PROBLEM IS HERE!
print extended_token
data = graph.get(group_id + "/feed", page=False, retry=3, limit=25)
data = self._parseJson(data)
return data
def _parseJson(self, data):
if data["data"][0]["message"]:
return data["data"][0]["from"]["name"] + " " + data["data"][0]["from"]["id"] + " " + data["data"][0]["message"]
else:
return None
result = Crawler(access_token)
schedule.every(1).minutes.do(result.crawl())
while 1:
schedule.run_pending()
time.sleep(1)
Seems like, no app, no token...