Google API OAuth2 access with Access and Refresh Tokens .net - vb.net

I am trying to figure out how to get permanent access to a users Google Calendar with OAuth2. I am at the point that I can get the user to authorize access and I have the AccessToken, RefreshToken, and/or the complete Response Token from Google stored in a database.
The issue I am facing is how to get these tokens in to a credential in the Google API. I have found no examples that work. I have tried a few things. It may be as simple as I am missing a reference to a dll that I cannot figure out. The GoogleCredential.Builder() seems to be the way to go, but I cannot find it anywhere except for the Java API.
Here is the closest I have gotten. The issue is how to create the correct credentials.
Dim user As CRMUser = GetUser(Membership.GetUser().ProviderUserKey)
Dim scopes As IList(Of String) = New List(Of String)()
scopes.Add(CalendarService.Scope.Calendar)
Dim ClientID As String = System.Configuration.ConfigurationManager.AppSettings("GoogleCalendarApplicationClientID").ToString()
Dim ClientSecret As String = System.Configuration.ConfigurationManager.AppSettings("GoogleCalendarApplicationClientSecret").ToString()
Dim service As CalendarService
Dim credential As GoogleCredential = New GoogleCredential.Builder().setJsonFactory(jsonFactory).setTransport(httpTransport).setClientSecrets(ClientID, ClientSecret).build()
credential.setAccessToken(user.GoogleUserToken)
credential.setRefreshToken(user.GoogleRefreshToken)
Dim initializer As New BaseClientService.Initializer()
initializer.HttpClientInitializer = credential
initializer.ApplicationName = "VB.NET Calendar Sample"
service = New CalendarService(initializer)

Related

.NET Google API access token failing with no refresh token specified

