Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication Issue #744

Closed
ericasaw opened this issue Jul 5, 2022 · 36 comments
Closed

Authentication Issue #744

ericasaw opened this issue Jul 5, 2022 · 36 comments
Assignees
Labels

Comments

@ericasaw
Copy link

ericasaw commented Jul 5, 2022

  • [ x ] I have checked that the SDK documentation doesn't solve my issue.
  • [ x ] I have checked that the API documentation doesn't solve my issue.
  • [ x ] I have searched the Box Developer Forums and reached out to Box Support directly and my issue hasn't yet been resolved.
  • [ x ] I have searched Issues on the GitHub and my exact issue isn't already reported.

Description of the Issue

I've been having issues with Box authentication through Python since I started using it. I am trying to use the Box Python SDK to generate permanent download links for thousands of files. I have figured out how to generate all of the links, but unfortunately when I first generated them they were not permanent. I now need to go and update all of the links, but the first time I did this, due to all of my authentication issues, I put in a developer token each hour for many many hours (weeks of time). I don't want to have to do this ever again so I am hoping to solve the authentication issue.

I have tried all of the different authentication methods recommended in the documentation. However, the only method that is successful for me is using the developer token. Here are some of the problems I ran into using various authentication methods:

  • using JWT: 'Access denied - insufficient permission' error
  • using JWT with user access token: I am not sure how to find the jwt_key_id, rsa_private_key_file_sys_path, or the rsa_private_key_passphrase. I am not sure if those are generated within the box developer or if I have to find them elsewhere? Particularly, the example for the rsa_private_key_file_sys_path has "CERT.PEM" as the example file but I have not had success generating such a file with the box developer. I thought that maybe this was the .json file that is generated from creating a public/private key-pair, but it doesn't seem to be the right type of file. I have also tried the following lines of code but I end up with an "'User' object has no attribute ‘authenticate_user'" attribute error, which I think can only be fixed by finding the above jwt_key_id, rsa_private_key_file_sys_path, and the rsa_private_key_passphrase:
