If you want to make calls from GCF to IAP protected app, you should indeed be using ID tokens. There are no examples in Nodejs so I made one using this as a reference (style may be wrong since that's the first time I touch nodejs). Unlike regular JWT claims set, it should not contain scope and have target_audience.
exports.CfToIAP = function CfToIAP (req, res) {
var crypto = require('crypto'),
request = require('request');
var token_URL = "https://www.googleapis.com/oauth2/v4/token";
var key = "-----BEGIN PRIVATE KEY-----\nMIIEvQexsQ1DBNe12345GRwAZM=\n-----END PRIVATE KEY-----\n";
var JWT_header = new Buffer(JSON.stringify({ alg: "RS256", typ: "JWT" })).toString('base64');
var iss = "12345@12345.iam.gserviceaccount.com";
var aud = "https://www.googleapis.com/oauth2/v4/token";
var iat = Math.floor(new Date().getTime() / 1000);
var exp = iat + 120;
var target_audience = "12345.apps.googleusercontent.com";
var claims = {
iss: iss,
aud: aud,
iat: iat,
exp: exp,
target_audience: target_audience
};
var JWT_claimset = new Buffer(JSON.stringify(claims)).toString('base64');
var unsignedJWT = [JWT_header, JWT_claimset].join('.');
var JWT_signature = crypto.createSign('RSA-SHA256').update(unsignedJWT).sign(key, 'base64');
var signedJWT = [unsignedJWT, JWT_signature].join('.');
request.post({url:token_URL, form: {grant_type:'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion:signedJWT}}, function(err,res,body){
var data = JSON.parse(body);
var bearer = ['Bearer', data.id_token].join(' ');
var options = {
url: 'https://1234.appspot.com/',
headers: {
'User-Agent': 'cf2IAP',
'Authorization': bearer
}
};
request(options, function (err, res, body) {
console.log('error:', err);
});
});
res.send('done');
};
{
"name": "IAP-test",
"version": "0.0.1",
"dependencies": {
"request": ">=2.83"
}
}
Update: Bundling service account key is not recommended, so a better option is to use the metadata server. For the below sample to work Google Identity and Access Management (IAM) API should be enabled and App Engine default service account should have Service Account Actor role (default Editor is not enough):
exports.CfToIAP = function CfToIAP (req, res) {
const request = require('request');
const user_agent = '<user_agent_to_identify_your_CF_call>';
const token_URL = "https://www.googleapis.com/oauth2/v4/token";
const project_id = '<project_ID_where_CF_is_deployed>';
const service_account = [project_id,
'@appspot.gserviceaccount.com'].join('');
const target_audience = '<IAP_client_ID>';
const IAP_GAE_app = '<IAP_protected_GAE_app_URL>';
var meta_req_opts = {
url: ['http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/',
service_account,
'/token'].join(''),
headers: {
'User-Agent': user_agent,
'Metadata-Flavor': 'Google'
}
};
request(meta_req_opts, function (err, res, body) {
var meta_resp_data = JSON.parse(body);
var access_token = meta_resp_data.access_token;
var JWT_header = new Buffer(JSON.stringify({ alg: "RS256", typ: "JWT" })).toString('base64');
var iat = Math.floor(new Date().getTime() / 1000);
var claims = {
iss: service_account,
aud: token_URL,
iat: iat,
exp: iat + 60,
target_audience: target_audience
};
var JWT_claimset = new Buffer(JSON.stringify(claims)).toString('base64');
var to_sign = [JWT_header, JWT_claimset].join('.');
var signature_req_opts = {
url: ['https://iam.googleapis.com/v1/projects/',
project_id,
'/serviceAccounts/',
service_account,
':signBlob'].join(''),
method: "POST",
json: {
"bytesToSign": new Buffer(to_sign).toString('base64')
},
headers: {
'User-Agent': user_agent,
'Authorization': ['Bearer', access_token].join(' ')
}
};
request(signature_req_opts, function (err, res, body) {
var JWT_signature = body.signature;
var JWT = [JWT_header, JWT_claimset, JWT_signature].join('.');
request.post({url:token_URL, form: {grant_type:'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion:JWT}}, function(err, res, body){
var ID_token_resp_data = JSON.parse(body);
var ID_token = ID_token_resp_data.id_token;
var IAP_req_opts = {
url: IAP_GAE_app,
headers: {
'User-Agent': user_agent,
'Authorization': ['Bearer', ID_token].join(' ')
}
};
request(IAP_req_opts, function (err, res, body) {
console.log('error:', err);
});
});
});
});
res.send('done');
};