Microsoft office 365 add-in + Azure Auth token graph - javascript

I have a problem getting token for Microsoft Graph.
I have followed this documentation to get the token.
With the Office Javascript API, I get from my add-in the identity token and the application token.
I have put my add-in on the exchange server, I have checked that an application was created in Azure Active Directory, add all authorizations for Microsoft Graph and Azure Active Directory for this application and generate a key for API access.
When I'm in Outlook, I open my add-in and I get the 2 token. In this step, i think the first step is done.
function getCallbackToken() {
Office.context.mailbox.getCallbackTokenAsync(cbToken);
}
function cbToken(asyncResult) {
var token = asyncResult.value;
console.log("token : " + token);
}
function getIdentityToken() {
Office.context.mailbox.getUserIdentityTokenAsync(cbIdentity);
}
function cbIdentity(asyncResult) {
var identity = asyncResult.value;
console.log("identity : " + identity);
}
function getMailUser() {
console.log(
"displayName : " +
Office.context.mailbox.userProfile.displayName +
", mail adresse : " +
Office.context.mailbox.userProfile.emailAddress
);
}
when I send theses token to my java server, I would like to get the token to Microsoft Graph, I request azure with ADAL4J library with this code
//idToken , token identity or token application get from addin api javascript
public AuthenticationResult acquireTokenForGraphApi(String idToken)
throws Throwable {
final ClientCredential credential = new ClientCredential(" --- application id get in azure application list --- ",
" --- generate key from azure application setting, only display one time ---");
final UserAssertion assertion = new UserAssertion(idToken);
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
String tenantId = "--- tocken get in azure configuration panel, application endpoints";
final AuthenticationContext context = new AuthenticationContext(
"https://login.microsoftonline.com/" + tenantId + "/", false, service);
final Future<AuthenticationResult> future = context.acquireToken("https://graph.windows.net/", assertion, credential, null);
result = future.get();
} catch (ExecutionException e) {
throw e.getCause();
} finally {
if (service != null) {
service.shutdown();
}
}
if (result == null) {
throw new ServiceUnavailableException(
"unable to acquire on-behalf-of token for client " + aadAuthFilterProp.getClientId());
}
return result;
i get an error code
com.microsoft.aad.adal4j.AuthenticationException: {
"error_description": "AADSTS50013: Assertion contains an invalid signature."
[
Reason - The key was not found.,
Thumbprint of key used by client: '0600F9F674620737E73404E287C45A818CB7CEB8',
Configured keys:
[
Key0:Start=02/18/2018, End=02/19/2020, Thumbprint=oZkMJ7Omv9GN7JVM;
Key1:Start=03/31/2018, End=03/31/2020, Thumbprint=xq4mEGikJ5Bkblfw;
Key2:Start=11/16/2016, End=11/16/2018, Thumbprint=i1DVz66b9dfpPV3Z;
]
]
Trace ID: b439ed2f-8a91-401e-91e8-133b57532b00
Correlation ID: cd8ebc72-5173-4725-9c79-e8dc0ef7634b
Timestamp: 2018-04-10 08:27:05Z,
"error": "invalid_grant"
}

Related

MemberOf in Graph Me api azure AD

