How to login to Google Calendar without OAuth2? - vb.net

SUMMARY
I have a Vb.net application (Windows form) and I need to manage google calendar for any of software user. I thought of having the user access directly from the program and not authenticate himself for every operation on the calendar
I tried OAuth2 method but is not user friendly. I must login directly from the app and without json files.
WHAT I TRIED
I'm developing software with integrated Google Calendar.
I'm not a Google API expert, so by searching a bit on the web, I managed to authenticate using the credentials.json file that directly creates Google.
At the moment, the software works like this: I press the "Login" button; automatically opens the Google login page in the browser; I enter email and password on the official Google login page; I access; I manually close the browser page; I go back to the program where a msgbox warns me of successful authentication.
Public scopes As IList(Of String) = New List(Of String)()
Public service As CalendarService
Function LoginGoogle()
scopes.Add(CalendarService.Scope.Calendar)
Dim credential As UserCredential
If Directory.Exists("C:\credentials") Then
Try
Directory.Delete("c:\credentials", True)
Catch
MsgBox("Error! Can't delete folder 'credentials'")
End Try
Using stream As New FileStream("credentials.json", FileMode.Open, FileAccess.Read)
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets, scopes, "user", CancellationToken.None,
New FileDataStore("c:\credentials")).Result
End Using
Dim initializer As New BaseClientService.Initializer()
initializer.HttpClientInitializer = credential
initializer.ApplicationName = "Carpedia" ' app's name
service = New CalendarService(initializer)
Return 0
Else
Using stream As New FileStream("credentials.json", FileMode.Open, FileAccess.Read)
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets, scopes, "user", CancellationToken.None,
New FileDataStore("c:\credentials")).Result
End Using
Dim initializer As New BaseClientService.Initializer()
initializer.HttpClientInitializer = credential
initializer.ApplicationName = "Carpedia" ' app's name
service = New CalendarService(initializer)
Return 0
End If
End Function
WHAT I WOULD LIKE
I would like to create a form where to enter email and password; click on the login button; log in automatically without using the browser.
Example: https://youtu.be/FW9_gCD8vVg?t=15 (minute 0:18)
How can I do?
Thanks in advance.

Logging in with username and password though your application is called client login. This was shut down by google in 2015.
In order to access user data you must have the permission of the user to do this. The only way for you to get the permission of the user is to use OAuth2. There is no other option for you to do this. As you can see the google calendar api documentation mentions only oauth2 for accessing private user data.
You appear to already be using the Google APIs .net client library which is the best way i know of to authenticate a user in a windows application.

Related

How to use YouTube Data API