I am trying to set up a class that can wrap around the .NET Google API so that I can use an Access Token that I have previously obtained to access a user's Google Drive. As of right now, I am just trying to get it to work so that I do not require a Refresh Token (more on that in a second). The ultimate goal is for somebody to go through a web page I have set up to authenticate where I obtain both an Access Token and a Refresh Token by directly calling to the Google Rest API (which I store in a database). They can then request to upload/download files onto their Drive on a different page which will first obtain the appropriate information from the database and then use the .NET Google API Library when accessing Drive.
However, when I attempt to access their Drive I get the the following error:
The access token has expired and could not be refreshed. Errors: refresh error, refresh error, refresh error
I know that the Access Token is valid because I obtain it only seconds earlier during my testing. Here is my code for setting up the Drive Service:
' NOTE: Code altered for brevity
Public Sub Initialize(accessToken As String)
' Set up the client secret information based on the default constants
Dim clientSecrets As New ClientSecrets()
clientSecrets.ClientId = DEFAULT_CLIENT_ID
clientSecrets.ClientSecret = DEFAULT_CLIENT_SECRET
' Set up a token based on the token data we got
' NOTE: Is it OK to leave some strings as NULL?
Dim token As New Responses.TokenResponse()
token.AccessToken = accessToken
token.RefreshToken = ""
token.TokenType = "Bearer"
token.IssuedUtc = DateTime.Now
token.ExpiresInSeconds = 3600
token.Scope = "drive"
token.IdToken = ""
' Set up a flow for the user credential
Dim init As New GoogleAuthorizationCodeFlow.Initializer()
init.ClientSecrets = clientSecrets
init.Scopes = New String() {DriveService.Scope.Drive}
init.Clock = Google.Apis.Util.SystemClock.Default
' Set up everything else and initialize the service
Dim baseInit As New BaseClientService.Initializer()
baseInit.HttpClientInitializer = New UserCredential(New GoogleAuthorizationCodeFlow(init), "user", token)
baseInit.ApplicationName = APP_NAME
_service = New DriveService(baseInit)
End Sub
Shortly after that, I then use the following code to request a folder so I can check to see if it exists or not.
Private Function GetDriveFolder(folderPath As String, ByRef folderIds As String(), Optional createMissingFolders As Boolean = False, Optional parentFolderId As String = "root") As Data.File
Dim creatingFolderPath As Boolean = False
Dim currentFolder As Data.File = Nothing
Dim folderPathSplit As String() = folderPath.Replace("/", "\").Trim("\").Split("\")
Dim folderIdList As New List(Of String)
folderIds = {}
' Loop through each folder in the path and seek each out until we reach the end
For x As Integer = 0 To folderPathSplit.Length - 1
Dim result As FileList = Nothing
If Not creatingFolderPath Then
' Build a list request which we will use to seek out the next folder
Dim request As FilesResource.ListRequest = _service.Files.List()
request.Q = "mimeType='application/vnd.google-apps.folder' and name='" & folderPathSplit(x) & "'"
If currentFolder Is Nothing Then
request.Q &= " and '" & EscapeDriveValue(parentFolderId) & "' in parents"
Else
request.Q &= " and '" & EscapeDriveValue(currentFolder.Id) & "' in parents"
End If
request.Spaces = "drive"
request.Fields = "files(id, name)"
' Execute the search, we should only get a single item back
' NOTE: Error thrown on this request
result = request.Execute()
End If
' So on.....
So, I'm just trying to get it to work with only the Access Token for the time being because if it ends up getting refreshed I'll need to know so that I can update my database. However, if I do include the Refresh Token I get the following error:
Error:"unauthorized_client", Description:"Unauthorized", Uri:""
I'm guessing this has something to do with the way I have configured my application through the Dev Console but if I authenticate through the Google API Library by having it launch a browser to get my credentials everything works fine. So, I'm really not sure where to go from here as I haven't found anybody having similar problems and the guides don't cover specifying your own Access Token.
Also, as a quick note this is the URL I am using when having the user authenticate:
String.Format("https://accounts.google.com/o/oauth2/v2/auth?client_id={0}&state={1}&redirect_uri={2}&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&access_type=offline&include_granted_scopes=true&prompt=select_account%20consent&response_type=code", GOOGLEAPI_CLIENTID, validateState, redirectUri)
Thanks for the help!
If you have an access-token then the simplest way to create a google credential is to use the GoogleCredential.FromAccessToken() method passing in your access token.
This returns you a GoogleCredential instance which you can use to set the HttpClientInitializer property when building the DriveService.
If you then still get an error when accessing the drive service, then it's likely there's something incorrect in how you are asking for the access-token.

Google Drive API v3 VB.NET Upload and Download Files to ServiceAccount

I've been trying to follow https://developers.google.com/drive/v3/web/quickstart/dotnet and the Google API documentation, as well as searching all over the internet, but there really isn't a straightforward example to use (especially for v3)
I have a VB.NET GUI that contains a listview with the names of all plain text files in a folder. Clicking on one will display its' contents in a textbox. You can also type into a blank text box and save it. I want to allow multiple users to upload their text file to the Google Drive and be able to download all text files that are stored there.
I don't have much of an issue translating code from C# to VB.NET, and I think I'm fine with authenticating the service account with Google (or at least I don't get an error), but uploading only shows me response = Nothing. Any help is appreciated.
I created the service account through Google and have the following:
Dim service = AuthenticateServiceAccount("xxxxx#xxxxx.iam.gserviceaccount.com", "C:\Users\xxxxx\Documents\Visual Studio 2015\Projects\MyProject\accountkey-0c1aa839896b.json")
If drive.UploadFile(service, "C:\Users\xxxxx\Documents\Visual Studio 2015\Projects\MyProject\file.txt") Is Nothing Then
MsgBox("File not uploaded")
Else
MsgBox("File uploaded")
End If
Authenticate:
Public Function AuthenticateServiceAccount(ByVal serviceAccountEmail As String, ByVal serviceAccountCredentialFilePath As String) As DriveService
Dim scopes As String() = {DriveService.Scope.Drive, DriveService.Scope.DriveAppdata, DriveService.Scope.DriveReadonly, DriveService.Scope.DriveFile, DriveService.Scope.DriveMetadataReadonly, DriveService.Scope.DriveReadonly, DriveService.Scope.DriveScripts}
Try
If (String.IsNullOrEmpty(serviceAccountCredentialFilePath)) Then
Throw New Exception("Path to the service account credentials file is required.")
End If
If Not IO.File.Exists(serviceAccountCredentialFilePath) Then
Throw New Exception("The service account credentials file does not exist at: " + serviceAccountCredentialFilePath)
End If
If (String.IsNullOrEmpty(serviceAccountEmail)) Then
Throw New Exception("ServiceAccountEmail is required.")
End If
If (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() = ".json") Then
Dim credential As GoogleCredential
Dim sstream As New FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read)
credential = GoogleCredential.FromStream(sstream)
credential.CreateScoped(scopes)
'Create the Analytics service.
Return New DriveService(New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "Drive Service Account Authentication Sample"
})
Else
Throw New Exception("Unsupported Service accounts credentials.")
End If
Catch e As Exception
MsgBox("Create service account DriveService failed" + e.Message)
Throw New Exception("CreateServiceAccountDriveFailed", e)
End Try
End Function
Upload File:
Public Function UploadFile(service As DriveService, FilePath As String) As Google.Apis.Drive.v3.Data.File
If (System.IO.File.Exists(FilePath)) Then
Dim body As New Google.Apis.Drive.v3.Data.File()
body.Name = System.IO.Path.GetFileName(FilePath)
body.Description = "Text file"
body.MimeType = "text/plain"
'files content
Dim byteArray As Byte() = System.IO.File.ReadAllBytes(FilePath)
Dim stream As New System.IO.MemoryStream(byteArray)
Try
Dim request As FilesResource.CreateMediaUpload = service.Files.Create(body, stream, "text/plain")
request.Upload()
Return request.ResponseBody
Catch e As Exception
MsgBox("An error occurred: " + e.Message)
Return Nothing
End Try
Else
MsgBox("File does not exist: " + FilePath)
Return Nothing
End If
End Function
As stated here, since you are using a Service Account, all the folders and files will be created in this Service Account's Drive which cannot be accessed through a web UI and will be limited to the default quota.
To add content in a user's Drive, you will need to go through the regular OAuth 2.0 flow to retrieve credentials from this user. You can find more information about OAuth 2.0 on this pages:
Retrieve and use OAuth 2.0 credentials.
Quickstart: it has a quickstart sample in C# that you could use.
Using OAuth 2.0 to access Google APIs
You may also check this related thread: upload files to Google drive in VB.NET - searching for working code