I am trying to get the member groups of the user to whom user belongs using azure graph api but it is not returning memberof in the api. I am using auth0 for the authentication.
Here is the java script code which I am using.
function(accessToken, ctx, cb) {
const jwt = require('jsonwebtoken#7.1.9');
console.log('azure - retrieve user profile');
// Retrieve the profile from Azure
request.get(
'https://graph.microsoft.com/v1.0/me?$select=id,mail,givenName,surname,userPrincipalName,otherMails,department,memberOf', {
headers: {
'Authorization': 'Bearer ' + accessToken,
},
json: true
},
function(e, r, profile) {
if (e) {
console.log('azure - error while retrieving user profile:');
console.log(e);
return cb(e)
}
if (r.statusCode !== 200) {
console.log('azure - error while retrieving user profile: ' + r.statusCode);
return cb(new Error('StatusCode: ' + r.statusCode));
}
console.log('azure - retrieved user profile.');
// Get the tenant id from the access token
let decodedToken = jwt.decode(accessToken);
let auth0Profile = {
user_id: profile.id,
given_name: profile.givenName,
family_name: profile.surname,
email: profile.mail || profile.otherMails[0] || profile.userPrincipalName,
email_verified: true,
name: profile.givenName + ' ' + profile.surname,
tenant_id: decodedToken.tid,
identification_value: decodedToken.tid,
user_principal_name: profile.userPrincipalName,
user_department: profile.department,
user_member: profile.memberOf
};
cb(null, auth0Profile);
}
);
}
I have added scope (User.Read Directory.Read.All) in Auth0 for the api call.
Can some one let me know why I am not getting memberOf?
If you want to get member groups of the user, along with multiple attributes, the query will not return the expected results.
I tried checking the same query in Microsoft Graph Explorer.
'https://graph.microsoft.com/v1.0/me?$select=id,mail,givenName,surname,userPrincipalName,otherMails,department,memberOf'
Even
for that, except memberOf, all objects displayed:
For getting memberOf, you have to query separately like below:
https://graph.microsoft.com/v1.0/me/memberOf
So, for the workaround, you can make use of the above query by giving it separately without querying with other attributes.
Also please make sure to add GroupMember.Read.All permissions in the scope as mentioned in this Microsoft Doc.
Please find below links if they are helpful: Ref1, Ref2

Google Sheets API throwing 401 error while append rows to spreadsheet without login

Objective: Submit a form and store data to google spreadsheet
documentation: link
What I've done so far:
var CLIENT_ID = 'my_client_id.apps.googleusercontent.com';
var API_KEY = 'MY_API_KEY';
var DISCOVERY_DOCS = ["https://sheets.googleapis.com/$discovery/rest?version=v4"];
var SCOPES = "https://www.googleapis.com/auth/spreadsheets";
var authorizeButton = document.getElementById('authorize_button');
var signoutButton = document.getElementById('signout_button');
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
function initClient() {
gapi.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
})
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
//authorizeButton.style.display = 'none';
//signoutButton.style.display = 'block';
//listMajors();
} else {
//authorizeButton.style.display = 'block';
//signoutButton.style.display = 'none';
}
}
function appendPre(message) {
var pre = document.getElementById('content');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
function update_docs(data) {
var params = {
spreadsheetId: '1YXMlr_-I45AWM2b9QnLkuLQoI6dq6wEuVOcttOMv9hU',
range: 'A:I', // TODO: Update placeholder value.
valueInputOption: 'RAW',
insertDataOption: 'INSERT_ROWS',
};
var valueRangeBody = {
"range": 'A:I', // 9 cols
"majorDimension": 'ROWS',
"values": [
[
data[0].value,//nom,
data[1].value,//prenom,
data[2].value,//email,
data[3].value,//user_phone,
data[4].value,//company_name,
data[5].value,//user_type,
data[6].value,//account_name,
data[7].value,//password,
data[8].value,//comptes_sources,
]
]
};
var request = gapi.client.sheets.spreadsheets.values.append(params, valueRangeBody);
request.then(function(response) {
console.log(response.result);
}, function(reason) {
console.error('error: ' + reason.result.error.message);
});
}
I can successfully append rows to the spreadsheet if I'm logged in to my google account.
Question: Can I append row without logging in(if yes please provide some docs/code)?
Because if I submit the form from a private window it throws 401 error.
error message: error: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential.
I think before you start working on this you need to understand a few things.
There is a difference between private and public data.
Public data, Searching publicly uploaded youtube videos
Private data, My person gmail account, drive account, calendar account.
Even setting the sheet to public will not help you as with it public using an api key you will only be allowed to read the sheet not update it.
Answer: No you can not append a row without the application being authenticated and having access to the data.
Assuming that this is a sheet that you personally own you could set up a service account authenticate and grant the service account access to the sheet it will then be able to make the changes for you without you having to login. However this depends upon how your application works and what language you are using. I dont think that javascript supports service account authentication.

Parse-server social login

I am developing application based on Parse-server and I want to offer social login. I found this guide in the documentation http://docs.parseplatform.org/js/guide/#linking-users.
I started to implement the social login by google. I did following steps:
1) I added following lines to the ParseServer settings
var api = new ParseServer({
...
auth:{
google: {}
},
...
});
2) I did the authentication by hello.js on the client side (call user._linkWith function on login)
hello.init({
google: 'My Google id'
});
hello.on('auth.login', function(auth) {
// Call user information, for the given network
hello(auth.network).api('me').then(function(r) {
const user = new Parse.User();
user._linkWith(auth.network, auth.authResponse).then(function(user){
console.log('You are logged in successfully.');
});
});
});
When I debugged it, I found that it fails in _linkWith() function, when provider object is preparing. Object AuthProviders, which should store all providers, is empty. Because of it the statement provider = authProviders['google']; leads to undefined. Invoking provider.authenticate(...); leads to error "Cannot read property 'authenticate' of undefined"
What am I missing or what am I doing wrong?
Thanks for all your answers.
Honza
Did you register the authenticationProvider? You can find examples in our unit tests on how to do so:
https://github.com/parse-community/parse-server/blob/5813fd0bf8350a97d529e5e608e7620b2b65fd0c/spec/AuthenticationAdapters.spec.js#L139
I also got this error and looked at the _linkWith(provider, options) source code. It checks if options has an authData field (which in turn should contain id and credentials). If so, it uses options.authData. Otherwise it falls back on looking up a previously registered authentication provider mentioned in the previous answer.
This is a fragment of the code I'm using:
const authData = {
"id": profile.getId(),
"id_token": id_token
}
const options = {
"authData": authData
}
const user = new Parse.User();
user._linkWith('google', options).then(function(user) {
console.log('Successful user._linkWith(). returned user=' + JSON.stringify(user))
}, function(error) {
console.log('Error linking/creating user: ' + error)
alert('Error linking/creating user: ' + error)
// TODO handle error
})

