Welcome to the new Box Support website. Check out all the details here on what’s changed.

How to set up JWT Auth on Box.com's end?

Answered
New post

Comments

15 comments

  • jcleblanc

    Hi ,

     

    When you create a JWT application, you'll be able to make API requests with a developer token, since that token points to your Box account. If you try to go through the OAuth 2 / JWT auth process for the app and make API requests without using a developer token, those requests will all fail and give you an unauthorized error.

     

    When you go through the admin auth step, your admin will review the scopes that you're requesting access to and grant you access to do so in your company. Once approved those API calls will start to work. That's what the admin auth piece does.

     

    Just a note, the admin auth is for a snapshot in time. What I mean by that is if you go into your app, change the scopes and permissions to request things that the admin didn't approve your app for, those API calls will still fail until the admin reauthorizes your app.

     

    Hope that helps,

    Jon 

    0
    Comment actions Permalink
  • Tunnelvisie

    I still don't understand. 

     

    I have two ways to connect to box set up, one via developer token:

     

    def connectToBoxOptionA():
    # Read app info from text file
    try:
    with open('box.json', 'r') as box_cfg:
    config = json.load(box_cfg)

    auth = OAuth2(
    client_id=config['client_id'],
    client_secret=config['client_secret'],
    access_token=config['dev_token'],
    )
    client = Client(auth)
    return client
    except Exception:
    print("Need to create new Developer Token, these are only valid for 1 hour")

     And one via JWT

     

    def connectToBoxOptionB():
    # Read app info from text file
    auth = JWTAuth.from_settings_file('box_JWT.json')
    client = Client(auth)
    return client

     When I use the developer token 'connectToBoxOptionA()' everything works fine. When I use 'connectToBoxOptionB()' I get a 404 error responds from the server and a 'attr.exceptions.FrozenInstanceError'. 

     

    I don't think I fully understand what I'm supposed to do here. Is OptionB the right approach? Or am I missing something here?

    0
    Comment actions Permalink
  • Tunnelvisie

    Would you mind looking at my follow-up question? Much appreciated!

    0
    Comment actions Permalink
  • Jason

    Hi  ,

     

    The 404 is coming from the fact that your developer token is tied to your Box user, while the token you get from the JWT is by default tied to the Service Account for the application. They're effectively two separate Box users. It's the second common scenario here:
    https://support.box.com/hc/en-us/articles/360043693734-API-Content-API-404-not-found-Errors-from-the-Box-API

    More info on user types (and where the Service Account comes from) here:

    https://developer.box.com/guides/authentication/user-types/

     

    If you were to start uploading content as your Service Account (i.e. using your JWT client), you would be able to interact with that content with your JWT client. Alternatively, you could also start adding collaborations between users on content, just as you would to share content between any two users.

     

    Hope that helps explain it!

     

    Thanks,

    Jason

    0
    Comment actions Permalink
  • Tunnelvisie

    Thanks , but I'm still confused by this.

     

    Here it says:

    Service Accounts are automatically created when a Custom App is created in the developer console with the JWT authentication method. When these applications authenticate the default user it authenticates as is the Service Account for that application.

     

    As shown above I try to log-in with JWT using the JWT.json file that is literally created on Box.com.

    Is that the wrong JWT file then?

     

    Could you more clearly state what I have to do? When I click on 'add the user as a collaborator on the file' here it takes me to some silly overview page (which btw has happened a lot while research this problem, hence why I'm not getting anywhere).

     

    Also would you mind explaining what you mean by this?

     

    If you were to start uploading content as your Service Account (i.e. using your JWT client), you would be able to interact with that content with your JWT client. Alternatively, you could also start adding collaborations between users on content.

     

    I've never used this Box API before so all the words you are saying make about zero sense to me. Thanks.

     

    0
    Comment actions Permalink
  • Tunnelvisie

     so the main concerns here are:

     

    - Where do I get the correct JWT.json

    - Who, what and where do I add this Service Account to collaborate on my folder?

     

    0
    Comment actions Permalink
  • Jason

     

     

    I can definitely explain! I can definitely relate to the learning curve, it's a lot to take in at once. I'll go through it step by step:

     

    As shown above I try to log-in with JWT using the JWT.json file that is literally created on Box.com.

    Is that the wrong JWT file then?

     

    You'll want to use that JWT file for sure (the JWT file ties your code, your app, and your enterprise together), but the thing to understand is what happens if you just use the default client vs a user-specific client. When you use a default client, API calls operate in the context of the Service Account, which you can think of as just one Box user. When you use a more specific client, using an app user as an example:

    https://github.com/box/box-python-sdk/blob/594df43e1f5eec1b9fc714a58f03fd0756d1776b/docs/usage/authentication.md#server-auth-with-jwt

     

     

    app_user = service_account_client.user(user_id='APP_USER_ID')

     

    That client's calls will now operate in the context of the "app user", which again you can just think of as a separate Box user. 

     

    To relate this to the 404 you're getting, one Box user cannot make API calls to another user's content unless a collaboration exists on the file or folder to the outside user. What's happening during the 404 is that your JWT client is operating in the context of your service account, while your developer token "client" is operating under the context of your Box account, i.e. the one you log into and navigate in the web UI at box.com.

     

    For the collaboration bit, this is the correct link:

    https://developer.box.com/reference/post-collaborations/

    For your app, I would recommend avoiding adding your Service Account as a collaborator if at all possible. JWT Auth is designed for applications that only interact with their own content (i.e. content owned by the Service Account and its App Users). While technically possible, you can get into some odd scenarios when you start collaborating app users and service accounts into content owned by managed users (our term for accounts used in the web app). 

     

    Instead, it sounds like what you're trying to do is just use your python code to interact with your Box account/managed user, is that correct? In that case, given that your app has both:

    * Enterprise "Application Access" (in the developer console)

    * Generate User Access Tokens

     

    And that you've reauthorized the app (see jon's note from before), you can replace the user_id argument in the python snippet above with your own User ID. This will give you a client in the context of your own Box user, just like how you can target the user ID of an app user. As a caveat, this also makes your application able to make calls for any user in your enterprise, so you'll want to balance that access with other access controls in your code, depending on what you're building.

     

    Thanks,

    Jason

    0
    Comment actions Permalink
  • Tunnelvisie

     thanks for trying to explain it to me. I really appreciate that you're taking the time to explain, but I have a feeling that either I'm terrible at understanding what to do, or the Box documentation is not setup for me to understand it. All in all, it's stupendously frustrating that even after 4 answers from you guys I have literally no idea what to do. 

     

    I re-read your answer 4 times, and didn't get any further. Simply because it's not clear what to do, while all I try to do is have my python script upload some files to a shared box folder (shared with other normal users), or at least, that's the plan.

     

    What is the code that I need to do that? Like, what combination of all this stuff here https://github.com/box/box-python-sdk/blob/594df43e1f5eec1b9fc714a58f03fd0756d1776b/docs/usage/authentication.md#server-auth-with-jwt do I need to use my JWT Auth in order to not need to generate a new dev token every hour.

     

    I already had both of the things below setup.

    * Enterprise "Application Access" (in the developer console)

    * Generate User Access Tokens

     

    Just give me a snippet of code I can copy paste to set it up, at this point I don't really care anymore about understanding it, all I want to do is just copy some files into a folder. It can't be that hard.

     

     

     

     

     

     

     

    0
    Comment actions Permalink
  • Tunnelvisie

     I think one of the reasons I'm getting stuck is because there is no explanation of some of this stuff.

     

    Like I want to use the JWT.json file to set it up, but then it seems like I can't use it but need to use this instead:

     

    app_user_auth = JWTAuth(
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
    user=app_user,
    jwt_key_id='YOUR_JWT_KEY_ID',
    rsa_private_key_file_sys_path='CERT.PEM',
    rsa_private_key_passphrase='PASSPHRASE',
    store_tokens=your_store_tokens_callback_method,
    )
    app_user_auth.authenticate_user()
    app_user_client = Client(app_user_auth)

    (that might not even be true, but I don't know that)

    And if this is correct, then I can't use my JWT.json file? But you said I can. But you also said I need to use app_user, and the  JWTHAuth.from_settings_file() doesn't seem to have an option to feed it 'app_user', or does it. And if I can't use the JWT.json file, where do I get the jwt_key_id, and what is the your_store_tokens_callback_method etc.etc.etc? There is not reference of that anywhere.

     

    It also doesn't help that "See the API documentation for detailed instructions on how to use app auth." just links to the main documentation page, like cool where do I go now?

    0
    Comment actions Permalink
  • bx_dev2

     

    You can just direct the application to the file path where you config file lives on your machine.

    I was able to get a simple application up and running with just the below code:

     

     

     

     

    ## authenticates client
    from boxsdk import JWTAuth, Client
    config_path='/Users/username/Desktop/config.json'
    sdk = JWTAuth.from_settings_file(config_path)
    client = Client(sdk)
    
    ##prints out the service account's id and email. As mentioned previously, by default, a JWT application will run as the service account.
    
    service_account = client.user().get()
    print('Service Account email is {0}'.format(service_account.login))
    print('Service Account id is {0}'.format(service_account.id))

     

     

    This the documentation I referenced

    Oauth with JWT with Sdk

    https://developer.box.com/guides/authentication/jwt/with-sdk/ 

     

    Python Sdk jwt authentication

    https://github.com/box/box-python-sdk/blob/594df43e1f5eec1b9fc714a58f03fd0756d1776b/docs/usage/authentication.md#server-auth-with-jwt

    0
    Comment actions Permalink
  • Tunnelvisie

       

     

    So now I do what  says and then what?

     

    With the dev token I could return the client and then do:

     

    root_folder = client.folder(folder_id=folder_id).get()

    If I do the same thing with the JWTAuth I still get the 404 and if I do:

    root_folder = service_account.folder(folder_id=folder_id).get()

    It says AttributeError: ("'User' object has no attribute 'folder'", 'occurred at index 0') which makes me believe that I shouldn't be using the service_account to access the folder?

     

    (folder_id is whatever folder I'm trying to put the files in)

     

    so what do I do with the service account?

     

     

    0
    Comment actions Permalink
  • Tunnelvisie

       

     

    So I figured out that I need to do this:

    folder_id = '*********'

    auth = JWTAuth.from_settings_file('box_JWT.json')
    client = Client(auth)
    service_account = client.user().get()
    print('Service Account user ID is {0}'.format(service_account.id))

    user_id = '***********'
    box_file = client.as_user(user_id).folder(folder_id).upload(file_path=file_path)

    But now I get AttributeError: 'str' object has no attribute 'object_id'

     

    When I google that I get to this page:

    https://community.box.com/t5/Platform-and-Development-Forum/API-Upload-files-in-Python/td-p/71848

     

    But that's not very helpful.. any suggestions?

    0
    Comment actions Permalink
  • Tunnelvisie

       

     

    So it seems like that error is another silly one. Where instead of providing the user_id as a string I need to provide 'client.user().get()' because that does have the 'object_id' attribute.

     

    However, what client do I use in this case? Do I use:

    service_account = client.user().get()

    or

    with open('box.json', 'r') as box_cfg:
    config = json.load(box_cfg)

    auth = OAuth2(
    client_id=config['client_id'],
    client_secret=config['client_secret'],
    access_token=config['dev_token'],
    )
    client = Client(auth)
    user = client.user().get()

    Both of them make no sense to me, because the first one is the service account and for the second one I need to use a dev_token, also both of them give me an access denied error. 

     

     

    0
    Comment actions Permalink
  • bx_dev2

     The service account isn't going to have access to any folders that other Box users own if it is not a collaborator on the folder. The easiest thing for you to do would probably be to add the service account as a collaborator to the folder in the web ui using its email.  

     

    ## This will print out all items in folder id 0 or the root folder. If you do this right now, it will probably return empty because the service account doesn't own or collaborate on any content.
    
    items = client.folder(folder_id='0').get_items()
    for item in items:
        print('{0} {1} is named "{2}"'.format(item.type.capitalize(), item.id, item.name))

     

     

     

    0
    Comment actions Permalink
  • Tunnelvisie

    Thanks inviting the email address you get from:

    service_account = client.user().get().login

    To the specific box-folder worked perfectly.

     

     

    0
    Comment actions Permalink

Please sign in to leave a comment.