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

JWT Oauth 2.0 using powershell

Answered
New post

Comments

9 comments

  • RobBayly

    Thanks for posting your code, however when I try to run it I'm not getting an valid object returned for 

    $ob2 = New-Object Box.V2.JWTAuth.BoxJWTAuth ($ob)

    When evaluated $ob2 returns Box.V2.JWTAuth.BoxJWTAuth, and as a result I'm not able to get an AdminToken. $ob evaluates as a complex object with the embedded cert and details from JSON.

     

    While a newby to Box, I'm fairly familiar with PowerShell, and this one has me stumped. The App has been approved by the Box Admin etc. 

     

    Appreciate any ideas.

    Thanks

    0
    Comment actions Permalink
  • BenMitchell1979

    Was this ever addressed? I'm also not able to authenticate. Is there any additional documentation around using this solution? 

    0
    Comment actions Permalink
  • ramon_schouten

    This doesn't work for me sadly.
    First i had to add the -Raw parameter to the Get-Content part.

    When filling in the paths and trying it out with getting the users, I get the following error message:

    Result                 : 
    Id                     : 121315
    Exception              : System.AggregateException: One or more errors occurred. ---> Box.V2.Exceptions.BoxException: The API returned an error [BadRequest]
                                at Box.V2.Extensions.BoxResponseExtensions.ParseResults[T](IBoxResponse`1 response, IBoxConverter converter)
                                at Box.V2.JWTAuth.BoxJWTAuth.JWTAuthPost(String assertion)
                                at Box.V2.JWTAuth.BoxJWTAuth.GetToken(String subType, String subId)
                                at Box.V2.JWTAuth.JWTAuthRepository.d__21.MoveNext()
                             --- End of stack trace from previous location where exception was thrown ---
                                at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
                                at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
                                at Box.V2.Managers.BoxResourceManager.d__13`1.MoveNext()
                             --- End of stack trace from previous location where exception was thrown ---
                                at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
                                at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
                                at Box.V2.Managers.BoxResourceManager.d__12`1.MoveNext()
                             --- End of stack trace from previous location where exception was thrown ---
                                at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
                                at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
                                at Box.V2.Managers.BoxResourceManager.d__11`1.MoveNext()
                             --- End of stack trace from previous location where exception was thrown ---
                                at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
                                at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
                                at Box.V2.Managers.BoxUsersManager.d__4.MoveNext()
                                --- End of inner exception stack trace ---
                             ---> (Inner Exception #0) Box.V2.Exceptions.BoxException: The API returned an error [BadRequest]
                                at Box.V2.Extensions.BoxResponseExtensions.ParseResults[T](IBoxResponse`1 response, IBoxConverter converter)
                                at Box.V2.JWTAuth.BoxJWTAuth.JWTAuthPost(String assertion)
                                at Box.V2.JWTAuth.BoxJWTAuth.GetToken(String subType, String subId)
                                at Box.V2.JWTAuth.JWTAuthRepository.d__21.MoveNext()
                             --- End of stack trace from previous location where exception was thrown ---
                                at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
                                at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
                                at Box.V2.Managers.BoxResourceManager.d__13`1.MoveNext()
                             --- End of stack trace from previous location where exception was thrown ---
                                at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
                                at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
                                at Box.V2.Managers.BoxResourceManager.d__12`1.MoveNext()
                             --- End of stack trace from previous location where exception was thrown ---
                                at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
                                at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
                                at Box.V2.Managers.BoxResourceManager.d__11`1.MoveNext()
                             --- End of stack trace from previous location where exception was thrown ---
                                at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
                                at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
                                at Box.V2.Managers.BoxUsersManager.d__4.MoveNext()<---
                             
    Status                 : Faulted
    IsCanceled             : False
    IsCompleted            : True
    CreationOptions        : None
    AsyncState             : 
    IsFaulted              : True
    AsyncWaitHandle        : System.Threading.ManualResetEvent
    CompletedSynchronously : False

    So after some fiddling around, I found out that the parameter $admintok doesn't actually contain the admin token;

     

    $admintok = $ob2.AdminToken

    is no actual API call, it should be 

    $admintok = $ob2.AdminToken()

    But whenever i switch that up, I get the following error message:

    Exception calling "AdminToken" with "0" argument(s): "The API returned an error [BadRequest]"
    At line:10 char:1
    + $admintok = $ob2.AdminToken()
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : BoxException

     

    Any ideas on what is going wrong here?

    0
    Comment actions Permalink
  • mwiller

     The .NET SDK is designed to work in full .NET applications, but unfortunately doesn't work out of the box in PowerShell.  Because the SDK's app.config file that binds any version of Newtonsoft.Json to the 10.x version that the SDK installs is not being loaded by PowerShell. Since you're going outside of the SDK's normal mode of operation and loading the assemblies manually, you'll need to ensure that the correct versions are installed and referenced. In the course of our investigation we found some information about how to make PowerShell load the app.config, which some other Powershell users have had luck with.

     

    One other option is to use the Box CLI in your PowerShell script; it handles JWT authentication currently.

    0
    Comment actions Permalink
  • ramon_schouten

    Hi MWiller,

     

    Thanks for your reply!

    I will look into the optional solution you provided.
    If I get this working, ofcourse I will share the code.

    Otherwise I will have to look to the Box CLI and post the working code of that one.

    Thanks!

    0
    Comment actions Permalink
  • ramon_schouten

    I have not been able to fix it with loading the config file in PowerShell sadly.

     

    The CLI seems to work like a charm, but I am missing some documentation, for instance when I am trying to create a user;

    $adminUsername = "***email address removed for privacy***"
    $name = "TestUser1"
    
    # Search Admin Account
    $admin = box users search $adminUsername --json
    $adminDetails = $admin | ConvertFrom-Json
    $adminId = $adminDetails.entries.id
    
    # Get Access Token for Admin Account
    $token = box tokens get -u $adminID
    
    # Create Json body with new user details
    $body = @{
        entries = @(
            @{
                name = $name
            }
        )
    } | ConvertTo-Json
    
    # Create new user
    box users create --token $token $body 

    I keep running into the error:

    box : A login is required for this command.
    At line:22 char:1
    + box users create --token $token $body
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (A login is required for this command.:String) [], RemoteException
        + FullyQualifiedErrorId : NativeCommandError

    What exactly causes this?
    And how can I perform a login? Or solve this?
    I thought it just meant no access token is provided (perhaps the service account cannot create users, only the admin user can) so I got the access token of my admin user.
    But I keep running into the same issue.

    0
    Comment actions Permalink
  • ramon_schouten

    Okay I'm dumb...

     

    Ignore this message...
    The login was referencing  to the required parameter for the user login (emailaddress).
    I tried it with a json body so I could create users in bulk, but this only works when using a file.

    0
    Comment actions Permalink
  • SunilM

    It works for me.. If you want to work in Admin Context there is a Powershell Module on github PoshBox which works currently, however, if you are trying to use it to upload files, it will upload to the service account and not the user account at this point (put in an issue and might consider working on it later)

     

    Here is how I can get it to work 

    # Location of the script and its contents
    $scriptLocation = "c:\scripts\box"
    
    # Locate the DLL's required to run
    $dlls = Get-ChildItem "$scriptLocation\lib"
    
    #loop through each DLL and import the DLL required to run
    foreach($dll in $dlls)
    {
        $library = get-childitem $dll.FullName -Recurse | Where-Object {$_ -like "*.dll"}
        if($library)
        {
            Add-type -Path $library.FullName
        }
    }
    
    
    # Set the location of the JSON file #
    $file = "$scriptLocation\test.json"
    $uplFolder = "C:\temp\testUpload"
    <# Import the JSON file #>
    $json = Get-Content $file
    
    #Create a Box instance
    $iBoxConfig = [Box.V2.Config.BoxConfig]::CreateFromJsonString($json)
    $JWTAuth = [Box.V2.JWTAuth.BoxJWTAuth]::new($iBoxConfig)
    
    #Set the Authentication
    $adminToken = $JWTAuth.AdminToken()
    $adminclient = $JWTAuth.AdminClient($adminToken)
    
    #Need a User Client to upload to user folder and not to the Automation user context with ID
    #This does not have to have admin permissions on the entire instance
    #Definitely needs "Generate User Access Tokens" Permission, which is linked to "Manage Users"
    #Read-Write Permissions as needed. Tested that it can't access other user accounts, but is able to list other users
    $userid = "0123456789"
    $ut = $adminclient.Auth.BoxJWTAuth.UserToken($userid)
    $uc = $adminclient.Auth.BoxJWTAuth.UserClient($ut,$userid) 
    $pagesize = 100
    $offset = 0
    $folderId = 0
    #List Current Contents
    $uc.FoldersManager.GetFolderItemsAsync($folderID,$pagesize,$offset).GetAwaiter().GetResult().entries.name
    #Upload some test Files
    $filesToUpload = (Get-childItem $uplFolder)
    
    #Get Files from $uplFolder and upload to Root Folder for the user
    foreach($file in $filesToUpload){
        try{
            #Uploading to root folder, which is ID 0, for the account using the User Client 
            $request = @{
                name = "$($file.Name)"
                parent = @{
                    id = "0"
                }
            }
            $content = Get-Content $file.FullName -Encoding UTF8
            $stream = ([IO.MemoryStream]::new([Text.Encoding]::UTF8.GetBytes($content)))
            #Will Fail if File Already exists since we are not adding versions. 
            #Adding GetResult makes it synchronous essentially. you could just getAwaiter and wait till IsCompleted is True
            $res = $uc.FilesManager.UploadAsync($request, $stream ).GetAwaiter().GetResult()        
        }catch{Write-Output "Failed to upload $($file.FullName)"}
    }
    #Get the number of Items in the Folder
    $totalItems = $uc.FoldersManager.GetFolderItemsAsync($folderID,$pagesize,$offset).GetAwaiter().GetResult().TotalCount
    #show uploaded files
    #How many iterations of paging it will take to get all items assuming nobody adds items to the Box folder while we are in the loop
    $iters = [math]::Ceiling($totalItems / $pagesize)
    for ($i=0; $i -lt $iters; $i++){
        $offset = $i * $pagesize
        $uc.FoldersManager.GetFolderItemsAsync($folderID,$pagesize,$offset).GetAwaiter().GetResult().entries.name
    }
    

     

    0
    Comment actions Permalink
  • elii

    Hi !

     

    Welcome to the community and thank you for an awesome first post!

     

    We really appreciate you sharing this solution that you found with the rest of the Box Community! Coming back to let other community members know how you were able to tackle a problem keeps the community thriving.

     

    Thanks again for your contribution and for your time in the community!

    0
    Comment actions Permalink

Please sign in to leave a comment.