1

I have a pair of Windows Phone apps that make use of Dropnet for Dropbox integration. One developed in Visual Studio Express 2012 targeting 7.1 and one in Visual Studio 2013 targeting 8.0.

Both are using Dropnet 1.9.6 and RestSharp 104.4.0 via NuGet.

The 7.1 project uses the following static class to interact with DropNet authentication (and file operations but I've excluded them for clarity).

public static class DropboxStorage
{
    const string UserToken = "UserToken";
    const string UserSecret = "UserSecret";

    private static DropNetClient _Dropbox;
    public static DropNetClient Dropbox
    {
        get
        {
            if (_Dropbox == null)
            {
                _Dropbox = new DropNetClient(AppKey, AppSecret);
                if (IsAuthenticated)
                {
                    _Dropbox.UserLogin = new UserLogin
                    {
                        Token = (string)IsolatedStorageSettings.ApplicationSettings[UserToken],
                        Secret = (string)IsolatedStorageSettings.ApplicationSettings[UserSecret]
                    };
                }
                _Dropbox.UseSandbox = true;
            }
            return _Dropbox;
        }
    }

    public static bool IsAuthenticated
    {
        get
        {
            return IsolatedStorageSettings.ApplicationSettings.Contains(UserToken) &&
                IsolatedStorageSettings.ApplicationSettings.Contains(UserSecret);
        }
    }

    public static void StartAuthentication(Action<Uri> success, Action<Exception> failure)
    {
        Dropbox.GetTokenAsync(userLogin =>
        {
            var url = Dropbox.BuildAuthorizeUrl(userLogin);
            if (success != null) success(new Uri(url, UriKind.RelativeOrAbsolute));
        }, error =>
        {
            if (failure != null) failure(error);
        });
    }

    public static void CheckAuthentication(Uri uri, Action success, Action<Exception> failure)
    {
        if (uri.LocalPath == "/1/oauth/authorize_submit")
        {
            Dropbox.GetAccessTokenAsync((accessToken) =>
            {
                IsolatedStorageSettings.ApplicationSettings[UserToken] = accessToken.Token;
                IsolatedStorageSettings.ApplicationSettings[UserSecret] = accessToken.Secret;
                IsolatedStorageSettings.ApplicationSettings.Save();
                if (success != null) success();
            },
            (error) =>
            {
                if (failure != null) failure(error);
            });
        }
    }

This is then accessed via a page with a WebBrowser control.

XAML

<phone:WebBrowser x:Name="Browser"
                              IsScriptEnabled="True"
                              Navigated="Browser_Navigated"/>

C#

public partial class DropboxPage : PhoneApplicationPage
{
    public DropboxPage()
    {
        InitializeComponent();

         DropboxStorage.StartAuthentication(
                uri => Browser.Navigate(uri),
                ex => ShowException(ex));
     }

    public void Browser_Navigated(object sender, NavigationEventArgs e)
    {
        Debug.WriteLine(e.Uri.OriginalString);
        DropboxStorage.CheckAuthentication(
            e.Uri,
            () => NavigationService.GoBack(),
            ex => ShowException(ex));
    }

    private void ShowException(Exception e)
    {
        MessageBox.Show(e.Message);
        NavigationService.GoBack();
    }
}

The Dropbox authentication web pages are displayed

  • Sign in
  • Allow app access
  • Success, app connected

and the code determines that authentication has been successful when the Dropbox url contains /1/oauth/authorize_submit.

This all works perfectly, however the 8.0 project behaves a little differently.

The first difference is the url returned by the BuildAuthorizeUrl call.

  • 7.1 https://www.dropbox.com/1/oauth/authorize?oauth_token=<token>
  • 8.0 https://api.dropbox.com/1/oauth/authorize?oauth_token=<token>

The second difference is that the Dropbox url doesn't change from /authorize?oauth_token= to /authorize_submit once the app has been connected so GetAccessTokenAsync is never called and the user token and user secret aren't stored.

My investigations to date suggest a couple of ways to fix this that aren't ideal

  1. Remove the url validation from around the GetAccessTokenAsync call and eat the resulting DropboxExceptions
  2. Add a callback url to the initial BuildAuthorizeUrl call.

I'm currently doing 1. just to get things up and running but obviously this isn't a long term solution. I'd prefer to avoid using a callback url as it seems like an over complication for a mobile app.

What I'm after is a way of getting the 8.0 project to behave in the same way as the 7.1 project. I've had a look at the Dropnet source code and that contains https://api.dropbox.com as it's base url so I'm even less clear as to how the 7.1 code is working in the first place.

Michael Phillips
  • 862
  • 2
  • 7
  • 17

2 Answers2

1

The difference in behaviour is caused by the 8.0 project using System.Windows.Interactivity to convert the Navigated event into a call to a Command on the ViewModel. This seems to prevent the website and WebBrowser control interacting correctly and redirecting to https://www.dropbox.com.


Current

XAML

<phone:WebBrowser x:Name="Browser"
                  IsScriptEnabled="True"
                  Visibility="{Binding IsAuthenticated, Converter={StaticResource BoolToInvisibilityConverter}}"
                  Source="{Binding AuthenticateUri}">               
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Navigated" >
            <i:InvokeCommandAction Command="{Binding BrowserNavigated}" 
                                    CommandParameter="{Binding Source, ElementName=Browser}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</phone:WebBrowser>

C#

BrowserNavigated = new RelayCommand<Uri>(u =>
                {                   
                    RemoteStorage.CheckAuthentication(u, () => IsAuthenticated = true, ex => ShowException(ex));
                });

Corrected

XAML

        <phone:WebBrowser x:Name="Browser"
                          IsScriptEnabled="True"
                          Visibility="{Binding IsAuthenticated, Converter={StaticResource BoolToInvisibilityConverter}}"
                          Source="{Binding AuthenticateUri}"
                          Navigated="Browser_Navigated"/>

C#

    private void Browser_Navigated(object sender, NavigationEventArgs e)
    {
        ViewModel.BrowserNavigated.Execute(e.Uri);
    }

In both cases the AuthenticateUri property of the view model bound to the WebBrowser Source is set by the StartAuthentication method in the DropboxStorage class.

Having added the event handler the 8.0 project works as expected.

Michael Phillips
  • 862
  • 2
  • 7
  • 17
0

I also started seeing this problem not too long ago. My app worked fine on WP8 up until that point. I don't remember all the details but I looked at my checkin to fix it and I think an extra page load for the first url started happening. No warning from Dropbox about the change either.

To solve it I wait for a certain url to show up in the LoadCompleted event. You use the Navigated event which I think should also work.

I would modify your event handler like this:

public void Browser_Navigated(object sender, NavigationEventArgs e)
{
    if (e.Uri.AbsolutePath == "/1/oauth/authorize_submit")
    {
        DropboxStorage.CheckAuthentication(
            e.Uri,
            () => NavigationService.GoBack(),
            ex => ShowException(ex));
    }
}
Austin Thompson
  • 2,251
  • 1
  • 17
  • 23
  • Thanks, however if you look at the code for CheckAuthentication I'm already looking for /1/oauth/authorize_submit in the uri LocalPath. You did however point me in the right direction as my 8.0 project doesn't have a Browser_Navigated event handler and instead uses System.Windows.Interactivity to convert the Navigated event into a Command call. Apparently doing that prevents the dropbox website redirects working properly. I've switched back to using an event handler and it now works fine. – Michael Phillips Aug 04 '14 at 19:09
  • I also vaguely recall having a similar problem in the 7.1 project (quite some time ago, so details are hazy). I fixed that by updating RestSharp to 104.4 from the 104.1 that Dropnet installs. – Michael Phillips Aug 04 '14 at 19:17