I'm sure there are clever people who can make anything run on Azure functions but does it make sense at all to use it for running Office Add-in's? I read that it's ideal for running small pieces of code and that's what my add-in currently running on Azure as an web app is.
You wouldn't use Azure Functions to build an add-in -- but you absolutely could use it in conjunction with a regular website, for some small server-side processing.
Concrete example: for an Add-in that a colleague and I were building, we needed to obtain a user's GitHub permissions to post Gists on the user's behalf. GitHub uses an "authorization code grant type" flow (see https://developer.github.com/v3/oauth/), so the flow would be as follows:
We pop a dialog (using the recently-introduce Dialog API in Add-ins) to direct the user to https://github.com/login/oauth/authorize, which shows a pretty login UI.
If the user signs in and consents, GitHub sends an authorization code back to us. The code does little good for us in client-side JavaScript, but if we pass it to an Azure Function, we can exchange it for an access token. This must be done in some server-side code (i.e., a web server, or Azure Functions, as a super-lightweight form of a web server) so that we can pass in a Client Secret for the exchange -- which quite naturally wouldn't be secret in sniffable-out client-side JavaScript. Hence putting that code on the server.
If you're curious to see what that code was like, here it is:
var request = require('request');
module.exports = function (context, data) {
context.log('code: ' + data.code);
if ('code' in data) {
request.post({
url: 'https://github.com/login/oauth/access_token',
json: {
client_id: '################',
client_secret: '################',
redirect_uri: '################',
code: data.code
}
}, function (err, httpResponse, body) {
if (err) {
context.log('error: ' + err);
context.res = {
body: {
status: 500,
error: err
}
}
}
else {
context.res = { body: body };
}
context.done();
});
}
else {
context.res = {
status: 400,
body: { error: 'Please pass the GitHub code in the input object' }
};
context.done();
}
}
Related
I am trying to integrate the credential manager API into my extension so I can save the API credentials using it but am having issues.
Just for testing purposes, I just tried saving the credentials from the popup (Where the user login happens) and when I tried that I got an error saying "NotSupportedError: The user agent does not support public key credentials." I did some googling but didn't find what I was looking for. I thought it might have something to do with the popup so I tried doing the same through the background script but it can't access the window object so that didn't work.
I haven't yet tried doing this though a content script which I would imagine would work but ideally I'd prefer to just store it when the user logs in and then just call it as I need it.
Here is the basic code I'm using for testing that I grabbed from the MDN site;
if ("PasswordCredential" in window) {
let cmAPICredential = new PasswordCredential({
id: "extension",
name: uname,
password: pwd
});
navigator.credentials.store(cmAPICredential).then(() => {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Credentials stored in the user agent's credential manager.`, cpage: "main.js" }, function(response) {});
}
}, (err) => {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Error while storing the credential: ${err}`, cpage: "main.js" }, function(response) {});
}
});
} else {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Password credential isn't supported in this browser`, cpage: "main.js" }, function(response) {});
}
}
Is there any way that I can do what I am attempting? The only thing I really want to secure is the password and this seems to be the only good way to do it through a chrome extension.
Based on #wOxxOm response and some other research, it looks like I can't use the credential management API with chrome extensions.
So the answer to my question is you can't.
This question builds on How to get Microsoft Graph API Access token from Node Script?, however, as a first-time user of, I don't have the required reputation for commenting on the accepted answer in that thread.
The thing is, I tried to implement the approach suggested by the accepted answer, but somewhere it goes wrong. The below code is part of an async function, and I can already tell you that the ONEDRIVE_TENANT_URI is of the format XXX.onmicrosoft.com.
const endpoint = `https://login.microsoftonline.com/${process.env.ONEDRIVE_TENTANT_URI}/oauth2/token`;
const requestParams = {
grant_type: "client_credentials",
client_id: process.env.ONEDRIVE_APP_ID,
client_secret: process.env.ONEDRIVE_CLIENT_SECRET,
resource: "https://graph.windows.net"
};
const authResponse = await request.post({
url: endpoint,
form: requestParams
});
authResponse gets, as its body, just a string with the requestParams as defined above filled out.
If I submit the post request via Postman, with the same parameters as x-www-form-urlencoded, I DO get an access_token in the response body.
So... What do I do wrong? Maybe - but I don't think so - it's because this function is invoked by a (for testing purposes) POSTMAN GET request with a json-formatted body?
You can download the sample here. And fill in the credentials in config.js. You can find them from Azure portal.
This is the code to get access token.
auth.getAccessToken = function () {
var deferred = Q.defer();
// These are the parameters necessary for the OAuth 2.0 Client Credentials Grant Flow.
// For more information, see Service to Service Calls Using Client Credentials (https://msdn.microsoft.com/library/azure/dn645543.aspx).
var requestParams = {
grant_type: 'client_credentials',
client_id: config.clientId,
client_secret: config.clientSecret,
resource: 'https://graph.microsoft.com'
};
// Make a request to the token issuing endpoint.
request.post({ url: config.tokenEndpoint, form: requestParams }, function (err, response, body) {
var parsedBody = JSON.parse(body);
console.log(parsedBody);
if (err) {
deferred.reject(err);
} else if (parsedBody.error) {
deferred.reject(parsedBody.error_description);
} else {
// If successful, return the access token.
deferred.resolve(parsedBody.access_token);
}
});
return deferred.promise;
};
You will get the access token successfully.
You've got two issues going on.
The first isn't an issue yet, but it will be once you try to call Microsoft Graph. The resource should be graph.microsoft.net, not graph.windows.net. The graph.windows.net refers to the legacy Azure AD Graph API, not Microsoft Graph.
The other issue, which is the root cause of this error, is await request.post. Request doesn't natively support promises. From the Request the documentation:
request supports both streaming and callback interfaces natively. If you'd like request to return a Promise instead, you can use an alternative interface wrapper for request. These wrappers can be useful if you prefer to work with Promises, or if you'd like to use async/await in ES2017.
Several alternative interfaces are provided by the request team, including:
request-promise (uses Bluebird Promises)
request-promise-native (uses native Promises)
request-promise-any (uses any-promise Promises)
I'm using the google node API to generate a spreadsheet. The 3rd party library I use is the official one (https://github.com/google/google-api-nodejs-client). Here is the code I use to generate the spreadsheet:
var sheets = google.sheets('v4');
sheets.spreadsheets.create({
auth: auth,
resource: {
properties:{
title: "Google export"
}
}
}, (err, response) => {
if (err) {
console.log('The API returned an error: ' + err);
return;
} else {
console.log("Added");
res.send(response);
}
});
It works fine and generate the sheet, when I display the response object, I get the link to the spreadsheet:
spreadsheetUrl: "https://docs.google.com/spreadsheets/d/1XS_QHxPUQcGqZGAzFHkHZV-RSKsv5IpJazcjVpLTYkk/edit"
But when I try to open it in the browser, I can't: You need permission.
The thing is if I click on the Request access button, I think it actually sends a request to the client_email from my credential (user#project-174309.iam.gserviceaccount.com), so it doesn't work.
If I open the spreadsheet programmatically it works...
Does anyone know how to open the generated spreadsheet in a browser?
Here is a solution: https://github.com/google/google-api-nodejs-client/issues/804#issuecomment-327931203
Need to use Google Drive API to grant permissions for this file
I have an offline Chrome Web App I want to sell through the web store.
It has a server backend. I'd like the server to check license data. How do I do that?
Here's the API. Here's a Java HowTo. Here's a OAuth-JWT lib for nodejs. I made a new client-id as described in the lib's readme.
I get this response:
{ error:
{ errors: [ [Object] ],
code: 403,
message: 'You don\'t have access to licensing data for App ID: aaaaaaaaaaaaaaaaaaaaaaaaaaaa'
}
}
How do I access license data for my app, in my server?
Mike
Here's the code:
var appId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
var userId = '1111111111111111111111';
// obtain a JWT-enabled version of request
var request = require('google-oauth-jwt').requestWithJWT();
request({
url: 'https://www.googleapis.com/chromewebstore/v1.1/licenses/'+appId+'/'+userId,
jwt: {
// use the email address of the service account, as seen in the API console
email: '11111111111-aaaaaaaaaaaaaaaaaaaaaaaa#developer.gserviceaccount.com',
// use the PEM file we generated from the downloaded key
keyFile: 'config/keys/app-11111111111111.pem',
// specify the scopes you wish to access - each application has different scopes
scopes: ['https://www.googleapis.com/auth/chromewebstore.readonly']
}
}, function (err, res, body) {
console.log(JSON.parse(body));
});
I've spent way to much time on this and other small problems with the Chrome Webstore. Going with Stripe.com, Braintree, or similar.
Has anyone been able to get the google-api-nodejs-client to successfully insert a moment?
Whatever I try, I get a generic 400 "Invalid value" error but am unable to narrow down the invalid value because the API Explorer doesn't work either.
Would it be because of the missing data-requestvisibleactions parameter? I'm using passport.js's require('passport-google-oauth').OAuth2Strategy for handling oauth access, and that part is working fine, but I have no idea how to incorporate requestvisibleactions into the oauth request flow since this is definitely not originating from a clientside form.
Here's a snippet of what I'm trying to do (using the latest version of googleapis, v1.0.2):
var google = require('googleapis')
var auth = new google.auth.OAuth2()
auth.setCredentials({
'access_token': user.token
})
google.plus('v1').moments.insert({
collection: 'vault',
userId: 'me',
debug: true,
resource: {
type: "http://schemas.google.com/AddActivity",
target: {
type: "http://schema.org/CreativeWork",
url: "...omitted...",
image: "...omitted...",
description: "test",
name: "test"
}
},
auth: auth
}, function (err, response) {
if (err) {
console.error(err)
res.send(err.code, err)
} else {
console.log(response)
res.send(200)
}
})
ref 1 (out-of-date w.r.t. an older version of googleapis)
ref 2 (client-side, where the use of data-requestvisibleactions is more obvious)
As you speculated, you need the request_visible_actions parameter as part of the URL calling the oauth endpoint.
It looks like the current version of passport-google-oauth doesn't support this parameter. Judging by several of the open issues and pull requests, it isn't clear that the author will respond to requests to add it either. You have two possible options:
Switch to using the OAuth support that is included in google-api-nodejs-client
Patch the passport-google-oauth code. (And possibly submit a pull request in the hopes it will be useful to someone else.)
I don't use passport.js or the passport module in question, so I can't test this, but based on the github repository, I think you can insert the following in lib/passport-google-oauth/oauth2.js after line 136 and before the return statement:
if (options.requestVisibleActions) {
// Space separated list of allowed app actions
// as documented at:
// https://developers.google.com/+/web/app-activities/#writing_an_app_activity_using_the_google_apis_client_libraries
// https://developers.google.com/+/api/moment-types/
params['request_visible_actions'] = options.requestVisibleActions;
}