I have been stuck on this for hours now.
I have the following code:
string[] scopes = new string[] {
"User.ReadWrite"
};
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(OAuthSettings.ApplicationId)
.Build();
InteractiveAuthenticationProvider authProvider = new InteractiveAuthenticationProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = new User
{
Country = "ZA"
};
await graphClient.Me
.Request()
.UpdateAsync(user);
Yet every time I execute this I get the following error:
Code: generalException
Message: An error occurred sending the request
What am I doing wrong?
Updating user info is very limited unfortunately. Updating the country with delegated auth isn't possible. It should be doable with app-only auth and the User.ReadWrite.All permission. This is (admittedly) not well explained.
From the information here:
User.ReadWrite and User.Readwrite.All delegated permissions allow the app to update the following profile properties for work or school accounts:
aboutMe
birthday
hireDate
interests
mobilePhone
mySite
pastProjects
photo
preferredName
responsibilities
schools
skills
There are some other gotchas as well (such as if the user is an admin, etc.).
Related
I have created a VisualStudio (C#) project, wihch use google classroom apis.
With Super admin, It can create a course, but with delegated admin, It returns an error.
I have use a delegated admin. Why does that error return ?
Errors [
Message[#UserCannotCreateCourse Current user cannot create courses.] Location[ - ] Reason[forbidden] Domain[global]
]
my code is below given
string[] Scopes = {ClassroomService.Scope.ClassroomCourses};
UserCredential credential;
using (var stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
var service = new ClassroomService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Classroom test"
});
var body = new Course()
{
Name = "MyFirstClass",
OwnerId = "me"
};
var course = service.Courses.Create(body).Execute();
"credentials.json"File is from My Google Cloud Platform project.
My project is verificated.
./auth/classroom/courses
./auth/classroom/rosters
(etc.)
Delegated admin is created by Google Console.
I added custom admin role.
I found that the owner of the classroom course must a member of classroom_teachers Group. (If the account is not the super admin)
With an account that has not be a teacher, classroom api gets error.
And after becoming a teacher (manually sign-in, and create a classroom course), classroom api succeeded.
So, before creating a course, I join that account into classroom_teacher group.
Thank you.
I'm trying to find a contact by his email using Google People API.
We are using the Create Contact method.
I'm run this method with all the relevant scopes and using oAuth2.0 with refresh token.
HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
JacksonFactory jsonFactory = JacksonFactory.getDefaultInstance();
GoogleCredential googleCredential = new GoogleCredential.Builder()
.setJsonFactory(jsonFactory)
.setTransport(transport)
.setClientSecrets(googlePeopleKey.getClientId(), googlePeopleKey.getClientSecret())
.build().setRefreshToken(googlePeopleKey.getRefreshToken());
this.peopleService = new PeopleService.Builder(transport, jsonFactory, googleCredential)
.setApplicationName(APPLICATION_NAME)
.build();
When we use the Create Contact method - Google search for the given email in the system (google plus and more tools) and enrich the contact with an existing data about the user such as photos, gender, profiles and a lot more.
public Person createContact(String email) {
Person requiredPerson = new Person();
EmailAddress emailAddress = new EmailAddress();
emailAddress.set("value", email);
requiredPerson.setEmailAddresses(Arrays.asList(emailAddress));
try {
PeopleService.People.CreateContact createContact = peopleService.people().createContact(requiredPerson);
return createContact.execute();
} catch (IOException e) {
log.error("Could not perform Google people search for email {}", email, e);
}
return null;
}
But, I struggled to get all the information about the users.
We have limitations of 90 calls per second but our system not near this limit.
With two different users, it seems like after 40-50 calls the API start to return empty contact (without photos, gender and more..)
Although the next day it found all the data about a specific person that the day before it returns me fewer data.
I know we have limitations of 25,000 contacts per day but we are not near this limit too.
I can really use your help to find why after 40/50 new contact, google people API start to create the contact without retrieving all the possible data in google systems.
Thank you!
I'm trying to pull google contacts information (photos, gender and more) and we used Google People API to do that.
I use in createContact service to search contact by an Email.
I created a client id and client secret, and with the simple code of oAuth2.0, I got a refresh token that my server used to generate credentials.
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
String clientId = "";
String clientSecret = "";
String scope = "https://www.googleapis.com/auth/contacts";
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, jsonFactory, clientId, clientSecret,
Arrays.asList(scope))
.setAccessType("offline")
.setApprovalPrompt("force")
.build();
LocalServerReceiver localReceiver = new LocalServerReceiver.Builder().setPort(8089).build();
Credential credential = new AuthorizationCodeInstalledApp(flow, localReceiver).authorize("user");
In that way, I get the refreshToken and save it offline in a secured file.
After that, my application connect to google and get the clientId, ClientToken, and RefreshToken from that file and try to connect -
GoogleCredential googleCredential = new GoogleCredential.Builder()
.setJsonFactory(jsonFactory)
.setTransport(transport)
.setClientSecrets(googlePeopleKey.getClientId(), googlePeopleKey.getClientSecret())
.build().setRefreshToken(googlePeopleKey.getRefreshToken());
It seems that the result is different per user. When I operate with a refresh code that my user authenticate earlier, I can't get all the information that another user in my company (that authorized by the same way) does. Today, it flipped, and in another test, I get results that he didn't. Very strange.
I was looking up about limit rate but, it seems that I even not near the limitations.
Do you have any idea why it returns different result for different users? The different result means that sometimes one of the users can see the profile picture but the other one can't.
Thank you!
I'm working on a web app in Google Apps Script, and I'm having some trouble understanding how the authorization is handled. When accessing the web app as the user using the app, it prompts for authorization, and everything appears okay. However, I'm call userProfiles.get and looking for student email addresses, and it returns the profile without the email.
function classRosters() {
var teacher = Classroom.UserProfiles.get(Session.getActiveUser().getEmail());
var classList = Classroom.Courses.list({teacherId: teacher.id}).courses;
var classes = [];
for (i in classList) {
if (classList[i].courseState != 'ACTIVE') {
continue;
}
var class = classList[i];
var classId = classList[i].id;
var className = classList[i].name;
classes.push([className]);
var teacherId = Classroom.Courses.Teachers.get(classId, classList[i].ownerId).userId;
var teacherEmail = Classroom.UserProfiles.get(teacherId);
var title = Classroom.Courses.get(classId).name;
var students = Classroom.Courses.Students.list(classId).students;
var studentArray = [];
if (students) {
for (j in students) {
var currStudent = students[j];
var email = Classroom.UserProfiles.get(currStudent.userId).emailAddress;
var email = Classroom.Courses.Students.get(classId, currStudent.userId).profile.emailAddress;
studentArray.push(email);
Logger.log(email);
}
}
for (j in classes) {
if (className.indexOf(classes[j]) > -1) {
var classIndex = +j;
classes[classIndex].push(studentArray);
}
}
}
return classes;
}
I've played with the API explorer, and it shows that classroom.profile.email is required, but that's not included in the scopes. When I use the API explorer, I can authorize, and it works, and my web app will work as well until the authorization from the explorer expires.
Is there any method to prompt for authorization in the GAS library for the Classroom advanced service? I can't find anything much that's specific to GAS and not part of the overall API.
Thanks,
James
Unfortunately Apps Script doesn't allow you to request additional scopes for your advanced services. The email and photos scopes aren't required to execute the method, but are required to return email and photo data in the response. You can follow issue 3070 for progress on this problem.
Update 2015-08-17:
We just implemented a workaround, which is that the Classroom advanced service now always prompts for the following fixed set of scopes:
https://www.googleapis.com/auth/classroom.courses
https://www.googleapis.com/auth/classroom.rosters
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
This provides access to emails, but does mean that the scopes requested for a given script may be more than it actually needs. We hope this unblocks admins that are trying to use Apps Script to manage their Classroom data, while we work on a longer-term solution for dealing with optional scopes in Apps Script.
EDIT: The original poster asked this for C#, but the same problem occurs regardless of the library used, and its solution is language independent.
Using C# lib,
string service_account = "myaccount#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"pathtomy-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(service_account)
{
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "My project name",
});
var calendarList = service.CalendarList.List().Execute();
IList<CalendarListEntry> items = calendarList.Items;
items is empty. On https://developers.google.com/google-apps/calendar/v3/reference/calendarList/list, when I try the api, I get the good result.
I really don't understand why I don't have any result : seems like if the service_account do not have the same calendar as my gmail account linked to.
Any suggestion ?
Thanks.
The solution is to share the existing calendar with the service account
<paste-your-account-here>#developer.gserviceaccount.com
(You can find the account in question under the credentials tab in Google Developers Console, it's called 'EMAIL ADDRESS')