I tried using YouTube Data API.
I really took a good look at everything I found on the internet. The code itself isn't the problem, but I did not find out, where to use this code. Do I simply create a python file (in Visual Studio Code for example) and run it there? Because it didn't work when I tried this...
I also saw many people using the API with the commander only, others used something in chrome (localhost:8888...). So I don`t really know what's the way to go or what I should do.
Thanks for any help :)
Best regards!
I'm not a python developer but as a guess you could start here:
https://developers.google.com/youtube/v3/quickstart/python
using pip to install the dependencies you need.
You should be able to create a simple python file that authenticates with the API and then calls a method on the on the google api client and then output it. There are some examples here:
https://github.com/youtube/api-samples/blob/master/python/
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
namespace Google.Apis.YouTube.Samples
{
/// <summary>
/// YouTube Data API v3 sample: upload a video.
/// Relies on the Google APIs Client Library for .NET, v1.7.0 or higher.
/// See https://code.google.com/p/google-api-dotnet-client/wiki/GettingStarted
/// </summary>
internal class UploadVideo
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("YouTube Data API: Upload Video");
Console.WriteLine("==============================");
try
{
new UploadVideo().Run().Wait();
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
{
Console.WriteLine("Error: " + e.Message);
}
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
private async Task Run()
{
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = "Default Video Title";
video.Snippet.Description = "Default Video Description";
video.Snippet.Tags = new string[] { "tag1", "tag2" };
video.Snippet.CategoryId = "22"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
video.Status = new VideoStatus();
video.Status.PrivacyStatus = "unlisted"; // or "private" or "public"
var filePath = #"REPLACE_ME.mp4"; // Replace with path to actual movie file.
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
}
void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
Console.WriteLine("{0} bytes sent.", progress.BytesSent);
break;
case UploadStatus.Failed:
Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
break;
}
}
void videosInsertRequest_ResponseReceived(Video video)
{
Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
}
}
}
Make sure you have python installed on your PC
Create a project: Google’s APIs and Services dashboard
Enable the Youtube v3 API: API Library
Create credentials: Credentials wizard
Now you need to get an access token and a refresh token using the credentials you created
Find an authentication example in one of the following libraries:
https://github.com/googleapis/google-api-python-client
https://github.com/omarryhan/aiogoogle (for the async version)
Copy and paste the client ID and client secret you got from step 4 and paste them in the authentication example you found in step 6 (Should search for an OAuth2 example), this step should provide with an access token and a refresh token
Copy and paste a Youtube example from either:
https://github.com/googleapis/google-api-python-client
https://github.com/omarryhan/aiogoogle (for the async version)
Replace the access token and refresh token fields with the ones you got.
Now you should be able to run the file from any terminal by typing:
python3 yourfile.py
[EDIT]
The API key is not the same as the access token. There are 2 main ways to authenticate with Google APIs:
Access and refresh token
API_KEY.
API key won't work with personal info. You need to get an access and refresh token for that (method 1).
Once you get an access token, it acts in a similar fashion to the API_KEY you got. Getting an access token is a bit more complicated than only working with an API_KEY.
A refresh token is a token you get with the access token upon authentication. Access tokens expire after 3600 seconds. When they expire, your authentication library asks Google's servers for a new access token with the refresh token. The refresh token has a very long lifetime (often indefinite), so make sure you store it securely.
To get an access token and a refresh token (user credentials), you must first create client credentials. Which should consists of 1. a client ID and 2. a client secret. These are just normal strings.
You should also, set a redirect URL in your Google app console in order to properly perform the OAuth2 flow. The OAuth2 flow is the authentication protocol that many APIs rely on to allow them to act on a user's account with the consent of the user. (e.g. when an app asks you to post on your behalf or control your account on your behalf, it typically will use this protocol.)
Aiogoogle's docs does a decent job in explaining the authentication flow(s) available by Google.
https://aiogoogle.readthedocs.io/en/latest/
But this is an async Python library. If you're not familiar with the async syntax, you can read the docs just to get a general idea of how the authentication system works and then apply it to Google's sync Python library.
About point no.6. The links I posted with Aiogoogle being one of them, are just client libraries that help you access Google's API quicker and with less boilerplate. Both libraries have documentation, where they have links to examples on how to use them. So, open the documentation, read it, search for the examples posted, try to understand how the code in the example(s) work. Then maybe download it and run it on your own machine.
I recommend that your read the docs. Hope that helps.

Dotnetnuke OAUTH/OWIN external Login with facebook

I developed a module for Dotnetnuke(8) with WebAPI 2 Endpoints via the DNN API
This API is consumed by an Android-App.
To access the functions that are populated via the API, the user needs to authenticate.
I have already implemented the JWT (Json Web Token) Authentication with the WebAPI and login with username/password from the App works fine with this method.
Now I also want to allow users to login via their facebook-login and to get their name and email and photo from their facebook profile to authenticate and authorize them via the DNN-Users-Database and allow/disallow them to use the API functions.
I googled around a lot and read a lot of blogposts and articles about external authentication in the last few days. The following are very interesting and already gave me ann good insight how the process may work:
http://bitoftech.net/2014/08/11/asp-net-web-api-2-external-logins-social-logins-facebook-google-angularjs-app/
Registering Web API 2 external logins from multiple API clients with OWIN Identity
https://www.asp.net/web-api/overview/security/external-authentication-services
but I cannot really find out (and it seems i do not really understand) if and how this can be made working with my dnn-API and the JSON-WebToken Auth Method in my project.
If anybody can help to get me in the right direction, your help is highly appreciated.
Thanks in advance and kind regards
Don
EDIT: The DNN-API gives all the JWT-Functionality I just need to define the api paths and functions. e.g:
'
<Route("{controller}/{action}/{p1}")>
<AcceptVerbs("GET")>
<AllowAnonymous>
Public Function userInf(ByVal p1 As String) As HttpResponseMessage
Dim response As New HttpResponseMessage
Dim pID As Integer = DotNetNuke.Entities.Portals.PortalController.Instance.GetCurrentPortalSettings.PortalId
Dim objUserInfo As New DotNetNuke.Entities.Users.UserInfo
objUserInfo = DotNetNuke.Entities.Users.UserController.Instance.GetUserById(pID, CInt(p1))
If Not objUserInfo Is Nothing Then
If objUserInfo.UserID > 0 Then
response = Request.CreateResponse(System.Net.HttpStatusCode.OK, JsonConvert.SerializeObject("Username: " & objUserInfo.Username.ToString))
Else
' Not logged in
response = Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized, "Not found")
End If
Else
' Not logged in
response = Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized, "Not logged in")
End If
response.Headers.Add("Access-Control-Allow-Origin", CORS) ' <- Allow CORs !!!!
' response.Headers.Add("Access-Control-Request-Method", "*")
Return response
End Function
The Api Path for the DNN Web-API is for authentication:
example.com/DesktopModules/JwtAuth/API/mobile/Login
where I pass the username and password in the request-body as a json-object
(Documentation on dnnsoftware[dot]com / docs / administrators / jwt /)
This all works as expected. The thing now is how to make work the facebook login as an external login work together with my JWT-AUTH
Web api doing authentication by itself, yout need to create OAuthAuthorizationServerOptions and configure web api to use methods, there is an example of how web api token based auth works with standart Bearer token.
There ApplicationOAuthProvider its a class which generates token for inhereting from OAuthAuthorizationServerProvider.
To call method from your token generator you need to get to the path /api/token and request will automaticly give you token and user Claims, which you will define in your token generator.
public void ConfigureOAuth(IAppBuilder app)
{
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
var oauthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(24),
Provider = new ApplicationOAuthProvider(),
};
// Token Generation
app.UseOAuthAuthorizationServer(oauthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
Hope this help.

Google .Net Service Account access to Calendar

I am updating a web service application that calls Google's calendar API's to list calendar events for a particular calendar and insert new calendar events. I am trying to upgrade it to version 3 of the api's. For authentication I am using a Service Account Credential that I created in the Google Developers Console (https://console.developers.google.com). I am able to create the CalendarService using the following code :
using System;
using Google.Apis.Auth.OAuth2;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Services;
using Google.Apis.Calendar.v3;
using Google.Apis.Calendar.v3.Data;
...
string SERVICE_ACCOUNT_EMAIL =
"....googleusercontent.com";
string SERVICE_ACCOUNT_PKCS12_FILE_PATH = #"C:\temp\API Project-123456789.p12";
// Create the service.
X509Certificate2 certificate = new X509Certificate2(SERVICE_ACCOUNT_PKCS12_FILE_PATH, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(SERVICE_ACCOUNT_EMAIL)
{
Scopes = new[] { CalendarService.Scope.Calendar }
, User = "something#mycompany.com"
}.FromCertificate(certificate));
// Create the service.
var cs = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar API Sample",
});
But when I call the list method to query a public calendar:
Events events = service.Events.List("something#mycompany.com").Execute();
A TokenResponseException is thrown with the following error message:
Error:"invalid_grant", Description:"", Uri:""
FYI : I have gone into the AdminHome for my company and under security Manage clients API Access and registered the SERVICE_ACCOUNT_EMAIL above to http://www.google.com/calendar/feeds/.
Any help with this would be greatly appreciated.
I believe you need to register the Client ID of the service account of your App, I have successfully got it working using this difference.
Here is a similar issue I resolved with inserting events.
Google API Calender v3 Event Insert via Service Account using Asp.Net MVC
Here is the google documention on domain-wide-authorization.
https://developers.google.com/+/domains/authentication/delegation

Why Does my Service Account Attempt to Connect to Google API via OAuth2 Return an 'invalid_grant' error?

I am attempting to use a Service Account to talk to the Admin SDK Directory API via the .Net Client Library. However, the response I keep getting is a 400 Bad Request with a json response content of "error":"invalid_grant".
Here is my code
Public Sub New()
Dim _certificate = New X509Certificate2("c:\path\to\file.psk", "notasecret" _
, X509KeyStorageFlags.Exportable)
Dim _serviceAccountEmail = "email#developer.gserviceaccount.com"
Dim _scopes As IList(Of String) = New List(Of String)()
_scopes.Add(directory_v1.DirectoryService.Scope.AdminDirectoryUserReadonly)
Dim credential = New ServiceAccountCredential(New
ServiceAccountCredential.Initializer(_serviceAccountEmail) With {
.Scopes = _scopes}.FromCertificate(_certificate))
Dim service = New directory_v1.DirectoryService(New BaseClientService.Initializer() With { _
.HttpClientInitializer = credential,
.ApplicationName = "AeriesConnect"})
Dim request = service.Users.List()
request.Domain = "my.domain.com"
Dim users As directory_v1.Data.Users = request.Execute()
For Each user In users.UsersValue
System.Diagnostics.Debug.WriteLine("userName ID: " & user.Id)
Next
End Sub
Service Object
My service object has the following properties. All other properties are empty collections of the UsersResource or GroupsResource
service.BaseUri = 'https://www.googleapis.com/admin/directory/v1"
service.HttpClientInitializer Properties
Notice that the Token property is empty. Does that matter? It seems like it does. Is that my fault?
Request Object
I get the error right at the Execute and the exception is from Google and is a Google.Apis.Auth.OAuth2.Responses.TokenResponseException.
I think I've granted all the permissions I need to BUT keep in mind, this is for a Google Apps for Education account.
Logging in as a Super User to the Developer Console, I've created a project and enabled the Admin SDK API. I've also created the Credentials.
As a 'Super User' in that GAFE, I've gone to the "Security --> Advanced Settings --> Manage API Client Access" area and added my Client ID (not email) and the scope as https://www.googleapis.com/auth/admin.directory.user
Request Header
My request header that is POST to https://accounts.google.com/o/oauth2/token has this
assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6Imh0dHBzOi8vd3d3L
mdvb2dsZWFwaXMuY29tL2F1dGgvYWRtaW4uZGlyZWN0b3J5LnVzZXIiLCJpc3MiOiI2OTY3MTQyNzg2OS1hb
XFmMjVzY204Y2N1azhycHJrMDQ2djAzb2cybGducEBkZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50LmNvbSAiLC
JhdWQiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20vby9vYXV0aDIvdG9rZW4iLCJleHAiOjE0MTQ2MTY
3NDMsImlhdCI6MTQxNDYxMzE0M30.T9H-3tgOQSNdSj3MrUjJNNR5YqsxuJ97bCMJrrEJQ1Noewyv2mj
Dp96AZSZmzSxseAWrgptUv7sR4Mi21CWNACmjQjWPw8BI48sAyIclUPq6UhtMhRZDB1xX7t-Tq8NSbddJt
yJWxTc-IEAT7ixVpjjrosdG8Zcs0MgTuUgKHb8
&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
...and the last bits of it feel wrong. Why does it feel like it suddenly has been HTML encoded?
Question
What have I done wrong? I'm assuming it think's I'm not authenticated. But what do I need to do differently? I've used their examples to help, but I can't quite figure it out.
My problem was that I had the wrong value in the Service Account Email field. Simple solution. Took me two days to figure it out. Ugh!

Refresh Token with Google API Java Client Library

I'm using the Google API Java Client http://code.google.com/p/google-api-java-client/ and am able to get the access token successfully for Android.
// Google Accounts
credential = GoogleAccountCredential.usingOAuth2(this, CalendarScopes.CALENDAR);
SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
credential.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
As I'd like my web server to make offline API calls, I need a refresh token. I have been searching extensively and have not yet figured out how to do so.
Ideally, I'd prefer to use the Google API Java Client over the WebView to grab the refresh token (no need to enter a username or password).
Any help would be appreciated!
You can also do this by creating a refresh token configured to a OAuth 2.0 Client Id.
Go to https://console.developers.google.com/apis/credentials
Click 'Create Credential'.
Click 'OAuth client Id'.
Select 'Web application' > Give a name.
Add https://developers.google.com/oauthplayground to 'Authorized redirect URIs'.
Click Create.
You will need the ClientId and the Secret for next steps.
Then go to https://developers.google.com/oauthplayground/
Click 'AOuth 2.0 Configuration' on right upper corner.
Check 'Use your own OAuth credentials'.
Update 'OAuth Client ID' and 'OAuth Client secret' with client id and secret of above created OAuth 2.0 credential.
In Step 1 on left corner, Select all the necessary scopes.(Please note that unmatching scopes in request will return 'invalid_scopes'.)
Click 'Authorize APIs'. This will redirect you to a consent page to allow permissions.
In Step 2, click 'Exchange authorization code for tokens'
You will get an Access Token with a Refresh Token. We will need this Refresh Token for the next step.
You can use this access token to authenticate to services you specified in scopes.
Access Tokens are short lived and Refresh tokens expire after 24 hours unless it is not bound to a OAuth 2.0 client (We just made our refresh token to last until it is revoked by the user or expires due to 6 months inactivity).
You need to refresh the Access Token before it expires. Check out following example to see how.
public String getNewToken(String refreshToken, String clientId, String clientSecret) throws IOException {
ArrayList<String> scopes = new ArrayList<>();
scopes.add(CalendarScopes.CALENDAR);
TokenResponse tokenResponse = new GoogleRefreshTokenRequest(new NetHttpTransport(), new JacksonFactory(),
refreshToken, clientId, clientSecret).setScopes(scopes).setGrantType("refresh_token").execute();
return tokenResponse.getAccessToken();
}
clientId and clientSecret in above example refers to OAuth 2.0 client credentials.
You can create a 'GoogleCredential' with that like this
public Credential getCredentials() throws GeneralSecurityException, IOException, FileNotFoundException {
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
// Load client secrets.
String CREDENTIALS_FILE_PATH = "/credentials.json"; //OAuth 2.0 clinet credentials json
InputStream in = DriveQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
if (in == null) {
throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
}
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
String clientId = clientSecrets.getDetails().getClientId();
String clientSecret = clientSecrets.getDetails().getClientSecret();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setClientSecrets(clientId, clientSecret)
.build();
String refreshToken = "<REFRESH-TOKEN>"; //Find a secure way to store and load refresh token
credential.setAccessToken(getNewToken(refreshToken, clientId, clientSecret));
credential.setRefreshToken(refreshToken);
return credential;
}
You need to set the following when you initiate the authorization flow :
approval prompt = force
access type = offline
With these params set, google will return a refresh token and the library will deal with refreshes. This works for me :
new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, getClientCredential(),
Arrays.asList(SCOPES)).setCredentialStore(new OAuth2CredentialStore()).setAccessType("offline")
.setApprovalPrompt("force").build();