box / box-windows-sdk-v2

Windows SDK for v2 of the Box API. The SDK is built upon .NET Framework 4.5

Home Page:https://developer.box.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Authentication Issue: Windows 2019 Server , ASP.NET leveraging Box.V2 SDK Github. Calling Box.V2.Client.Auth.AuthenticateAsync(Code) not returning

John-Belisle opened this issue · comments

  • [*] I have checked that the SDK documentation doesn't solve my issue.
  • [*] I have checked that the API documentation doesn't solve my issue.
  • [*] I have searched the Box Developer Forums and my issue isn't already reported (or if it has been reported, I have attached a link to it, for reference).
  • I have searched Issues in this repo and my issue isn't already reported.

Description of the Issue

Making a call AuthenticateAsync with a valid returned code and it never returns from the ExchangeAuthCode process. As you can see from the log file entries below I do get the new Token (Which I am very happy about this!) but it never returns to the original caller --> AuthenticateAsync. Stack/Call back is blown...don't know where it returns to?

This is what the high level call stack looks like and they are all async tasks with an await… Program pointer is lost at tier 4 and 5
We had issues with firewalls and polices within our network and so I have to build out a new server and never had time to get the debugger working again but it is out of the equations of possible cause.

5 - await Box.V2_service.ToResponseAsync(boxRequest).ConfigureAwait(false);
4 - await Box.V2ExchangeAuthCode(string authCode)
3 - await Box.V2.ent.ActiveBoxClient.Auth.AuthenticateAsync(Code)
2 - My. BoxAPICall.RefreshBoxClientAuthCode(Code)
1 - My.Default.Page_Load

Other than nested async tasks there is nothing I can see that is special in the code you are offering us to use. The ASP.NET site and the box.v2 complies with compiling under the 4.5 .NET.

Excerpt from log.txt
Log Entry : 2:13:09 PM Wednesday, November 17, 2021
:
:Redirect to Box Auth -> https://account.box.com/api/oauth2/authorize?response_type=code&client_id=qz3wil2f9tva82g7zuzwfy939xyh0zpb

Log Entry : 2:13:21 PM Wednesday, November 17, 2021
:
:Box CallBack Redirect Recieved New AuthCode -> ODPDSeM9kAHSBfj8Yna5vGvQvLZoVp5A

Log Entry : 2:13:21 PM Wednesday, November 17, 2021
:
:Start Box Call to -> ActiveBoxClient.Auth.AuthenticateAsync(ODPDSeM9kAHSBfj8Yna5vGvQvLZoVp5A)

Log Entry : 2:13:21 PM Wednesday, November 17, 2021
:
:Entering Box Call to -> AuthenticateAsync()

Log Entry : 2:13:21 PM Wednesday, November 17, 2021
:
:Start Box Call to -> ExchangeAuthCode _serviceToResponseAsync

Log Entry : 2:13:22 PM Wednesday, November 17, 2021
:
:End Box Call to -> boxResponse.ParseResults(_converter) - AccessToken:2QEpjNEL1MbAcHfcThhlfQfGZ6SwcYS1

Never returns to Auth.AuthenticateAsync...Program pointer is lost.

Steps to Reproduce

Related Code snippets to recreation:
Launch the site and the Default.aspx pages Creates a BoxClient via a central BoxAPICall class then redirects to Box to start the authentication process.

namespace PrismDoc2
{
public class BoxAPICall
{
public string ClientBoxAuthUrl { get; set; }

    public BoxClient ActiveBoxClient { get; set; }

