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

Custom app with JWT authentication, cannot create file in folder

New post

Comments

5 comments

  • Rui Barbosa

    Hi Julien,

    I've tried to replicate your use case and I was able to upload a file. I think the source of the issue might be how the service account was added to the folder as co-owner.

    If I understood correctly there are 2 service accounts. A folder owned by service account A is shared with service account B and service account B uploads a file.

    In my test this worked:

    Service account A in my case is name JWT, has a folder named "JWT folder for UI Sample apps"

     

    I used the sharing link feature:

     

    and this python script works fine:

    from boxsdk import JWTAuth, Client
    from boxsdk.object.file import File


    class CFG:
        """config class"""

        JWT_CONFIG_FILE = ".jwt.config.json"
        AS_USER = "18622116055"
        PARENT_FOLDER_ID = "0"  # folder id 0 is root folder


    def get_box_client(as_user: bool = False):
        """get a box client"""
        auth = JWTAuth.from_settings_file(CFG.JWT_CONFIG_FILE)
        service_client = Client(auth)
        if not as_user:
            return service_client
        user = service_client.user(CFG.AS_USER)
        return service_client.as_user(user)


    def print_items(items):
        """print items"""
        print("\n")
        print("Type\tID\tName")
        print("----\t--\t----")
        for item in list(items):
            print(f"{item.type}\t{item.id}\t{item.name}\t")


    def main():
        """main function"""

        client = get_box_client(as_user=False)

        # print current user info
        user = client.user().get()
        print(f"Current User: {user.name}\tid:{user.id}\temail:{user.login}")

        # list files in parent folder
        items = client.folder(CFG.PARENT_FOLDER_ID).get_items()
        print_items(items)

        # folder = 198775845609

        client.folder("198775845609").upload("test_upload.txt", "test_upload.txt")


    if __name__ == "__main__":
        main()
        print("\n")
        print("-" * 80)
      print("All Done!")

    However, when trying to replicate your use case, I also tried to invite the UI Elements service account as a co-owner collaborator to another folder, and the invite is pending acceptance from that service account, which in my case has no valid email.

    Could this be the source of your issue?

    0
    Comment actions Permalink
  • Julien Lincy

    Hello Rui and thanks for this very clear answer.

    First to answer your question, yes I think you understood the issue

    "If I understood correctly there are 2 service accounts. A folder owned by service account A is shared with service account B and service account B uploads a file."  ==> if you consider my personal account is a "service account" which I'm not sure regarding Box terminology

    I finally found a solution on my side :

    - with Python API, create a new folder with Client authenticated with app config file (using create_subfolder)

    - share this with folder with my personal account so that I can have access to that folder (.add_collaborator("mail@mail.com", CollaborationRole.CO_OWNER)

    Since, I can now use this folder to CRUD file without any problem with authentication with config file.

    But I still can't use the folder I created with my personal account, which I then shared with app Service Account.

    The hypothesis you raised regarding the "Pending" sharing is interesting, it might be root cause of issue, as it seems to be some access right management issue. But you have to know I don't have this view when checking information regarding the sharing.

    If it helps, here are the folder I created

    The folder with which I have an issue (the one created with my account then shared)

    https://harmonicinc.app.box.com/folder/196886012101

    The folder which works fine (created with app service account then shared with my personal account)

    https://harmonicinc.app.box.com/folder/198717991676

    Strangely, the folder has been created with "Box Admin Service Account" while I was expecting it to be created with the app service account (Box_XOS_manufacturing). Since I'm using the config.json file from the app, I would have expected to always have a client which is using "Box_XOS_manufacturing" for any command.

    But it looks like that once client is created with same config.json file :

    - we send upload commands with "Box_XOS_manufacturing"  account

    - we send create_subfolder commands with  "Box Admin Service Account"

     

    0
    Comment actions Permalink
  • Julien Lincy

    BTW I reused your python library to redo the test with my own folder/accounts and problem is the same.

    0
    Comment actions Permalink
  • Rui Barbosa

    Hi Julien,

    That is odd indeed...

    So looking at your screenshots I can see both folders are shared with different service users, which is puzzling, and leads me to think there are 2 service users.

    Perhaps the person acting as the box administrator in your company did something manually or has some policies in place.

    Once an app is using JWT authentication the service user should be consistent, these apps, depending on configuration, can impersonate another user but not another service app.

    To be honest, what you're describing is somewhat unexpected, but there are so many variables in play, that I lost track. I'm glad you found a workaround.

    However here are some of the more generic rules and my configurations, perhaps these can help you identify your situation.

    You can check these under the developer console for your specific app (https://app.box.com/developers/).

    So in my example I have an app configure to authenticate using JWT (server side):

     

    Then on the app access level it is configured as app+enterprise access, giving it access to the entire enterprise:

     

     

    On application scopes I have everything selected, and on the advanced features I have:

    This will allow this service user to impersonate any other user in the enterprise.

    There is an interesting detail here. If you set a developer token that usually only lasts for 60 minutes, that token is associated with your user rather than the service user.

    Every time you change something here you need to ask you box administrator to reauthorize the app, you can check its status or submit via the authorization tab:

    Another test you can do is check which user represents the service account associated with the JWT, for my previous example:

        client = get_box_client(as_user=False)

        # print current user info
        user = client.user().get()
        print(f"Current User: {user.name}\tid:{user.id}\temail:{user.login}")

        # list files in parent folder
      items = client.folder(0).get_items()
        print_items(items)

    outputs, the service user details and the items of the root folder:

    Current User: 
    UI-Elements-Sample        
    id:20344589936  
    email:AutomationUser_1841316_RbcnIM9B2l@boxdevedition.com


    Type    ID      Name
    ----    --      ----
    folder  177388203339    100k
    folder  172599089223    Bookings
    folder  163422716106    Box UI Elements Demo
    folder  189803765719    ClassificationService
    folder  198775845609    JWT Folder for UI Sample Apps
    folder  172611202270    My Signed Documents
    folder  170845975022    Waivers
    folder  176837925976    Webhook

    You can also list all the users visible to your service user:

        users = client.users()
        for user in users:
            print(f"User: {user.name}\tid:{user.id}\temail:{user.login}")

    which outputs in my case:

    User: Administrator     id:18662105676  email:AppUser_1715931_Il2dcyHuqu@boxdevedition.com
    User: Administrator     id:18662356345  email:AppUser_1715931_vt8XOps1Ff@boxdevedition.com
    User: Administrator     id:18661971368  email:AppUser_1715931_xSifhdw6W7@boxdevedition.com
    User: Investment User   id:22240548078  email:barduinor+inv@gmail.com
    User: Wealth User       id:22240405099  email:barduinor+we@gmail.com
    User: Wholesale User    id:22240545678  email:barduinor+wh@gmail.com
    User: Rui Barbosa       id:18622116055  email:barduinor@gmail.com

    From here you can create a folder and share it with your personal user and verify how does it show up on your side.

        # create a new folder
        folder_new = client.folder(CFG.PARENT_FOLDER_ID).create_subfolder("Shared with RB")

        # share it with RB as co-owner
        folder_new.add_collaborator("barduinor@gmail.com", CollaborationRole.CO_OWNER)

    Now when I login as myself on the box app, I can see the folder owned by the service user which created it:

    Now, lets upload a file, again using the app service user to this folder which in my case is:

    Type    ID      Name
    ----    --      ----
    folder  177388203339    100k
    folder  198947288178    aaaa
    folder  172599089223    Bookings
    folder  163422716106    Box UI Elements Demo
    folder  189803765719    ClassificationService
    folder  198775845609    JWT Folder for UI Sample Apps
    folder  172611202270    My Signed Documents
    folder  198948099055    Shared with RB
    folder  170845975022    Waivers
    folder  176837925976    Webhook

    using this:

    client.folder("198948099055").upload("test_upload.txt", "test_upload.txt")

    And the file is also owned by the service user:

     

    Hope this helps you test your use case and find out why we can see 2 distinct service users.

    Best regards

    0
    Comment actions Permalink
  • Julien Lincy

    Hello Rui,

    Regarding the suggested app parameters, I do have same authentication as you have.

    I also tested :

    - app access level (it was App acc only for me)

    - advanced features (it was no ticked for both paremeters, I ticked the boxes)

    but no effect, still have the issue.

    Regarding the following test

    client.user().get()

    I have following error message

    Context Info: {'errors': [{'reason': 'invalid_parameter', 'name': 'user', 'message': "Invalid value 'u_217074553'. 'user' with value 'u_217074553' not found"}]}

    For the client.users() request it does not print anything.

    It looks like that either i don't have rights to get users for client, or there are no users...

    I only see this log trace 

    boxsdk.pagination.limit_offset_based_object_collection.LimitOffsetBasedObjectCollection 

    I tried several things, like managing exception and so on, but still hve nothing print

            try:
                user_iterator = self.client.users()
            except BoxAPIException as e:
                print(f"Error fetching user list: {e}")
                exit()

            # Loop through all users
            for user in user_iterator:
                if isinstance(user, User):
                    print(user.name, user.login)
                else:
                    print(f"Skipping non-user object: {user}") 

    The create_subfolder / add_collaborator proposed test is what I did as a workaround and I do confirm it works fine and this now what I'm using.

     

    0
    Comment actions Permalink

Please sign in to leave a comment.