auth = JWTAuth.from_settings_file('config.json’) #generated from the box developer website
client = Client(auth)
user = client.user(user_id='#######’) #my user ID
user.authenticate_user()
user_client = client.as_user(user)
  • using CCGAuth: "The "box_subject_type" value is unauthorized for this client_id" error
  • using OAuth2.0 Auth: "Message: No "refresh_token" parameter found" error and I am unsure where to find the refresh token on the Box Developer website for the app.

I’m sorry if this is generally straight forward for other developers to figure out, but I am just a grad student who isn’t really familiar with this type of developer programming and I find the API for all of the Box ecosystem pretty opaque.

I should also note that I have tried all of the above authentication methods with both the initial Box CLI app and another trial app that I generated which allows me to create the downloadable .json files with public/private key pairs.

Steps to Reproduce

All of the lines of code I tried for authorization I directly copied from the SDK auth wiki page and inserted my own user ID, .json file, etc., generated using the Box Developer. This leads me to believe that this might be an issue with setting up the Box Developer side of things--unless that code only works in very specific cases, but if so, then more detailed instructions should be added to the wiki for how to set that up properly so that the sample code will work.

Expected Behavior

Successful authentication that did not require a development token to be put in each hour.

Versions Used

Python SDK: 3.3.0
Python: 3.6.13

@lukaszsocha2
Copy link
Contributor

Hi @ericasaw,
ok let me explain what might have gone wrong in all cases:

  1. JWT

Have you went through all steps from this page? Especially have you done step: App Authorization? JWT requires submitting your application and your Box enterprise admin approval. Without your app being approved you cannot use JWT auth.

Also the easiest and recommended way to use JWT auth is to just pass path to your config file as the only parameter. The class will extract all required fields. So the code that it is recommended to use will be:

auth = JWTAuth.from_settings_file('config.json') #generated from the box developer website
client = Client(auth)
user = client.user(user_id='#######') #my user ID
user_client = client.as_user(user)
  1. CCG

Same issue as with JWT. Have you fulfil all Prerequisites described here? CCG also requires approving your application by the admin.

  1. Oauth2.0

This is the only method which does not require admin approval (except developer token), but will require from you to press "Grant Access" button each time you want to use it. All required steps are described here: Overview. The redirect step depends on a framework you use. Some time ago I wrote a small flask app, which does Oauth authentication and list all files in your root folder. You may find it useful:

from boxsdk import OAuth2, Client
from flask import Flask, request, redirect

app = Flask(__name__)

AUTH = OAuth2(
    client_id='YOUR CLIENT ID',
    client_secret='YOUR CLIENT SECRET'
)


@app.route("/")
def get_auth():
    auth_url, csrf_token = AUTH.get_authorization_url('http://localhost:5000/oauth2callback')
    return redirect(auth_url, code=302)


@app.route("/oauth2callback")
def callback():
    AUTH.authenticate(request.args.get("code"))
    client = Client(AUTH)

    root_folder = client.root_folder()
    list_of_items = ""
    for item in root_folder.get_items():
        list_of_items += f'{item.name}, '

    return list_of_items


if __name__ == '__main__':
    app.run(debug=True)

To make it work you have to set http://localhost:5000/oauth2callback value in the Dev Console as described here: Redirect URI


Also:
Check what type of authentication have you chosen while creating your app. You should use the authentication type you selected. See: App creation steps.

To summarise: think which auth type do you want to use, create an app with authentication type you have chosen and try to go through all steps described in developer guides (https://developer.box.com/guides/authentication/). If this answer won't solve all your concerns, don't hesitate to ask again.
Best,
@lukaszsocha2

@ericasaw
Copy link
Author

ericasaw commented Jul 5, 2022

Hi Lukas,
Thanks for the detailed response! For both the JWT and the CCG I have completed the set up for the app as described in the Box API documentation, obtained the app authorization, and it was approved by my organization, but I still cannot get either method to work. Your script for the Oauth2.0 is very helpful, I will try implementing the method and get back to you on if it works for me!

@lukaszsocha2
Copy link
Contributor

Hi @ericasaw,
do you remember which authentication type have you chosen during app creation? If it was OAuth 2.0 then JWT and CCG won't work for you.
image
@lukaszsocha2

@ericasaw
Copy link
Author

ericasaw commented Jul 6, 2022

Hi Lukas,
The original Box CLI app I had set up is configured for OAuth 2.0 (just by following the directions here). The trial application I created & had approved was for the JWT Auth (by following the directions here). I don't think I ever generated one for the CCG which is probably why it has never worked for me. It's good to know that the app you make can only have one authentication method (other than developer). This will help me for troubleshooting!

@lukaszsocha2
Copy link
Contributor

Hi @ericasaw,
have you managed to authenticate using any other method than developer token?

@ericasaw
Copy link
Author

ericasaw commented Jul 7, 2022

Hi Lukas,
I am still having a bit of difficulty with the OAuth 2.0 example you have above. I've changed the Redirect URI like you've mentioned, but when I put the http://127.0.0.1:5000/ url in chrome, after clicking the grant access button I get an "Access to localhost was denied HTTP ERROR 403" message. I think this might have to do with MacOS using port 5000 regularly so I cannot use it for my application, do you know how I can change the port the flask runs on?

@lukaszsocha2
Copy link
Contributor

@ericasaw
Copy link
Author

ericasaw commented Jul 7, 2022

Hi Lukas,
Is it normal for the localhost website to have a continuous loading wheel after granting access or is something else meant to happen?

@ericasaw
Copy link
Author

ericasaw commented Jul 7, 2022

Hi Lukas,
Maybe I should provide a more detailed example of what I'm trying to accomplish here is a script that I am testing with:

  from boxsdk import OAuth2, Client
  import _pickle as pickle
  import glob
  from flask import Flask, request, redirect

  app = Flask(__name__)

  auth = OAuth2( client_id='detzn3cmpd7wgjl9mk84af1mjerpnra1', 
  client_secret='9fyDak6LFP17i8aOkbESlF7olRXOyWfz')

  @app.route("/")
  def get_auth():
      auth_url, csrf_token = auth.get_authorization_url('http://localhost:3000/oauth2callback')
      return redirect(auth_url, code=302)

 @app.route("/oauth2callback")
 def callback():
     auth.authenticate(request.args.get("code"))
     client = Client(auth)

     RRISA_items = client.folder(folder_id='163358064246').get_items()
        
     general_folders = []
     for item in RRISA_items:
         #get the IDS for indata, outdata, and recipe
         general_folders.append(client.folder(folder_id=item.id).get_items())
          
     written = glob.glob("recipe_box_ids/*")
     w_fn = [fn.split('_')[0] for fn in written[:-1]]
          
     #in indata, outdata, or recipe get the date folder IDS
     for file in general_folders[2]:
         ID_organize = {}
         if (file.name).split('.')[0] not in w_fn:
             #set the key equal to the download link for the file so we dont even have to mess with the IDs, make the link perm
             ID_organize[str(file.name)] = client.file(file.id).get_shared_link_download_url(access='open', unshared_at = None)
             #dump the dictionary with filename and file download link
             pickle.dump(ID_organize, open(f"recipe_box_ids/{(file.name).split('.')[0]}_recipe.pkl", "wb"))

I get the following error "TypeError: The view function for 'callback' did not return a valid response. The function either returned None or ended without a return statement." because I don't have a return for the callback function, but I don't really need one for my specific application. Or did I build the function wrong? should I just be returning the client to the script and then generating the permanent download links?

@lukaszsocha2
Copy link
Contributor

lukaszsocha2 commented Jul 7, 2022

@ericasaw So maybe can you just make callback() return empty string? I also don't understand why don't you try to create new app with CCG or JWT auth method and get your app authenticated. The usage of these auth methods will be much easier than OAuth2 and won't require your interaction during authentication process.

@ericasaw
Copy link
Author

ericasaw commented Jul 7, 2022

Hi Lukas,
Okay I can try with the other authentication methods, I was just trying the OAuth since I had an app for it and it takes my organization weeks sometimes to approve other apps. The app that I had created for JWT Auth is authorized by my organization, but still does not work with the example code you have provided, still with the "Code: access_denied_insufficient_permissions" error. I'm not sure how else to fix this particular issue for JWT Auth as I set everything up following the directions from the same link you recommended. Does the app have to be re-authenticated for each public/private key pair generated?

@lukaszsocha2
Copy link
Contributor

@ericasaw but also did you try to make callback() return empty string?

@lukaszsocha2
Copy link
Contributor

Can you tell me on which line does your code throws exception with JWT? Is it client = Client(auth) or user_client = client.as_user(user)?

@ericasaw
Copy link
Author

ericasaw commented Jul 7, 2022

It is only when I try to use the user_client that that specific error is thrown. If I just try to use the client I get a different error that says the ID (corresponding to the folder that holds all the data) doesn't exist. Haven't tried to make callback return an empty string yet.

@lukaszsocha2
Copy link
Contributor

lukaszsocha2 commented Jul 7, 2022

Ok so I know why JWT didn't work: You must have as-user headers enabled: Go to Configuration of your app in Dev Console and then select checkbox Make API calls using as-user header.
image
Then save changes and submit your app for reapproval. This should help ;)

@ericasaw
Copy link
Author

ericasaw commented Jul 7, 2022

Okay! App is resubmitted for approval, I'll update you when it's approved.

@ericasaw
Copy link
Author

ericasaw commented Jul 8, 2022

Hi Lukas!
I had the app reauthorized (for JWT) last night. I downloaded the .json file again, but it doesn't include my public/private key. Do I need to generate a new one and have that .json file read in instead? I'm not sure if the app needs to be reauthorized each time I generate a key pair.

I tried copying the old key pair into the new .json file and I still get the same "Code: access_denied_insufficient_permissions" error from before even with the make API calls using the as-user header checked in the box developer app.

@lukaszsocha2
Copy link
Contributor

Hi,
I checked and no reauthentication is needed when you generate new keypair. So I suggest you to:

  1. Generate a new keypair by clicking Generate a Public/Private Keypair
  2. A file with configuration file should download automatically.
  3. Put path to this file in your code.

Let me know if it worked.
Best,
@lukaszsocha2

@ericasaw
Copy link
Author

ericasaw commented Jul 8, 2022

Hi Lukas,
Unfortunately even with a new key pair and app authorization I still get the "Code: access_denied_insufficient_permissions" error.

@ericasaw
Copy link
Author

ericasaw commented Jul 8, 2022

My CCG app was approved as well so I tried authenticating using the information from that app with:

auth = CCGAuth(
client_id="my_client_id",
client_secret="my_client_secret",
user="my_user")

client = Client(auth)

but I get the following error:

"GET https://api.box.com/2.0/folders/163358064246/items?offset=0" 500 182
{'Date': 'Fri, 08 Jul 2022 17:06:54 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'x-envoy-upstream-service-time': '359', 'box-request-id': '0a1505c8b99a802e28a0ec8ab3f44bdea', 'cache-control': 'no-cache, no-store', 'strict-transport-security': 'max-age=31536000'}
{'code': '---rror',
 'help_url': 'http://developers.box.com/docs/#errors',
 'message': 'Internal Server Error',
 'request_id': 'e4r1xah400ls9yqf',
 'status': 500,
 'type': 'error'}

"GET https://api.box.com/2.0/folders/163358064246/items?offset=0" 500 182
{'Date': 'Fri, 08 Jul 2022 17:06:56 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'x-envoy-upstream-service-time': '75', 'box-request-id': '1446244467da6adb295013dbf22e6ff7e', 'cache-control': 'no-cache, no-store', 'strict-transport-security': 'max-age=31536000'}
{'code': '---rror',
 'help_url': 'http://developers.box.com/docs/#errors',
 'message': 'Internal Server Error',
 'request_id': '1katv8h400lt772u',
 'status': 500,
 'type': 'error'}

"GET https://api.box.com/2.0/folders/163358064246/items?offset=0" 500 182
{'Date': 'Fri, 08 Jul 2022 17:06:58 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'x-envoy-upstream-service-time': '77', 'box-request-id': '020b0186fcda7cbc17fc6b3dbf2d442c4', 'cache-control': 'no-cache, no-store', 'strict-transport-security': 'max-age=31536000'}
{'code': '---rror',
 'help_url': 'http://developers.box.com/docs/#errors',
 'message': 'Internal Server Error',
 'request_id': 'jd6m1ih400luj7ow',
 'status': 500,
 'type': 'error'}

"GET https://api.box.com/2.0/folders/163358064246/items?offset=0" 500 182
{'Date': 'Fri, 08 Jul 2022 17:07:01 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'x-envoy-upstream-service-time': '87', 'box-request-id': '086f4acaca05b68b9eb16fa1d5d887418', 'cache-control': 'no-cache, no-store', 'strict-transport-security': 'max-age=31536000'}
{'code': '---rror',
 'help_url': 'http://developers.box.com/docs/#errors',
 'message': 'Internal Server Error',
 'request_id': 'gzwjw4h400lwp5mj',
 'status': 500,
 'type': 'error'}

"GET https://api.box.com/2.0/folders/163358064246/items?offset=0" 500 182
{'Date': 'Fri, 08 Jul 2022 17:07:09 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'x-envoy-upstream-service-time': '80', 'box-request-id': '06e68760d7f669ae481ee6c4c70b1e570', 'cache-control': 'no-cache, no-store', 'strict-transport-security': 'max-age=31536000'}
{'code': '---rror',
 'help_url': 'http://developers.box.com/docs/#errors',
 'message': 'Internal Server Error',
 'request_id': 'zdyuyuh400m18c3b',
 'status': 500,
 'type': 'error'}

"GET https://api.box.com/2.0/folders/163358064246/items?offset=0" 500 182
{'Date': 'Fri, 08 Jul 2022 17:07:27 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'x-envoy-upstream-service-time': '103', 'box-request-id': '0c1b83bedcdb410fc9b403e6ce2c121c8', 'cache-control': 'no-cache, no-store', 'strict-transport-security': 'max-age=31536000'}
{'code': '---rror',
 'help_url': 'http://developers.box.com/docs/#errors',
 'message': 'Internal Server Error',
 'request_id': '2ol1k6h400mcr40f',
 'status': 500,
 'type': 'error'}

Traceback (most recent call last):
  File "Recipe_IDs.py", line 22, in <module>
    for item in RRISA_items:
  File "/Users/ericasaw/opt/miniconda3/envs/photometry/lib/python3.6/site-packages/boxsdk/pagination/box_object_collection.py", line 81, in next
    return next(self._all_items)
  File "/Users/ericasaw/opt/miniconda3/envs/photometry/lib/python3.6/site-packages/boxsdk/pagination/box_object_collection.py", line 87, in _items_generator
    response_object = self._load_next_page()
  File "/Users/ericasaw/opt/miniconda3/envs/photometry/lib/python3.6/site-packages/boxsdk/pagination/box_object_collection.py", line 124, in _load_next_page
    box_response = self._session.get(self._url, params=params)
  File "/Users/ericasaw/opt/miniconda3/envs/photometry/lib/python3.6/site-packages/boxsdk/session/session.py", line 90, in get
    return self.request('GET', url, **kwargs)
  File "/Users/ericasaw/opt/miniconda3/envs/photometry/lib/python3.6/site-packages/boxsdk/session/session.py", line 134, in request
    response = self._prepare_and_send_request(method, url, **kwargs)
  File "/Users/ericasaw/opt/miniconda3/envs/photometry/lib/python3.6/site-packages/boxsdk/session/session.py", line 337, in _prepare_and_send_request
    self._raise_on_unsuccessful_request(network_response, request)
  File "/Users/ericasaw/opt/miniconda3/envs/photometry/lib/python3.6/site-packages/boxsdk/session/session.py", line 273, in _raise_on_unsuccessful_request
    network_response=network_response
boxsdk.exception.BoxAPIException: Message: Internal Server Error
Status: 500
Code: internal_server_error
Request ID: 2ol1k6h400mcr40f
Headers: {'Date': 'Fri, 08 Jul 2022 17:07:27 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'x-envoy-upstream-service-time': '103', 'box-request-id': '0c1b83bedcdb410fc9b403e6ce2c121c8', 'cache-control': 'no-cache, no-store', 'strict-transport-security': 'max-age=31536000'}
URL: https://api.box.com/2.0/folders/163358064246/items
Method: GET
Context Info: None

I'm not sure what an internal service error indicates, do you have any ideas?

@cristynkells
Copy link

ericasaw, the "Internal Server Error" is a different issue than the auth issue in the issue description. However, I'd like to state that we are also receiving this error. There are some intermittent issues going on with the Box servers right now if I had to guess.

  File "boxsdk/pagination/box_object_collection.py", line 81, in next
  File "boxsdk/pagination/box_object_collection.py", line 87, in _items_generator
  File "boxsdk/pagination/box_object_collection.py", line 124, in _load_next_page
  File "boxsdk/session/session.py", line 90, in get
  File "boxsdk/session/session.py", line 134, in request
  File "boxsdk/session/session.py", line 337, in _prepare_and_send_request
  File "boxsdk/session/session.py", line 264, in _raise_on_unsuccessful_request
boxsdk.exception.BoxAPIException: Message: Internal Server Error
Status: 500
Code: internal_server_error
Request ID: 3llc7h400r53ja0
Headers: {'Date': 'Fri, 08 Jul 2022 17:12:03 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'x-envoy-upstream-service-time': '78', 'box-request-id': '0de4280d7c30a4c605598d8e59a14cde7', 'cache-control': 'no-cache, no-store', 'strict-transport-security': 'max-age=31536000'}
URL: https://api.box.com/2.0/search
Method: GET
Context Info: None

@ericasaw
Copy link
Author

ericasaw commented Jul 8, 2022

Okay good to know! I wasn't sure if it was related to me setting up my app wrong, I might just try to run the CCG Auth again tomorrow to see if things are fixed.

@ericasaw
Copy link
Author

Okay the CCG Authentication method seems to work this morning! Thank you all so much for your help. I still cannot get the JWT Auth to work, maybe it is a university limitation? I am open to trouble shooting that further since it seems like the preferred authentication method.

@kailashlimba1997
Copy link

Hi, @ericasaw @lukaszsocha2 I'm facing the same problem again and again. Can you help me with that? I used JWT but getting permission denied errors and with CCG I'm getting Grant credentials are invalid.

@arjankowski
Copy link
Contributor

hi @kailashlimba1997 ,

To make JWT and CCG apps works, you need to authorize your app.
To check if you did it, please open the developer console, select your application, and then go to the Authorization tab.
Your app should have Authorization Status: Authorized and Enabled Status: Enabled.

Screenshot 2022-08-22 at 16 43 28

If your app is not yet authorized, click Review and Submit to ask the Box Enterprise administrator for approval.
Read this for more information: https://developer.box.com/guides/authentication/jwt/jwt-setup/#app-authorization.

Please let me know about the result
@arjankowski

@kailashlimba1997
Copy link

Hi, @arjankowski Thanks for replying. I've authorized the application and enable status is also enabled. Still, I'm facing this issue.

@ericasaw
Copy link
Author

Hi @kailashlimba1997,
I generated a new app in the box developer console specifically for the CCG method. Under the configuration tab, I selected the app + enterprise access option for the app access level. Under application scopes I checked the "write all files and folders stored in Box", "manage users", "manage groups", and "manage enterprise properties" boxes. I didn't check anything else. After doing all of that, I then clicked the review and submit button under the authorization tab. It's important to note that nothing will work until the app has been approved by whatever admin is in control of the organization's Box settings. After the app was approved I used the following lines in python to generate the client:

from boxsdk import Client, CCGAuth

auth = CCGAuth(
  client_id="", #your client ID in the box developer app
  client_secret="", #your client secret in the box developer app
  user="" #your user ID in your box developer app
)

client = Client(auth)

I hope that works! I was never able to figure out the JWT Auth and the Box team has not been very responsive when I reached out through the support channels so I just gave up trying to use that method.

@kailashlimba1997
Copy link

Hi @ericasaw @arjankowski After writing the below code

from boxsdk import Client, CCGAuth

auth = CCGAuth(
client_id="", #your client ID in the box developer app
client_secret="", #your client secret in the box developer app
user="" #your user ID in your box developer app
)

client = Client(auth)

events = client.events().generate_events_with_long_polling()
print(events)
for event in events:
print(f'Got {event} event')

I'm getting this error, message Message: Grant credentials are invalid

@ericasaw
Copy link
Author

HI @kailashlimba1997,
Did you add your client ID, client secret, and user ID from your CCG Auth application (between the "" placeholders in the code I wrote above)?

@kailashlimba1997
Copy link

Yes, I write in inverted commas. Can I have your contact and discuss this issue on zoom/meet?

@ericasaw
Copy link
Author

@kailashlimba1997 since I am not a box developer I am not really comfortable meeting or spending a ton of time helping with resolving issues like these. Hopefully one of the developers in this thread can help!

@kailashlimba1997
Copy link

Hi @antusus @mhagmajer @cristynkells @lukaszsocha2, can anyone help me with the above issue?

@arjankowski
Copy link
Contributor

@kailashlimba1997
Did you manage to resolve this issue?
If not please contact me on email so we may arrange some call.

@kailashlimba1997
Copy link

No, the error is still not resolved. I've emailed you on ajankowski@box.com for a call.

@kailashlimba1997
Copy link

If you haven't received the email, please ping me at kailash@nroadcorp.com whenever you get time.

@kailashlimba1997
Copy link

Thanks, @arjankowski. The CCG credential (for the user) worked for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants