My goal is to download a file from Google Drive using the Drive API v3 and VB.NET.
I set up my credentials in the Google console: Under "OAuth 2.0 client IDs" I put "http://localhost" in "Authorized redirect URIs" and in "Authorized JavaScript origins" and have my client secret file. I am on ASP.NET 4.0 using NuGet package Google.Apis.Drive.v3.
The error happens on line "credential = GoogleWebAuthorizationBroker.AuthorizeAsync". It pops up a new Chrome tab and says:
That’s an error.
Error: redirect_uri_mismatch
The redirect URI in the request, http://localhost:9895/authorize/, does not match the ones authorized for the OAuth client. To update the authorized redirect URIs, visit: https://console.developers.google.com/apis/credentials/oauthclient/[MyClientID]?project=[MyProjectNumber]
However each time I get a different port number.
Public Function AuthenticateOauth() As DriveService
' Request authentication from a user using oAuth2
Dim clientSecretJson = "C:\WebApps\PeruvianGuineaPig\App_Data\client_secret.json"
Dim applicationName = "DriveApi"
Try
' Permissions
Dim scopes As String() = New String() {DriveService.Scope.DriveReadonly}
Dim credential As UserCredential
Using stream As New FileStream(clientSecretJson, FileMode.Open, FileAccess.Read)
Dim credPath As String
credPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)
credPath = Path.Combine(credPath, ".credentials/", System.Reflection.Assembly.GetExecutingAssembly.GetName.Name)
' Requesting Authentication
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
scopes,
"user",
CancellationToken.None,
New FileDataStore(credPath, True)).Result
End Using
' Create Drive API service
Dim Service = New DriveService(New BaseClientService.Initializer() With
{.HttpClientInitializer = credential, .ApplicationName = applicationName})
Return Service
Catch ex As Exception
Return Nothing
End Try
End Function
I didn't realize I needed to open the project as a Web Site (and pick it from my list of Local IIS Sites) and not simply open the Project/Solution file. It now uses the port number I gave it in IIS when I'm debugging.
I have another issue now, but that's for another question...
Related
In our I have company intranet a server, that is responsible for storing files. Initially, the server had to operate only in an intranet environment, but now there is a need to share files with external web applications. Making this server accessible from the internet is not an option.
I want to create a ASP.NET MVC solution that uses the WebClient to get these files from the intranet server and send back them to the user through FileResult of the external app. This client would be provided with custom domain user credentials. So far I have tried to create a CredentialCache class, set correct credentials and append it to WebClients Credentials property like in the following code:
public ActionResult Download(int id, string fileName)
{
var fileService = new FilesService();
var documentUrl = fileService.GetUrlFileByFileId(id);
string filePath = "http://my.intranet.com/" + documentUrl;
var fileNameFromUrl = filePath.Substring(filePath.LastIndexOf("\\") + 1);
byte[] filedata;
CredentialCache cc = new CredentialCache();
cc.Add(new Uri("http://my.intranet.com/"),
"ntlm",
new NetworkCredential("myUserName", "myPassword", "myDomain"));
using (var client = new WebClient())
{
client.Credentials = cc;
filedata = client.DownloadData(filePath);
}
string contentType = MimeMapping.GetMimeMapping(filePath);
var cd = new ContentDisposition
{
FileName = fileName,
Inline = false
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(filedata, contentType);
}
According to the question posted in Domain credentials for a WebClient class don't work it should work, but it’s not. It’s running only if I run the problem on localhost, but when I publish my solution on a test server, it return 401 error. My question is did how to get this working? And is it possible to download files through this method?
UPDATE--- I've published my test app on another server and it started to working. Now the test app is on another server than the server That stores files. Any ideas why it's not working when both are on the same machine?
401 error is unauthorized, so perhaps the issue is related to permissions. Are you sure the user account you are using to login to that folder has the proper access?
Ok, I found the solution on this site: https://blogs.msdn.microsoft.com/distributedservices/2009/11/10/wcf-calling-wcf-service-hosted-in-iis-on-the-same-machine-as-client-throws-authentication-error/
The solution was to add an registry entry and add my web apps to this entry to allow back connections.
I set up SendGrid account and got key and pw.
My VisualBasic 2015 console app runs DeliverAsync without error, but email doesn't get to Internet receipients (my Hotmail account).
Also, the task.wait() throws exception "Bad username / password", which is posted at the end
Wireshark on Azure shows no SMTP, but I don't know if SendGrid uses SMTP.
Here is the app:
' Create the email object first, then add the properties.
Dim myMessage As SendGridMessage
myMessage = New SendGridMessage()
' Add the message properties.
myMessage.From = New MailAddress("<my email addr>")
' Add multiple addresses to the To field.
myMessage.AddTo("<destination email addr 1>")
myMessage.AddTo("<destination email addr 2>")
myMessage.AddTo("<destination email addr 3>")
myMessage.Subject = "Testing the SendGrid Library 2"
'Add the HTML and Text bodies
myMessage.Html = "<p>Hello World!</p>"
myMessage.Text = "Hello World plain text!"
Dim credentials As NetworkCredential
credentials = New NetworkCredential("apikey", "<my api pw>")
transportWeb = New Web(credentials)
Dim task = transportWeb.DeliverAsync(myMessage)
Try
task.wait()
Catch ex As AggregateException
Stop '<<<<<<<<< I GET: "Bad username / password"
Catch
End Try
EXCEPTION DETAILS:
"Bad username / password"
DeliverAsync returns a Task, so you need to await the task.
Await transportWeb.DeliverAsync(myMessage)
Of course, to use the await keyword your method needs to be marked as async. If you don't want to do that, then you can manually wait on the task.
Dim task = transportWeb.DeliverAsync(myMessage)
task.Wait()
You should familiarize yourself with the Task-based Asynchronous Pattern (TAP). Often when a function name ends in -Async then it uses TAP.
I got it working by creating new VB web app instead of win app.
VB > create new proj > web app > MVC and then props > references > NU.. Mgr > search SendGrid > Install, and that's it.
EDIT: This is my final code after taking your(#DalmTo) advice:
public static AnalyticsService Authenticate()
{
string[] scopes = new string[] { AnalyticsService.Scope.Analytics,
AnalyticsService.Scope.AnalyticsManageUsers};
string keyFilePath = #"G:\PleskVhosts\mydomainname\httpdocs\App_Data\API Project-2f74017ed363.p12"; // found in developer console
string serviceAccountEmail = "myconsoleapiaccount#developer.gserviceaccount.com"; // found in developer console
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail) { Scopes = scopes }.FromCertificate(certificate));
AnalyticsService service = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "myappname",
});
Thank you so much for the tutorial you linked me, I examined it carefully and your code was so much less work than trying to do it manually. I am aware of the tips you have given me, and I have necessary permissions for that account in my Google Analytics account. I followed your tutorial, and it works like a charm in my localhost, but when I publish my website, this is the current error I am getting this error:
{"Message":"Access is denied.\r\n","StackTrace":" at System.Security.Cryptography.X509Certificates.X509Store.Add(X509Certificate2 certificate)\r\n at Thunder.Main.Default.Authenticate()\r\n at Thunder.Main.Default.GetChartData()","ExceptionType":"System.Security.Cryptography.CryptographicException"}
I have contacted with my hosting provider, and they are telling me that they won't be making changes in IIS, I've added trust level full tag to the web.config, but I am still getting this error.I am currently working on it, but if you have any advices, please let me know. I will update here If I can come up with a solution. Thanks!
As mentioned above you need to configure IIS but as our case, some time you need to check the permission of the following folder:
C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
If you set X509KeyStorageFlags parameter it will create a key file in this folder. In my case there was a difference in permission of this folder. Pool account was not added in the mentioned folder.
You really are making things harder for yourself here. I am not sure whats wrong with your code. TBH I have never tried doing it manually because I use the client library
NuGet Package
PM> Install-Package Google.Apis.Analytics.v3
Authentication with a service account.
You need to send the full path to the key file or sometimes it complains. Ideally it should be out side of the web root but it needs to be someplace that the webserver has access to read it since you are using asp for this.
string[] scopes =
new string[] {
AnalyticsService.Scope.Analytics, // view and manage your Google Analytics data
AnalyticsService.Scope.AnalyticsManageUsers}; // View Google Analytics data
string keyFilePath = #"c:\file.p12" ; // found in developer console
string serviceAccountEmail = "xx#developer.gserviceaccount.com"; // found in developer console
//loading the Key file
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential( new ServiceAccountCredential.Initializer(serviceAccountEmail)
{Scopes = scopes }.FromCertificate(certificate));
Creating Service
You pass the credential created above to the service. All of your requests will then go though the service.
AnalyticsService service = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Analytics API Sample",
});
Tips
For a service account to work with Google Analytics it must have access to your Google Analytics account. Go to the Google Analytics website admin section create a new user at the account level it must be the account level. Did I mention it wont work if it isn't the account level.
Code is taken from my tutorial series. Google Analtics with C# enjoy
I want to open a website on a button click of asp.net web page. The website have basic authentication to open. I am using following code but its not the desired code to open website.
CredentialCache credentialCache = new CredentialCache();
NetworkCredential credentials = new NetworkCredential("uid", "pwd", "domain");
credentialCache.Add(new Uri("https://www.abc.com"), "Basic", credentials);
var request = (HttpWebRequest)WebRequest.Create("https://www.abc.com");
request.Credentials = credentialCache;
HttpWebResponse res1 = (HttpWebResponse)request.GetResponse();
using (HttpWebResponse res = (HttpWebResponse)request.GetResponse())
{
string ss = res.ResponseUri.ToString();
StreamReader sr = new StreamReader(res.GetResponseStream());
Response.Write (sr.ReadToEnd());
}
Response.Write write the html of the requested page on my page but I need to redirect to that page to access the website.
After a long search and R & D finally i got the solution of this problem. We need to add the reference of a COM Component Microsoft Internet Controls i.e. using SHDocVw;
When we try to open any website which requires the basic authentication then server respond with "HTTP/1.1 401 Unauthorized\r\n" and requires the client credentials and sends the header "www_authenticate" to the client when any browser recieve this header then its open the pop up box to insert the client credentials and we pass the client credentials to the server. Then server allows to access the application.
If we pass the client credentials on the first request to the server then it allows to access the application and don't requires the client credentials.
I have passed the client credentials with the following code .
InternetExplorer IEControl = new InternetExplorer();
IWebBrowserApp webBrowserCtl = (IWebBrowserApp)IEControl;
string strUserName = "UserName";
string strPassword = "Password";
string strAuthenticationHeader = "Authorization: Basic " + Convert.ToBase64String(Encoding.Default.GetBytes(strUserName + ":" + strPassword)) + "\r\n";
webBrowserCtl.Visible = true;
webBrowserCtl.Navigate("http://www.abc.com/Home.aspx", null, null, null, strAuthenticationHeader);
Thanks
Rahul Pratap Singh
I am trying to connect to a web service that uses Kerberos Authentication to authorize the user, but all I get is a 401 unauthorized everytime I try to make the request. Below is the code that I am using. Thanks in advance for any help you can provide!
public XPathNavigator GSASearch(string url, string searchString)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + searchString);
request.CookieContainer = new CookieContainer();
request.Credentials = CredentialCache.DefaultCredentials;
request.ContentType = "text/xml";
request.Method = "POST";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream receiveStream = response.GetResponseStream();
XPathDocument doc = new XPathDocument(receiveStream);
return doc.CreateNavigator();
}
EDIT: I feel I should explain a bit more what I am attempting to do. I have been tasked with providing a new interface for my company's Google Search Appliance. I am using an ASP.NET page, which does some things like choose a Collection depending on where a user is located, etc. and then sends the appropriate search string the the GSA. This was all working well until they decided to turn authentication on, and now I can't get any results (I either get a 401 unauthorized, or a message stating that 'Data at the root level is invalid'). If I take the search string and provide it directly to the GSA, it authenticates fine, and displays the results, I just can't seem to get it through the HttpWebRequest.
EDIT 2: I did a little more looking (ran the request through Fiddler) and it looks like the request is only attempting Negotiate and not Kerberos. I set the credentials to use Kerberos explicitly as below, but it didn't help...
public XPathNavigator GSASearch(string url, string searchString)
{
CredentialCache credCache = new CredentialCache();
credCache.Add(new Uri(url), "Kerberos", CredentialCache.DefaultNetworkCredentials);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + searchString);
request.CookieContainer = new CookieContainer();
request.PreAuthenticate = true;
request.Credentials = credCache;
request.ContentType = "text/xml";
request.Method = "POST";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream receiveStream = response.GetResponseStream();
//StreamReader readStream = new StreamReader(receiveStream);
XPathDocument doc = new XPathDocument(receiveStream);
return doc.CreateNavigator();
}
EDIT 3: Ok, looking closer again, the CredentialCache.DefaultCredentials doesn't appear to have my network credentials in it...
1) Have you done a wireshark trace of a successful session to the GSA using the browser? Does that work?
2) If #1 works, what is the WWW-Authenticate header that is sent by the GSA on the first unauthenticated request?
3) Is the machine on which the ASPX app is running a part of the same AD domain that the GSA is in? AFAIK this is probably required for a successful auth.
4) Next, since it is the ASPX app that is doing the request, you cannot use the DefaultCredentials because you actually need the credentials of a user that is trusted by the GSA. For this you should either create a special user account for the app that is talking to the GSA, or have each user be a trusted user on the GSA and have the ASPX page authenticate the user first, then pass those credentials to the GDA using Delegation. For this you will also have to make the server running the ASPX app trusted for delegation.
In my opinion, you should first model your code into a console app that you run, and debug. Then port it to ASPX page. That way you will be able to know if the failure is due to the host (ASPX vs console) or something else.