Get authenticated ClientContext with logged in credentials

I'm new to using SharePoint. Is it possible to get an authenticated ClientContext without a username and password? I would like to make my app automatically access SharePoint if the machine is already logged in to Office 365 (word,excel,outlook) or to SharePoint in a web browser. I need the authenticated ClientContext so i can pass the credentials to a DownloadFile method.
Private Sub DownloadFile(webUrl As String, credentials As ICredentials, fileRelativeUrl As String)
Using client = New WebClient()
client.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f")
client.Headers.Add("User-Agent: Other")
client.Credentials = credentials
client.DownloadFile(webUrl, fileRelativeUrl)
End Using
End Sub
Thanks in advance.

Google Calendar - To manage multiple users calendar

I have a module in my .NET system that manage multiple user's Google Calendar which written base on their V2 API that was depreciated few days ago.
My question is, how can I access and manage multiple users calendar? As in V2 API we can just pass in UserID and Password to access the resources. But in V3, OAuth2 is used. I manage to authenticate for one user at a time. How can I access the another user calendar after this? (eg:logut user1 and login with user2 again?). I can't find any relevant example so far.
Sub Main()
Dim secret As New ClientSecrets
secret.ClientId = "MyClientID.apps.googleusercontent.com"
secret.ClientSecret = "MyClientSecret"
Dim init As New Flows.AuthorizationCodeFlow.Initializer("https://accounts.google.com/o/oauth2/auth", "https://accounts.google.com/o/oauth2/token")
init.ClientSecrets = secret
Dim flow As New Flows.AuthorizationCodeFlow(init)
Dim token As Responses.TokenResponse = flow.LoadTokenAsync("user1#gmail.com", CancellationToken.None).Result
Dim credential As New UserCredential(flow, "user1#gmail.com", token)
Try
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(secret, {CalendarService.Scope.Calendar}, "user", CancellationToken.None, New FileDataStore("CAS.GoogleConnector")).Result
Catch ex As Exception
MsgBox(ex.Message)
End Try
' Create the calendar service using an initializer instance
Dim initializer As New BaseClientService.Initializer()
initializer.HttpClientInitializer = credential
initializer.ApplicationName = "Clinic Assist"
Dim service As CalendarService
service = New CalendarService(initializer)
Dim a As CalendarsResource.GetRequest = service.Calendars.Get("user1#gmail.com")
a.Execute()
Console.WriteLine("Press any key to continue...")
Dim key As ConsoleKeyInfo = Console.ReadKey
If key.Key = ConsoleKey.N Then
NewEvent(service)
ElseIf key.Key = ConsoleKey.D Then
DeleteEvent(service)
ElseIf key.Key = ConsoleKey.C Then
NewCalendar(service)
End If
End Sub
Private Sub NewEvent(service As CalendarService)
Dim startDateTime, endDateTime As New Data.EventDateTime
startDateTime.DateTime = "2014-12-01T16:00:00"
endDateTime.DateTime = "2014-12-01T17:00:00"
Dim eventData As New Data.Event
With eventData
.Start = startDateTime
.End = endDateTime
.Location = "ALocation"
.Summary = "TRY"
End With
Try
Dim insertRequest As InsertRequest = service.Events.Insert(eventData, "user1#gmail.com")
insertRequest.Execute()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
I tried to change the userId from "user1#gmail.com" to "user2#gmail.com" it seems to have accessing the user1 calendar as well. Appreciate if anyone could help this out.
If all the users are on single domain you can use Service Accounts. Service account belongs to your application instead of to an individual end user. Your application calls Google APIs on behalf of the service account, so users aren't directly involved.
If users are not on the same domain, you will need to store credentials for each user in your application. Here is the link which helps you.

VB.NET (WebRequest Authentication Issue)

I'm new to WebRequest authentication and have been researching how to authenticate w/ a couple websites to pull some excel data from them. Couple things I'm confused about is
a.) how to properly read a log from Fiddler (using this to pick up get/post data from the website authentication)
b.) how do use the data from Fiddler to program the VB.NET WebRequest properly.
I've been able to authenticate w/ websites that use simple authentication HTTPS, but any site that does any redirects/REST/cookie auth I'm lost...
Let me know if I can provide anymore detail.
Dim req As Net.HttpWebRequest = Net.WebRequest.Create(Url)
If Not Login = Nothing AndAlso Not Password = Nothing Then
Dim myCache As New System.Net.CredentialCache()
myCache.Add(New Uri(Url), "Basic", New System.Net.NetworkCredential(Login, Password))
req.Credentials = myCache
End If
Dim sr As New StreamReader(req.GetResponse().GetResponseStream())
Dim ss as string = sr.ReadToEnd
'Save it as excel & close stream
sr.Close()