    public void CreateActiveClient()
    {
        string ThisBoxClientID = Properties.Settings.Default.BoxClientID;
        string ThisBoxClientSecret = Properties.Settings.Default.BoxAPIKey;
        string RedirectUrl = Properties.Settings.Default.BoxRedirectUrl;
        Box.V2.Config.BoxConfig config = new Box.V2.Config.BoxConfig(ThisBoxClientID, ThisBoxClientSecret, new Uri(RedirectUrl));
        this.ClientBoxAuthUrl = Box.V2.Config.Constants.AuthCodeEndpointString + "?response_type=code&client_id=" + ThisBoxClientID;

        this.ActiveBoxClient = new BoxClient(config);
    }

Page Load:
//Inital Page Load should create an Active BoxClient object if there is none.
if (((BoxAPICall)Session["BAC"]).ActiveBoxClient == null ||
((BoxAPICall)Session["BAC"]).ActiveBoxClient.Auth.Session == null)
{
//Initiate a BoxClient object which will be used to Authenticate
((BoxAPICall)Session["BAC"]).CreateActiveClient();
// 1st time page load
if (HttpContext.Current.Request.RawUrl.LastIndexOf("?") == -1)
{
((LogFile)Session["MYLOG"]).Log("Redirect to Box Auth -> " + ((BoxAPICall)Session["BAC"]).ClientBoxAuthUrl);
Response.Redirect(((BoxAPICall)Session["BAC"]).ClientBoxAuthUrl);
}
}

Once redirected there are several login sites that ultimately brings you to “Grant Access to Box” site where you click a button to allow access. Once clicked, it redirects back to my site with the new code=**** {see log} where it calls the same central BoxAPICall class RefreshBoxClientAuthCode

//This is the Box's call back containing the new Code
if (HttpContext.Current.Request.RawUrl.LastIndexOf("?") > 0)
{
int Idx = HttpContext.Current.Request.RawUrl.LastIndexOf("?") + 1;
string Params = HttpContext.Current.Request.RawUrl.Substring(Idx);
NameValueCollection Query = HttpUtility.ParseQueryString(Params);
// Extract the new code and create a new authenticated BoxClient.
if (Query.Get("code").Length > 0)
{
((LogFile)Session["MYLOG"]).Log("Box CallBack Redirect Received New AuthCode -> " + Query.Get("code").ToString());
string LFCR = Environment.NewLine;
bool BoxAuth = ((BoxAPICall)Session["BAC"]).RefreshBoxClientAuthCode(Query.Get("code").ToString()).Result;
if (BoxAuth)
{
txtResult.Text = "ActiveBoxClient Details:AccessToken=" +
((BoxAPICall)Session["BAC"]).ActiveBoxClient.Auth.Session.AccessToken + LFCR +
"RefreshToken=" + ((BoxAPICall)Session["BAC"]).ActiveBoxClient.Auth.Session.RefreshToken + LFCR +
"ExpiresIn=" + ((BoxAPICall)Session["BAC"]).ActiveBoxClient.Auth.Session.ExpiresIn + LFCR;
((LogFile)Session["MYLOG"]).Log("End to Auth Process:" + txtResult.Text);
}
else { txtResult.Text = "Failed to authenticate Code:" + Query.Get("code").ToString(); }

      }
  }

Local to Project BoxAPICall class:

public async Task RefreshBoxClientAuthCode(string Code)
{

        try
        {
            LogFile MyLogFile = new LogFile();
            MyLogFile.FilePath = @"C:\PrismDocShare\";
            MyLogFile.Log("Start Box Call to -> ActiveBoxClient.Auth.AuthenticateAsync(" + Code + ")");
            Box.V2.Auth.OAuthSession session = await this.ActiveBoxClient.Auth.AuthenticateAsync(Code);
            MyLogFile.Log("End Box Call to -> ActiveBoxClient.Auth.AuthenticateAsync(" + Code + ")");
            Box.V2.BoxClient client = new BoxClient(this.ActiveBoxClient.Config, session);

            this.ActiveBoxClient = client;
            return true;
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message);
        }
    }

That’s it…It all basically works as you can see in the log, I get my new AccessToken (Which I am Extremely happy about!) with the exception of the ExchangeAuthCode never returning.

We are trying to provide a solution for a Very Important Client in a timely manner and do not have the luxury to retool and start from scratch picking another platform. I will say, what is attractive of the Box.V2 offering is it maintains the AccessTocken/RefreshTokens when expired and that was a big dev time saving feature on going down this path. All things aside, the code is very well written and I am hoping to get it working soon for our Client.
Thank you for your help and support,
John

Expected Behavior

Since the Task is set to await when calling ExchangeAuthCode I expect it to return?

Error Message, Including Stack Trace

5 - await Box.V2_service.ToResponseAsync(boxRequest).ConfigureAwait(false);
4 - await Box.V2ExchangeAuthCode(string authCode)
3 - await Box.V2.ent.ActiveBoxClient.Auth.AuthenticateAsync(Code)
2 - My. BoxAPICall.RefreshBoxClientAuthCode(Code)
1 - My.Default.Page_Load

Screenshots

Nothing to show...the site is blank and spinning.

Versions Used

Box.V2 4.0.0 <title>Box Windows SDK V2</title> Box Inc. Box Inc. https://github.com/box/box-windows-sdk-v2 Apache-2.0 false Windows SDK for v2 of the Box API. The SDK is targeting .NET Framework 4.5 See https://github.com/box/box-windows-sdk-v2/blob/main/CHANGELOG.md#400-2021-11-02 Copyright 2021 Box V2 SDK Platform Enterprise Collaboration Storage File Management

Windows: Windows 2019 Server Standard Edition.

Hi @John-Belisle,

Thanks for submitting this Issue! We will take a look and get back to you ASAP!

@arjankowski

Hi @John-Belisle

I've tried to reproduce your case by implement similar method to your RefreshBoxClientAuthCode on my computer and
actually everythings is working ok.

But I have some questions that maybe will help us to resolve your problem.

Can you ensure that there is no exception in catch block in RefreshBoxClientAuthCode method?

I see that your BoxAPICall class has a method
public async Task RefreshBoxClientAuthCode(string Code) but shouldn't it be like this
public async Task<bool> RefreshBoxClientAuthCode(string Code) since its return a bool value?

Can you try to execute this sample of code (could be even in simple console app) and see if it works for you?
This example skips fetching authorization code and just try to reach the server.

