I have done a class which already works with the Dropbox API uploading files, downloading, deleting and so on. It has been working quite well since I was just using my own access token, but I need to register other users and a single but "big" problem appeared: retrieving the access token.
1.- Redirect URI? I'm starting to doubt why do I need this. I finally used this URI (https://www.dropbox.com/1/oauth2/redirect_receiver) because "The redirect URI you use doesn't really matter" Of course I included this one on my app config on Dropbox.
2.- I reach the user's account (I can see the user's count increased and I see the app has access to the user's account.
3.- I have a breakpoint on my code to inspect the variables in order to apply the DropboxOAuth2Helper.ParseTokenFragment but I have no success on there.
This is my code, but on the if before the try catch is where it gets stuck:
string AccessToken;
const string AppKey = "theOneAtmyAppConfigOnDropbox";
const string redirectUrl = "https://www.dropbox.com/1/oauth2/redirect_receiver";
string oauthUrl =
$#"https://www.dropbox.com/1/oauth2/authorize?response_type=token&redirect_uri={redirectUrl}&client_id={AppKey}";
private string oauth2State;
private bool Result;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Start(AppKey, webBrowser1);
webBrowser1.Navigating += Browser_Navigating;
}
private void Start(string appKey, WebBrowser w)
{
this.oauth2State = Guid.NewGuid().ToString("N");
Uri authorizeUri = DropboxOAuth2Helper.GetAuthorizeUri(OauthResponseType.Token, appKey, redirectUrl, state: oauth2State);
w.Navigate(authorizeUri);
}
private void Browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
if (!e.Url.ToString().StartsWith(redirectUrl, StringComparison.InvariantCultureIgnoreCase))
{
// we need to ignore all navigation that isn't to the redirect uri.
return;
}
try
{
OAuth2Response result = DropboxOAuth2Helper.ParseTokenFragment(e.Url);
if (result.State != this.oauth2State)
{
// The state in the response doesn't match the state in the request.
return;
}
this.AccessToken = result.AccessToken;
this.Result = true;
}
catch (ArgumentException)
{
// There was an error in the URI passed to ParseTokenFragment
}
finally
{
e.Cancel = true;
this.Close();
}
}
I've been fighting against this for hours and I'm starting to see the things a little cloudy at this point.
This is the tutorial I used, but I'm not moving forward. I would really appreciate any help!
EDIT: I finally made some steps forward. I changed the line which contains
Uri authorizeUri2 = DropboxOAuth2Helper.GetAuthorizeUri(appKey);
Now I'm showing the generated access token on the WebClient! Bad part comes when trying to get it (it gets inside the if) and it gets generated every time I ask the user for permission, so it gets overwrited.
EDIT 2: I noted the token I get generated on the browser is somehow malformed. I try to manually change it hardcored when I'm debugging and I get an exception when an AuthException when creating the DropboxClient object :( What the hell!
As Greg stated, the solution was using the event Browser_Navigated. Looks like the version of the embedded IE my Visual Studio (2015) uses didn't notice that if it's a redirect, it won't launch the event BrowserNavigating.
Related
I have a site where I'm trying to deliver files via WriteFile and they work fine in Chrome and Firefox, but in IE I have to hit "Retry" once or twice to actually make the file download.
Here is the code:
public class DownloadHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
var r = context.Response;
r.Clear();
r.ClearContent();
r.ContentType = "application/octet-stream";
string path = "";
try
{
if (HttpContext.Current.Request.QueryString["n"] != null)
{
var file = HttpContext.Current.Request.QueryString["n"].ToString();
var type = HttpContext.Current.Request.QueryString["t"].ToString();
r.AddHeader("Content-Disposition", "attachment; filename=" + file.Substring(file.IndexOf('_')+1));
string folder = "";
switch (type.ToLower())
{
case "public":
folder = ConfigurationManager.AppSettings["BCD_PublicDocsLoc"];
break;
case "private":
folder = ConfigurationManager.AppSettings["BCD_PrivateDocsLoc"];
break;
case "internal":
folder = ConfigurationManager.AppSettings["BCD_InternalDocsLoc"];
break;
}
path = folder + "/" + file;
r.WriteFile(path);
r.Flush();
r.Close();
r.End();
}
}
catch (Exception ex)
{
r.Flush();
r.Close();
r.End();
context.Response.Redirect("Error.aspx?err=301");
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
If anyone has any advice as to why this is happening, it would be greatly appreciated. Thanks!
Try substituting the HttpResponse's Close() and End() calls with HttpApplication.CompleteRequest().
Read here why, there are examples too.
Also, this solution was suggested here(in the first answer) for a situation similar to yours.
As was hinted that a small explanation in this post would be convenient, due to the possibility of the links going dead in the future, here it goes:
In short: IE seems to have problems with the HttpResponse.Close and HttpResponse.End methods. Aside of that, anyways, Microsoft recommends in most cases the use of HttpApplication.CompleteRequest over the former two, because:
-HttpResponse.Close() terminates the connection abruptly, dropping buffered data and is not intended for normal HTTP use in which a response to the client is desired
-HttpResponse.End() exists for compatibility reasons with the older ASP technology. It calls the EndRequest event directly and no further code after the End call is executed which is inconvenient in many cases
-HttpApplication.CompleteRequest(): also executes the EndRequest event and it does allow the execution of the code that following the CompleteRequest call, which makes it more appropriate to handle most situations.
Just a hunch but it sounds like an I.E. caching issue to me...
if I.E is set to automatically check for newer pages 'every time i go to the website.' (in [tools\internet options\general\ browsing history\settings]) then you wont have a cache issue.
Like I say, only a hunch, but give it a whirl.
If you want to get around this [*1], add a guid to your Query string.[*2]
[*1] The cache setting is a user by user setting, you can never pre-empt the users settings, so work with them instead
[*2] The nocache value is always different, the browser will never have a cached version to go to.
I use something like this...
protected void Page_PreRender(object sender, EventArgs e)
{
if (HttpContext.Current.Request.QueryString["FirstRun"] == "1")
{
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.Url.Query);
nvc.Remove("FirstRun");
string url = Request.Url.AbsolutePath;
for (int i = 0; i < nvc.Count; i++)
url += string.Format("{0}{1}={2}", (i == 0 ? "?" : "&"), nvc.Keys[i], nvc[i]);
Response.Redirect(string.Format("{1}&NoCache={0}",System.Guid.NewGuid().ToString().Replace("-",""),url));
}
}
Any links/redirects to this page need ?FirstRun=1 (or &FirstRun=1) appended to the querystring. The page reload cycles itself once adding a &noCache value to the querystring.
Note:
Because you added FirstRun=1, it will always execute twice serverside, but appear like a single load to your user, and the browser.
If you don't add FirstRun=1, it will behave like a normal request as it never gets into the condition.
As always: Im quite the noob, as you will prob see from the question.
I am playing around with Azure Wams in Xamarin.Android, and it seems to be a great tool. It logs in a user in Xamarin.Android greatly. My problem comes when i want the user to be able to log out and then log in with another account (im using Google for authentication). I used to be able to use it with a log out button, like this:
When logging in the AuthenticationToken is saved in a String for later use. So when the user confirms he wants to log out, i just UserAuth = String.Empty and then i call ConnectToMobileService() again:
public async Task ConnectToMobileService ()
{
try
{
CurrentPlatform.Init ();
client = new MobileServiceClient(
Constants.ApplicationURL,
Constants.ApplicationKey, progressHandler);
if (string.IsNullOrEmpty(UserAuth)) {
await Authenticate();
UserId = user.UserId;
await CreateTables();
await CheckUserId ();
}
else if (!string.IsNullOrEmpty(UserAuth)) {
client.CurrentUser = new MobileServiceUser(UserId);
client.CurrentUser.MobileServiceAuthenticationToken = (UserAuth);
await CreateTables();
}
}
catch (Exception e)
{
CreateAndShowDialog(e, "Error");
}
}
This used to re-launch the authentication-window for me, and the user logged in with hes new account - while the info is saved in preferences for use in other activities and so on. Well, after updating Xamarin and upgrading my license to Indie, this is no longer the case. Now it launces Authenticate for a short second, and then it goes straight back and acts as if the user logged in the exaxt way he did before.
I realize this i probably because there is some sharedpreference saved somewhere for the Wams. Ive studied the methods for clearing these i Java, but i have not been able to recreate them in C#.
client.Logout () does not seem to clear them alone. This is how i tried to recreatet the rest of it:
private void ClearPreferences(){
var prefs = this.GetSharedPreferences("UserDate", 0);
var editor = prefs.Edit ();
editor.Clear ();
editor.Commit ();
}
This does nothing. So, can anyone help me along? How do i reset it, so the users are able to log in with another account - or for example let a friend log in on their phone? Thanks in advance!
OK, so it turns out the info is stored as cookies by the auth provider. You have to both log out and clear the cookies. And it then works like a charm. This is how to clear cookies:
client.Logout ();
ClearCookies ();
await ConnectToMobileService ();
}
public static void ClearCookies () {
Android.Webkit.CookieSyncManager.CreateInstance (Android.App.Application.Context);
Android.Webkit.CookieManager.Instance.RemoveAllCookie ();
}
The below code an attempt to try and get get an Mp3 file from the MusicLibrary
It gives me,
A first chance exception of type
'System.UnauthorizedAccessException'
occurred in AccessingPictures.exe
This is my code:
public async void getFile()
{
StorageFolder folder = KnownFolders.MusicLibrary;
try
{
sampleFile = await folder.GetFileAsync("Test1.mp3");
}
catch (FileNotFoundException e)
{
// If file doesn't exist, indicate users to use scenario 1
Debug.WriteLine(e);
}
}
private void btnRead_Click(object sender, RoutedEventArgs e)
{
getFile();
}
Wouldn't we able to access the media files?
I am able to do this using the file picker.
But it does not work while i try to access it directly.
Am i missing anything here ?
To retrieve Pictures from Camera Roll
Void GetCameraPhotos()
{
using (var library = new MediaLibrary())
{
PictureAlbumCollection allAlbums = library.RootPictureAlbum.Albums;
PictureAlbum cameraRoll = allAlbums.Where(album => album.Name == "Camera Roll").FirstOrDefault();
var CameraRollPictures = cameraRoll.Pictures
}
}
You cannot access the files unless it is in response to a user request. i.e. the user must tap a button or something and that tap logic ends up calling your code that accesses the file. If you want to get at the file afterwards, you'll need to copy it in the app's data folder.
I finally figured the issue. It was because i hadn't enabled the capabilities in the Manifest file.
It works like a charm now.
Thanks everyone.
I am building a Windows c# app that needs to upload files to DropBox. Basically I have everything I need for my app(app secret and app key), but I need to have the client tokens saved to my sql DB for future use. According to Dropbox I am unable to save user login info which is good, but finding a good lib is getting tough.I have tried many different DropBox based libraries but run across the following issues:
SharpBox: seems easy enough to use, but need some kind of deserializer to save the client key and client secret anywhere.
OAuth2 Authorizer: Not enough documentation that I can find, in order for me to actually implement this.
DropNet: This is one that looked promising. It's async and looked good, but again I can't find an example of how to perform the auth function and save the variables to a file/DB/Reg/ or anything.
DropBox.API: This is the method that I currently use and it's working. Problem is it's not Async and requires .NET 4.5. I was ok with all the downs but lately found that's it's very touchy about different versions of JSON and other libraries.
I was hoping someone could give me some assistance in getting any of the above OAUTH libs actually working, Just to get the 3 legged auth process working.
UPDATE::
ok so i am going to include some of the code that I am using at the moment, that uses dropbox.api:
// Get Oauth Token
private static OAuthToken GetAccessToken()
{
string consumerKey = "mykey";
string consumerSecret = "myseceret";
var oauth = new OAuth();
var requestToken = oauth.GetRequestToken(new Uri(DropboxRestApi.BaseUri), consumerKey, consumerSecret);
var authorizeUri = oauth.GetAuthorizeUri(new Uri(DropboxRestApi.AuthorizeBaseUri), requestToken);
Process.Start(authorizeUri.AbsoluteUri);
MessageBox.Show("Once Registration is completed Click OK", "Confirmation");
return oauth.GetAccessToken(new Uri(DropboxRestApi.BaseUri), consumerKey, consumerSecret, requestToken);
}
// Complete Oauth function and write to file
private void button5_Click(object sender, EventArgs e)
{
DialogResult result1 = MessageBox.Show("Please register for dropbox before continuing with authentication. The authorization process will take 1 minute to complete. During that time the backup utility window will be unresponsive. Click yes if you are ready to begin the authorization. HAVE YOU REGISTERED FOR DROPBOX YET?", "DO YOU HAVE A DROPBOX ACCOUNT?", MessageBoxButtons.YesNo);
if (result1 == DialogResult.Yes)
{
try
{
u_w.Enabled = false;
var accesstoken = GetAccessToken();
StringBuilder newFile = new StringBuilder();
string temptoken = "";
string tempsecret = "";
string tempprovider = "";
string tempstatus = "";
string[] file = System.IO.File.ReadAllLines(#"C:\cfg\andro_backup.ini");
foreach (string line in file)
{
if (line.Contains("dbkey:"))
{
temptoken = line.Replace("dbkey:", "dbkey:" + accesstoken.Token);
newFile.Append(temptoken + "\r\n");
continue;
}
if (line.Contains("dbsecret:"))
{
tempsecret = line.Replace("dbsecret:", "dbsecret:" + accesstoken.Secret);
newFile.Append(tempsecret + "\r\n");
continue;
}
if (line.Contains("Provider:"))
{
tempprovider = line.Replace("Provider:", "Provider:DropBox");
newFile.Append(tempprovider + "\r\n");
continue;
}
if (line.Contains("Status:"))
{
tempstatus = line.Replace("Status:", "Status:Connected");
newFile.Append(tempstatus + "\r\n");
continue;
}
newFile.Append(line + "\r\n");
}
System.IO.File.WriteAllText(#"C:\cfg\andro_backup.ini", newFile.ToString());
MessageBox.Show("Completed Backup Provider Setup", "Provider Setup Complete");
Configuration.Reload();
The Above works at the moment and I can upload, download files. The issue is it's not Async and I would like to attempt to stay within the .NET 4.0 if possible, this code requires 4.5
Trying to do the same thing with dropnet, I am unable to get it to work at all even using the examples he has given on the page located here https://github.com/dkarzon/DropNet.
I attempted to look at the demos he has on there as well , but they explaing having the user login everytime to perform any functions, where I need the app to be authorized so it can do it's deeds when it needs to. As far as the code I am using for drop net, I literally just copied and pasted what he had there, just to even see if I can get it to connect and still no go.
If you are using DropNet similar to the examples all you need to do is save the return object from the GetAccessToken method. That returns an instance of a UserLogin object which has the Token and secret on it. Or if you are using the async methods for it then the callback parameter.
Checkout the sample here:
https://github.com/dkarzon/DropNet/blob/master/DropNet.Samples/DropNet.Samples.WP7/MainPage.xaml.cs#L69
Post the code you are using for it so I can give you a better explanation for it.
I'm trying to intercept tapping on a link in a WebBrowser control.
My HTML page contains custom links, for some starting with shared:// I'd like to intercept when the user tap on it.
On iPhone I would use the webView:shouldStartLoadWithRequest:navigationType: method, and look at the URL that is selected.
I haven't managed to reproduce a similar behaviour with Silverlight for Windows Phone.
I do something like:
{
webBrowser1.Navigating += new EventHandler<NavigatingEventArgs>(webBrowser1_Navigating);
}
void webBrowser1_Navigating(object sender, NavigatingEventArgs e)
{
string scheme = null;
try
{
scheme = e.Uri.Scheme; // <- this is throwing an exception here
}
catch
{
}
if (scheme == null || scheme == "file")
return;
// Not going to follow any other link
e.Cancel = true;
if (scheme == "shared")
{
}
But I guess an exception when reading some properties of the Uri, when it's a standard Uri with a default file:// URL
Additionally, the Navigating event isn't even triggered for links starting with shared://
Now that I'm able to capture tapping on a shared:// I do not care much, but at least I'd like to be able to retrieve the URL we're going to navigate to, and cancel the default operation for a particular URL.
Any ideas what's going on?
Thanks
Edit:
It turned out that the problem is that the Navigating event is only generated for the following links: file://, http:// or mailto://
The scheme attributes of the Uri is only available for the http:// and mailto:// links
so what I did in the end is replace the shared:// link with http://shared/blah ... And I look at the URL... This works for my purpose. I can now have links that have a different action (like opening an extra window) depending on the links in the html.
Here is my final code, in case this is useful for someone in the future:
For an about screen, I use an html file displayed in a WebBrowser component.
The about page has a "tell your friend about this app" link as well as links to external web site.
It also has local subpages.
Local sub-pages are linked to using a file:// link. Those can be navigated within the WebBrowser component.
External links are opened externally with Internet Explorer.
Tell your friend link is made of a http://shared link, that opens an email with a pre-set subject and body. Unfortunately, no other scheme than the standard ones are usable as they do not trigger a Navigating event
There's also a support link which is a mailto:// link and opens an EmailComposeTask
void webBrowser1_Navigating(object sender, NavigatingEventArgs e)
{
String scheme = null;
try
{
scheme = e.Uri.Scheme;
}
catch
{
}
if (scheme == null || scheme == "file")
return;
// Not going to follow any other link
e.Cancel = true;
if (scheme == "http")
{
// Check if it's the "shared" URL
if (e.Uri.Host == "shared")
{
// Start email
EmailComposeTask emailComposeTask = new EmailComposeTask();
emailComposeTask.Subject = "Sharing an app with you";
emailComposeTask.Body = "You may like this app...";
emailComposeTask.Show();
}
else
{
// start it in Internet Explorer
WebBrowserTask webBrowserTask = new WebBrowserTask();
webBrowserTask.Uri = new Uri(e.Uri.AbsoluteUri);
webBrowserTask.Show();
}
}
if (scheme == "mailto")
{
EmailComposeTask emailComposeTask = new EmailComposeTask();
emailComposeTask.To = e.Uri.AbsoluteUri;
emailComposeTask.Show();
}
}