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
- Remove the url validation from around the
GetAccessTokenAsync
call and eat the resulting DropboxExceptions - 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.