   private static async Task Check()
    {
        try
        {
            string thisBoxClientID = Properties.Settings.Default.BoxClientID;
            string thisBoxClientSecret = Properties.Settings.Default.BoxAPIKey;
            string redirectUrl = Properties.Settings.Default.BoxRedirectUrl;
            Box.V2.Config.BoxConfig config = new Box.V2.Config.BoxConfig(thisBoxClientID, thisBoxClientSecret, new Uri(redirectUrl));
            string clientBoxAuthUrl = Box.V2.Config.Constants.AuthCodeEndpointString + "?response_type=code&client_id=" + thisBoxClientID;

            var activeBoxClient = new BoxClient(config);
            var code = "anything can be here just for test";
            Box.V2.Auth.OAuthSession session = await activeBoxClient.Auth.AuthenticateAsync(code);
            Box.V2.BoxClient client_new = new BoxClient(activeBoxClient.Config, session);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            return 
        }
    }

This should result with an error exception with message
"The API returned an error [BadRequest] invalid_grant - Auth code doesn't exist or is invalid for the client".

Can you confirm that you was able to achieve this result?

Hi Artur,
I appreciate the quick response and will attempt to answer your questions…

  1. Can you ensure that there is no exception in catch block in RefreshBoxClientAuthCode method?
    a. I will remove the try catch in this method and retested.
    Test results after removing Try Catch from RefreshBoxClientAuthCode:
    Log Entry : 5:42:09 AM Monday, November 22, 2021
    :
    :Entering Box Call to -> AuthenticateAsync()

Log Entry : 5:42:09 AM Monday, November 22, 2021
:
:Start Box Call to -> ExchangeAuthCode _serviceToResponseAsync

Log Entry : 5:42:10 AM Monday, November 22, 2021
:
:End Box Call to -> boxResponse.ParseResults(_converter) - AccessToken:3dx7oojF69o2cNiAZvpKrFwPKzj80rOe

Still results in lost stack pointer, log should be Next Log Entry in AuthenticateAsync:  "ExchangeAuthCode Returned -> AuthenticateAsync()"

  1. I see that your BoxAPICall class has a method
    public async Task RefreshBoxClientAuthCode(string Code) but shouldn't it be like this
    public async Task RefreshBoxClientAuthCode(string Code) since its return a bool value?
    a. Yes, you are correct, I just look at this and it does correctly have a return this could have been from me cut and pasting and editing/formatting my issue email.

  2. Can you try to execute this sample of code (could be even in simple console app) and see if it works for you?
    This example skips fetching authorization code and just try to reach the server.
    a. I can try this for you but you should know that I have already posted a file to box from the same application. I used a separate PowerShell application to obtain an AccessToken and I manually cut/pasted it into my Application settings. I am now trying to code the actual login process to our Makena client so that I can automate refreshing the token and this is where I ran into the lost stack pointer issue
    Test Result logs from call to Box Support Check() method triggered from a button click event:
    Log Entry : 7:13:18 AM Monday, November 22, 2021
    :
    :Entering Box Support Task from Page Button Click -> Check()


Log Entry : 7:13:18 AM Monday, November 22, 2021
:
:Entering Box Call to -> AuthenticateAsync()

Log Entry : 7:13:18 AM Monday, November 22, 2021
:
:Start Box Call to -> ExchangeAuthCode _serviceToResponseAsync

Note: The ExchangeAuthCode methods dies sooner as you don’t see expected next log entry boxResponse.ParseResults(_converter)
{see Log Excerpt in Item 2 for compare}

b. Also, you should be informed that I have tried the same ASP.NET web application on two separate servers within our company’s network / user enforced policies as well as on my own personal tablet from my home and the logs still show I am able to get the AccessToken: {see log excerpts} but ExchangeAuthCode method never returns to the caller AuthenticateAsync().

Please let me know what more I can do to help get to the bottom of this…

Thanks again for your help Artur!
John

The ConfigureAwait(false) fix you MW added to the ExchangeAuthCode call fixed this issue once I also added ConfigureAwait(false); to my own call to AuthenticateAsync() as well. So it should be noted consumers should also mimic these changes on their own calls to Box.V2 task methods.
Thank you for the quick turn around on this issue!
John