Error while creating google calendar event with a service account - java

I have a requirement of creating a google calendar event on a calendar and add other users as attendees to that event. The objective is to send calendar events to the application users without taking their consent ( O-Auth ).
After reading Google's documentation, I found out that I need a service account. So I created a project and a service account from one of the email addresses of our G-Suite, noreply#xxxxx.com and enabled calendar API for the same.
I created and downloaded a key pair ( JSON ) whose content is,
{
"type": "service_account",
"project_id": "*****",
"private_key_id": "bdbbcd**************49f77d599f2",
"private_key": "**"
"client_email": "******#*****.iam.gserviceaccount.com",
"client_id": "11083******576856",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/****dev%40*****kdev.iam.gserviceaccount.com"
}
And as per the documentation, I proceded to write authentication flow code,
public static GoogleCredential doOauth( String credsPath ) throws IOException
{
GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(credsPath))
.createScoped(Collections.singleton(CalendarScopes.CALENDAR));
System.out.println(credential);
return credential;
}
The credential object has most of the details from the key file. But, the fields, serviceAccountUser, accessToken, refreshToken, clientAuthentication and requestInitializer have null value. ( I am guessing something wrong here )
Now, using the credentialObject, I continued to write the code as per the documentation to create the event.
GoogleCredential credential = doOauth(CREDENTIALS_FILE_PATH);
Event event = new Event().setSummary("Google I/O 2015").setLocation("800 Howard St., San Francisco, CA 94103")
.setDescription("A chance to hear more about Google's developer products.");
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
DateTime startDateTime = new DateTime("2020-12-28T09:00:00-07:00");
EventDateTime start = new EventDateTime().setDateTime(startDateTime).setTimeZone("America/Los_Angeles");
event.setStart(start);
DateTime endDateTime = new DateTime("2020-12-28T17:00:00-07:00");
EventDateTime end = new EventDateTime().setDateTime(endDateTime).setTimeZone("America/Los_Angeles");
event.setEnd(end);
String[] recurrence = new String[] { "RRULE:FREQ=DAILY;COUNT=2" };
event.setRecurrence(Arrays.asList(recurrence));
EventAttendee[] attendees = new EventAttendee[] { new EventAttendee().setEmail("myemailaddress#gmail.com.com") };
event.setAttendees(Arrays.asList(attendees));
EventReminder[] reminderOverrides = new EventReminder[] {
new EventReminder().setMethod("email").setMinutes(24 * 60),
new EventReminder().setMethod("popup").setMinutes(10), };
Event.Reminders reminders = new Event.Reminders().setUseDefault(false)
.setOverrides(Arrays.asList(reminderOverrides));
event.setReminders(reminders);
String calendarId = "primary";
Calendar service = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName("testapp").build();
event = service.events().insert(calendarId, event).execute();
System.out.printf("Event created: %s\n", event.getHtmlLink());
But, this resulted in the error,
{
"code" : 403,
"errors" : [ {
"domain" : "calendar",
"message" : "Service accounts cannot invite attendees without Domain-Wide Delegation of Authority.",
"reason" : "forbiddenForServiceAccounts"
} ],
"message" : "Service accounts cannot invite attendees without Domain-Wide Delegation of Authority."
}
After spending time on Domain-Wide Delegation, I understood that this is needed if we have to send the event as other user from our g-suite, which is not needed for my problem. But, to debug, I went ahead and provided Domain-Wide Delegation and re ran the program. The same error came again.
So, I removed the invitees/attendees from the event object and re ran the application. This time the program ran without any error, but the event link generated, on click says, Could not find the requested event.
I do not see any examples of using the service account via java client libraries on the google developer link.
Can you please let me know what is going wrong here and the official/working documentation on how exactly to create a google calendar event from my project add other ( non g suite as well ) users to add as attendees, so that I do not have to get consent from other users to add events to their own calendar?
Thank You.

Fist off I want to say that this is something new, if you are seeing a lot of questions, and tutorials that did not state you needed to do this its because service accounts used to be able to send invites, this is something google locked down about a year ago.
This should work but i have not tested it as i no longer have access to a Gsuite account. You need to have the gsuite admin set up domain wide delegation to anther user. Then the service account needs to impersonate that user so it will appear as that user is the one sending the invites.
The only example I have for how that is added is in .net
.net example
var gsuiteUser = "se#clawskeyboard.com";
var serviceAccountCredentialInitializer = new ServiceAccountCredential.Initializer(serviceAccount)
{
User = gsuiteUser,
Scopes = new[] { GmailService.Scope.GmailSend, GmailService.Scope.GmailLabels }
}.FromCertificate(certificate);
Java example guess
I am not a java dev but the .net client library and the Java client library are very close in how they were developed. I would guess that you are looking for a method called setServiceAccountUser
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(CalendarScopes.CALENDAR)
.setServiceAccountPrivateKeyFromP12File(credsPath))
.setServiceAccountUser(gsuiteUser)
.build();
OAuth2 for Service Accounts

You should be able to impersonate the account user by setting the delegated variable to the user's email in the GoogleCredential object.
GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("credentials.json"))
.createScoped(<SCOPES>)
.createDelegated("user#example.com");

Related

not able to add meet link to google calendar events in java

I am working on a spring boot api which schedules events for me but and its working fine but along with events I want to add a meet link too and I am not able to do that.I am using a service account from cloud console and shared my personal account with this service account.How can I implement google meet?
Here's my code:
Event event = new Event()
.setSummary("Google I/O 2015")
.setLocation("800 Howard St., San Francisco, CA 94103")
.setDescription("A chance to hear more about Google's developer products.");
ConferenceSolutionKey conferenceSKey = new ConferenceSolutionKey();
conferenceSKey.setType("hangoutsMeet");
CreateConferenceRequest createConferenceReq = new CreateConferenceRequest();
createConferenceReq.setRequestId("adojajaod"); // ID generated by you
createConferenceReq.setConferenceSolutionKey(conferenceSKey);
ConferenceData conferenceData = new ConferenceData();
conferenceData.setCreateRequest(createConferenceReq);
System.out.println(conferenceData);
event.setConferenceData(conferenceData);
DateTime startDateTime = new DateTime("2021-08-14T09:00:00-07:00");
EventDateTime start = new EventDateTime()
.setDateTime(startDateTime)
.setTimeZone("America/Los_Angeles");
event.setStart(start);
DateTime endDateTime = new DateTime("2021-08-15T17:00:00-07:00");
EventDateTime end = new EventDateTime()
.setDateTime(endDateTime)
.setTimeZone("America/Los_Angeles");
event.setEnd(end);
String[] recurrence = new String[]{"RRULE:FREQ=DAILY;COUNT=2"};
event.setRecurrence(Arrays.asList(recurrence));
EventReminder[] reminderOverrides = new EventReminder[]{
new EventReminder().setMethod("email").setMinutes(24 * 60),
new EventReminder().setMethod("popup").setMinutes(10),
};
Event.Reminders reminders = new Event.Reminders()
.setUseDefault(false)
.setOverrides(Arrays.asList(reminderOverrides));
event.setReminders(reminders);
event = client.events().insert("himanshuranjan30#gmail.com", event).setConferenceDataVersion(1).execute();
System.out.printf("Event created: %s\n", event.getHtmlLink());
Here I am trying to setup a meet link using conference but its not working and giving a error like:
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Invalid conference type value.",
"reason" : "invalid"
} ],
"message" : "Invalid conference type value."
}
Any help will be appreciated.
conferenceSKey.setType("hangoutsMeet");
Looks like the calendar where you try to insert the event does not accept the "hangoutsMeet" conference call type.
To verify use get of Calendar API to view your target Calendar metadata.
https://developers.google.com/calendar/api/v3/reference/calendars
You can see the allowed types under,
"conferenceProperties": {
"allowedConferenceSolutionTypes": [
string
]
}

Obtain Facebook Events using restfb

I need to obtain all public events from my fb page.
It's not possible using an App access token anymore: "Requires a valid Page or User access token with basic permissions." - https://developers.facebook.com/docs/graph-api/reference/page/events/
I don't want to put my User credentials into my Application.
Current code:
FacebookClient.AccessToken accessToken = new DefaultFacebookClient(Version.LATEST).obtainAppAccessToken(facebookProperties.getAppId(), facebookProperties.getAppSecret());
FacebookClient facebookClient = new DefaultFacebookClient(accessToken.getAccessToken(), Version.LATEST);
Date now = new Date();
Connection<com.restfb.types.Event> eventConnection = facebookClient.fetchConnection(facebookProperties.getPageId() + "/events", com.restfb.types.Event.class, Parameter.with("fields", "name,start_time"));
List<Event> events = eventConnection.getData().stream().filter(facebookEvent -> now.compareTo(Optional.ofNullable(facebookEvent.getEndTime()).orElse(facebookEvent.getStartTime())) <= 0).map(facebookEvent -> Event.fromFacebookEvent(facebookEvent)).collect(Collectors.toCollection(LinkedList::new));
A dirty Workaround would be creating a new fb account, adding it as a Page admin and provide its Credentials. Is there a better way to simply obtain the public Events?

Subject must match Issuer claim in the client assertion

I want to integrate office365 service management API for collecting events from it.I want to use client credential way to use service to service call but i am getting following error,
{
"error":"invalid_client",
"error_description":"AADSTS50048: Subject must match Issuer claim in the client assertion.
\r\nTrace ID: 1ad7acd8-3945-4fe0-a313-07638eb76e42\r\nCorrelation ID: a6c3a3c9-b737-4bfc-894f-3086c3ce8dfa\r\nTimestamp: 2016-06-09 07:20:15Z",
"error_codes":[50048
],
"timestamp":"2016-06-09 07:20:15Z",
"trace_id":"1ad7acd8-3945-4fe0-a313-07638eb76e42",
"correlation_id":"a6c3a3c9-b737-4bfc-894f-3086c3ce8dfa"
}
i use following doc to integration,
For getting client assersion,
https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx I am getting this. But for Access token,
https://msdn.microsoft.com/en-us/library/office/dn707383.aspx I not getting this as a response getting above error.
Somebody help me please :)
How did you get the client_assertion? The link you provide doesn’t describe how to get the ‘client_assertion’. It acquire the token with the app’s id and secret which is doesn’t support for the Office 365 Management API. You can refer the blog to about the ‘client_assertion’.
And here is an C# code sample which use the ADAL to get the access token for the client credentials flow:
string clientId = "{clientId}";
string certThumbprint = "‎{copy from mmc}";
certThumbprint = certThumbprint.Replace("\u200e", string.Empty).Replace("\u200f", string.Empty).Replace(" ", string.Empty);
string apiResourceId = "https://manage.office.com";
X509Certificate2 cert = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
string authority = "https://login.windows.net/{yourTentant}";
var authContext = new AuthenticationContext(authority);
try
{
store.Open(OpenFlags.ReadOnly);
cert = store.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, false)[0];
}
finally
{
store.Close();
}
var certCred = new ClientAssertionCertificate(clientId, cert);
AuthenticationResult result = null;
try
{
result = await authContext.AcquireTokenAsync(apiResourceId, certCred);
}
catch (Exception ex)
{
}

Facebook4j access token error 2500

I would like get data from facebook with facebook4j library, but I received this error
An active access token must be used to query information about the
current user. code - 2500
The code:
ConfigurationBuilder confBuilder = new ConfigurationBuilder();
confBuilder.setDebugEnabled(true);
confBuilder.setOAuthAppId("MY ID APP");
confBuilder.setOAuthAppSecret("MY APP SECRET");
confBuilder.setUseSSL(true);
confBuilder.setJSONStoreEnabled(true);
Configuration configuration = confBuilder.build();
FacebookFactory facebookFactory = new FacebookFactory(configuration );
Facebook facebookClient = facebookFactory.getInstance();
AccessToken accessToken = null;
OAuthSupport oAuthSupport = new OAuthAuthorization(configuration );
accessToken = oAuthSupport.getOAuthAppAccessToken();
facebookClient.setOAuthAccessToken(accessToken);
ResponseList<Post> feeds = facebookClient.getHome(new Reading().limit(1000));
You go to the following link: https://developers.facebook.com/tools/explorer/
There you choose your app, which you created and want to access from.
.setOAuthAccessToken("The User Access Token")
accessToken = oAuthSupport.getOAuthAccessToken(); // NOT AppAccessToken!!!
Then it should work

Java Google Calendar api "access_denied" on Service Account

I'm trying to connect to a calendar using the Java Google Calendar api. The java application uses a service account.
I've the following code:
java.io.File licenseFile = new java.io.File("39790cb51b361f51cab6940d165c6cda4dc60177-privatekey.p12");
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("xxx#developer.gserviceaccount.com")
.setServiceAccountUser(EMAIL_ADRESS)
.setServiceAccountScopes(CalendarScopes.CALENDAR)
.setServiceAccountPrivateKeyFromP12File(licenseFile)
.build();
client = new com.google.api.services.calendar.Calendar.Builder(
HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName("Google Calendar Sync").build();
Calendar calendar = client.calendars().get(EMAIL_ADRESS).execute();
On the last line I get an IOException with the message:
ex = (com.google.api.client.auth.oauth2.TokenResponseException)
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad
Request { "error" : "access_denied" }
I dubble checked the values for the GoogleCredential object and they are correct.
I've also added https://www.google.com/calendar/feeds/, http://www.google.com/calendar/feeds/ in my domain console with the application id as client to authorize third party application access
Am I forgetting a step?
The api isn't finished yet. More specifically the service account part.
The calendar owner needs to give permission to the application to read/write the calendar in it's calendar settings. It's found in the sharing settings of the calendar, there you can add e-mail adresses of accounts and give them permission on your calendar.
So in this case I had to add: xxx#developer.gserviceaccount.com to the permission/share list of the calendars the application needed access to.
I also deleted another post that didn't full work because of the issue above. I'll undelete it since it contains some code fragments that may help other people in the future. But beaware of the permission issues and service accounts not supporting Google Calendar
I do:
List find = client.events().list(EMAIL_ADRESS);
DateTime timeMin = new DateTime(DateUtil.stringToDate("01/01/2013"));
DateTime timeMax = new DateTime(DateUtil.stringToDate("01/02/2013"));
find.setTimeMin(timeMin);
find.setTimeMax(timeMax);
try{
Events events = find.execute();
int i =0;
while (true) {
System.out.println("Page: "+(++i)+": "+events.getItems().size());
for (Event event : events.getItems()) {
System.out.println(event.getSummary());
}
String pageToken = events.getNextPageToken();
if (pageToken != null && !pageToken.isEmpty()) {
events = client.events().list(EMAIL_ADRESS).setPageToken(pageToken).execute();
} else {
break;
}
}
}catch(GoogleJsonResponseException e){
System.out.println(e.getMessage());
// e.printStackTrace();
}
I got it to work
I don't know why but I deleted the line
.setServiceAccountUser(EMAIL_ADRESS)
Also I added an extra url in the domain scope:
https://apps-apis.google.com/a/feeds/calendar/resource/#readonly
and deleted a link that wasn't working.
Finally I changed the applicationName in my client declaration to the same name as in the api console
client = new com.google.api.services.calendar.Calendar.Builder(
HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName("HolidaySyncs").build();
After these steps it started to work.
Also note for future reference after I did this I had the following error:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "notFound",
"message": "Not Found"
}
],
"code": 404,
"message": "Not Found"
}
}
I solved this by changing for example:
Event result = client.events().insert(<EMAIL ADRESS>, event).execute();
to
Event result = client.events().insert("primary", event).execute();
First I tought there was something wrong with the google servers, but apparently it goes wrong when you try to link to a calendar ID. So linking to "primary" which is the primary calendar of an account works. But according to the documentation it should also work when you refer to a specific calendar ID, where the email address is the primary calendar. Probably a bug?
UPDATE: after these code correction I still had issues. Read the accepted answer for more information.

Categories