Vertx JS Eventbus connection being created then closing

Background: using Vertx 3.3.3 Core and Web on Java side as a server, using vertx-web-3.3.3-client.js as the client with sockjsclient1.1.2.js
Issue: I am successfully making a connection to the eventbus from the client when on my local machine or LAN. When I go through a proxy, the wss eventbus connection is being blocked (in Firefox I see "Firefox can't establish a connection to "wss://..."; in Chromium I see "WebSocket connection to wss://... failed: Error during WebSocket handshake: Unexpected response code: 400", then I see "https ://.../eventbus/.../xhr_send?t=... Failed to load resource: the server responded with a status code of 500"). However, the onopen fires and I receive some data (connection downgraded to an accepted protocol?). Immediately after this, onclose fires and I have lost connection. I know that I am successfully reaching the Java vertx server because my static web and API calls are working.
Questions: I have looked through the Vertx and SockJS documentation extensively. Is there:
Documentation on how vertx tries different transport protocols in the JS Eventbus connection?
An example of JS Vertx Eventbus working through a business proxy?
Another way to implement the Eventbus connection, perhaps specifying the SockJS protocol(s) to try/use? (I am trying the simplest way to create an eventbus connection, as shown in many places in documentation)
Something I need to do on the Java side of the SockJS/Eventbus setup?
Thanks in advance for any advice/help!
EDIT 1: Adding the following code for both Java server and JavaScript web client sides. The web side is very basic (and what is failing). The Java side is using Spring for dependency injection and application config, has an Eventbus connection, one API call, and serves static web content.
The API call from client to server works, and the server sources the web contents correctly, so accessing the tool is working. However, the proxy is causing wss to fail (as expected) but the downgrade to xhr-streaming is failing (I think)
Javascript:
var EB;
var URL;
var APICall = "api/eventbus/publish/";
var IncomingAddress = "heartbeat-test";
var OutgoingAddress = "client-test";
function createConnection(){
URL = $("#serveraddressinput").val(); //Getting url from html text box
console.log("Creating Eventbus connection at " + URL + "eventbus"); //Eventbus address is '<link>/eventbus'
EB = new EventBus(URL + "eventbus");
testAPICall();
EB.onopen = function(){
console.log("Eventbus connection successfully made at " + URL + "eventbus");
console.log("Registering Eventbus handler for messages at " + IncomingAddress);
EB.registerHandler(IncomingAddress, function(error, message){
console.log("Received Eventbus message " + JSON.stringify(message));
};
EB.onclose = function(){
console.log("Eventbus connection at " + URL + " has been lost");
URL = "";
};
}
function testAPICall(){
var link = URL + APICall + "heartbeat-test";
console.log("Testing API call to " + link);
$.ajax({
url: link,
type: 'POST',
data: JSON.stringify({"testFromClient": "Test message sent from Client via API Call"}),
dataType: 'json',
success: function (data, textStatus) {
console.log("API Call Success: " + JSON.stringify(data));
},
error: function (request, error) {
console.log("API Call ERROR: " + JSON.stringify(request) + " " + error);
}
});
}
function sendTestMessage(){
console.log("Sending test message to address " + OutgoingAddress);
EB.send(OutgoingAddress, "Testing 1, 2, 3...");
}
Java:
...
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.web.handler.sockjs.BridgeEvent;
import io.vertx.ext.web.handler.sockjs.BridgeEventType;
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import org.apache.logging.log4j.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
#Service
public class MyTestVerticle extends AbstractVerticle {
private final static Logger log = LoggerFactory.getLogger(MyTestVerticle.class);
final Level ACCESS = Level.forName("ACCESS", 450);
private boolean started;
private int port;
#Value("${webserver.testpath.enabled}")
private boolean testPathEnabled;
#Value("${webserver.urlpath.test}")
private String testUrlPath;
#Value("${webserver.filepath.test}")
private String testFilePath;
#Value("${webserver.caching.enabled}")
private boolean cachingEnabled;
#Value("${webserver.ssl.enabled}")
private boolean sslEnabled;
private BridgeOptions bridgeOptions;
private SockJSHandler sockJsHandler;
private Router router;
private JksOptions sslKeyStoreOptions;
private JksOptions sslTrustStoreOptions;
public MyTestVerticle() {
this.started = false;
}
#Override
public void start(Future<Void> fut) throws Exception {
log.info("start() -- starting Vertx Verticle with eventbus, API handler, and static file handler");
// grab the router
router = getRouter();
// enable CORS for the router
CorsHandler corsHandler = CorsHandler.create("*"); //Wildcard(*) not allowed if allowCredentials is true
corsHandler.allowedMethod(HttpMethod.OPTIONS);
corsHandler.allowedMethod(HttpMethod.GET);
corsHandler.allowedMethod(HttpMethod.POST);
corsHandler.allowedMethod(HttpMethod.PUT);
corsHandler.allowedMethod(HttpMethod.DELETE);
corsHandler.allowCredentials(false);
corsHandler.allowedHeader("Access-Control-Request-Method");
corsHandler.allowedHeader("Access-Control-Allow-Method");
corsHandler.allowedHeader("Access-Control-Allow-Credentials");
corsHandler.allowedHeader("Access-Control-Allow-Origin");
corsHandler.allowedHeader("Access-Control-Allow-Headers");
corsHandler.allowedHeader("Content-Type");
// enable handling of body
router.route().handler(BodyHandler.create());
router.route().handler(corsHandler);
router.route().handler(this::handleAccessLogging);
// publish a payload to provided eventbus destination
router.post("/api/eventbus/publish/:destination").handler(this::publish);
// open up all for outbound and inbound traffic
bridgeOptions = new BridgeOptions();
bridgeOptions.addOutboundPermitted(new PermittedOptions().setAddressRegex(".*"));
bridgeOptions.addInboundPermitted(new PermittedOptions().setAddressRegex(".*"));
// sockJsHandler = SockJSHandler.create(vertx).bridge(bridgeOptions);
sockJsHandler = SockJSHandler.create(vertx);
sockJsHandler.bridge(bridgeOptions, be -> {
try {
if (be.type() == BridgeEventType.SOCKET_CREATED) {
handleSocketOpenEvent(be);
}
else if(be.type() ==BridgeEventType.REGISTER) {
handleRegisterEvent(be);
}
else if(be.type() ==BridgeEventType.UNREGISTER) {
handleUnregisterEvent(be);
}
else if(be.type() ==BridgeEventType.SOCKET_CLOSED) {
handleSocketCloseEvent(be);
}
} catch (Exception e) {
} finally {
be.complete(true);
}
});
router.route("/eventbus/*").handler(sockJsHandler);
if(testPathEnabled){
router.route("/" + testUrlPath + "/*").handler(StaticHandler.create(testFilePath).setCachingEnabled(cachingEnabled));
}
// create periodic task, pushing all current EventBusRegistrations
vertx.setPeriodic(1000, handler -> {
JsonObject obj =new JsonObject();
obj.put("testMessage", "Periodic test message from server...");
vertx.eventBus().publish("heartbeat-test", Json.encodePrettily(obj));
});
EventBus eb = vertx.eventBus();
eb.consumer("client-test", message -> {
log.info("Received message from client: " + Json.encodePrettily(message.body()) + " at " + System.currentTimeMillis());
});
HttpServerOptions httpOptions = new HttpServerOptions();
if(sslEnabled){
httpOptions.setSsl(true);
httpOptions.setKeyStoreOptions(sslKeyStoreOptions);
}
log.info("starting web server on port: " + port);
vertx
.createHttpServer(httpOptions)
.requestHandler(router::accept).listen(
port,
result -> {
if (result.succeeded()) {
setStarted(true);
log.info("Server started and ready to accept requests");
fut.complete();
} else {
setStarted(false);
fut.fail(result.cause());
}
}
);
}
private void handleSocketOpenEvent(BridgeEvent be){
String host =be.socket().remoteAddress().toString();
String localAddress = be.socket().localAddress().toString();
log.info("Socket connection opened! Host: " + host + " Local address: " + localAddress);
}
private void handleRegisterEvent(BridgeEvent be){
String host =be.socket().remoteAddress().toString();
String localAddress = be.socket().localAddress().toString();
String address = be.getRawMessage().getString("address").trim();
log.info("Eventbus register event! Address: " + address + " Host: " + host + " Local address: " + localAddress);
}
private void handleUnregisterEvent(BridgeEvent be){
String host =be.socket().remoteAddress().toString();
String localAddress = be.socket().localAddress().toString();
String address = be.getRawMessage().getString("address").trim();
log.info("Eventbus unregister event! Address: " + address + " Host: " + host + " Local address: " + localAddress);
}
private void handleSocketCloseEvent(BridgeEvent be){
String host =be.socket().remoteAddress().toString();
String localAddress = be.socket().localAddress().toString();
log.info("Socket connection closed! Host: " + host + " Local address: " + localAddress);
}
//Method handles logging at custom level for access logging to files
private void handleAccessLogging(RoutingContext routingContext){
Marker accessMarker = MarkerFactory.getMarker("ACCESS");
if(routingContext.normalisedPath().contains("/api")){
log.info(accessMarker, "Api access log: request= " + routingContext.normalisedPath() + " source=" + routingContext.request().remoteAddress());
}
else{
log.info(accessMarker, "Web access log: path= " + routingContext.normalisedPath() + " source= " + routingContext.request().remoteAddress());
}
routingContext.next();
}
/**
* Accept a payload (anything) and publish to the provided destination
*
* #param routingContext
*/
private void publish(RoutingContext routingContext) {
String destination = routingContext.request().getParam("destination");
String payload = routingContext.getBodyAsString();
if ((destination == null) || (payload == null)) {
Exception e = new Exception("Missing arguments");
routingContext.response().setStatusCode(406);
routingContext.fail(e);
} else {
log.info("API Call -> Publishing to destination: " + destination + " payload: " + payload);
vertx.eventBus().publish(destination, payload);
routingContext
.response()
.setStatusCode(200)
.putHeader("content-type", "application/json; charset=utf-8")
.end(payload);
}
}
public boolean isStarted() {
return started;
}
public void setStarted(boolean started) {
this.started = started;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public Router getRouter(){
if(router == null){
router = Router.router(vertx);
}
return router;
}
public void setRouter(Router router){
this.router = router;
}
public void setSslOptions(JksOptions keyStoreOptions, JksOptions trustStoreOptions) {
this.sslKeyStoreOptions = keyStoreOptions;
this.sslTrustStoreOptions = trustStoreOptions;
}
}
This error can be resolved by doing the following:
In the Java verticle, move the Eventbus handler to the top, before any other handlers. I believe the BodyHandler or CorsHandler were messing it up and causing the 500 error.
...
router.route("/eventbus/*").handler(sockJsHandler);
...
// enable handling of body
router.route().handler(BodyHandler.create());
router.route().handler(corsHandler);
router.route().handler(this::handleAccessLogging);
// publish a payload to provided eventbus destination
router.post("/api/eventbus/publish/:destination").handler(this::publish);

How to access gmail API?

I generate my JWT, if my token is correct why dont work ? in Google Developers Console i enabled gmail plus youtube and other API, in credentials generate and download json
{
"private_key_id": "22dcf",
"private_key": "-----BEGIN PRIVATE KEY-----(remove)-----END PRIVATE KEY-----\n",
"client_email": "vgfjjc6#developer.gserviceaccount.com",
"client_id": "jc6.apps.googleusercontent.com",
"type": "service_account"
}
first generate token
var sHead=JSON.stringify({"alg":"RS256","typ":"JWT"});
var iat=timeStampf();
var exp=iat+3600;
var sPayload=JSON.stringify({
"iss":client_email,
"scope":scope,//gmail scope https://mail.google.com/
"aud":"https://www.googleapis.com/oauth2/v3/token",
"exp":exp,
"iat":iat
});
var sJWS = KJUR.jws.JWS.sign("RS256", sHead,sPayload, private_key);
var paramstoken="grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-ty
pe%3Ajwt-bearer&assertion="+sJWS
getToken("POST","/oauth2/v3/token",paramstoken,jsonData,replier);
/*rest petition return 200 OK
{
"access_token" : "1bHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
"token_type" : "Bearer",
"expires_in" : 3600
}*/
next i test my token
function testToken(accessToken,replier)
{
// /gmail/v1/users/me/messages /plus/v1/people/me
var client = vertx.createHttpClient().host(urlbase).port(443).ssl(true).maxPoolSize(10);
var request = client.request("GET", "/gmail/v1/users/me/messages", function(resp) {
console.log('server returned status code: ' + resp.statusCode());
console.log('server returned status message: ' + resp.statusMessage());
resp.bodyHandler(function(body) {
replier(JSON.parse(body.toString()));
});
});
request.headers()
.set("Content-type", contentType)
.set("Authorization", "Bearer "+accessToken);
request.end();
client.close();
}
if i use google+ scope and this petition the answer is 200 ok
https://www.googleapis.com/auth/plus.me /plus/v1/people/me
{
"kind":"plus#person",
"etag":"\"LR9iFZQGXELLHS07eQ\"",
"objectType":"person","id":"1149981343","displayName":"","name":{"familyName":"","givenName":""},"image":{"url":"https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg?sz=50","isDefault":true},"isPlusUser":false,"language":"en_US","circledByCount":0,"verified":false}
but if i try with gmail
{"error":{"errors":[{"domain":"global","reason":"failedPrecondition","message":"Bad Request"}],"code":400,"message":"Bad Request"}}
In case of GMail, you are accessing a particular user's data, so when creating the JWT, you need to specify the user whom you are trying to impersonate, i.e. the user whose mailbox you want to access.
You can do this using the sub:"User's email address parameter" when forming the JWT Claim set
var sPayload=JSON.stringify({
"iss":client_email,
"sub":USER_EMAIL_ADDRESS
"scope":scope,//gmail scope https://mail.google.com/
"aud":"https://www.googleapis.com/oauth2/v3/token",
"exp":exp,
"iat":iat
});

Categories

Resources