Skip to content

Commit 061a536

Browse files
author
Ace Nassri
authored
GCF: add bearer-auth sample (#1705)
✅Tested manually ~**TODO** add automated [smoke] test~ (_Properly_ testing this would require deploying it 😢)
1 parent 46f6409 commit 061a536

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed

.kokoro/functions/security.cfg

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Format: //devtools/kokoro/config/proto/build.proto
2+
3+
# Set the folder in which the tests are run
4+
env_vars: {
5+
key: "PROJECT"
6+
value: "functions/security"
7+
}
8+
9+
# Tell the trampoline which build file to use.
10+
env_vars: {
11+
key: "TRAMPOLINE_BUILD_FILE"
12+
value: "github/nodejs-docs-samples/.kokoro/build.sh"
13+
}

functions/security/index.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
// [START functions_bearer_token]
18+
const {get} = require('axios');
19+
20+
// TODO(developer): set these values
21+
const REGION = 'us-central1';
22+
const PROJECT_ID = 'my-project-id';
23+
const RECEIVING_FUNCTION = 'myFunction';
24+
25+
// Constants for setting up metadata server request
26+
// See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
27+
const functionURL = `https://${REGION}-${PROJECT_ID}.cloudfunctions.net/${RECEIVING_FUNCTION}`;
28+
const metadataServerURL =
29+
'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
30+
const tokenUrl = metadataServerURL + functionURL;
31+
32+
exports.callingFunction = async (req, res) => {
33+
// Fetch the token
34+
const tokenResponse = await get(tokenUrl, {
35+
headers: {
36+
'Metadata-Flavor': 'Google',
37+
},
38+
});
39+
const token = tokenResponse.data;
40+
41+
// Provide the token in the request to the receiving function
42+
try {
43+
const functionResponse = await get(functionURL, {
44+
headers: {Authorization: `bearer ${token}`},
45+
});
46+
res.status(200).send(functionResponse.data);
47+
} catch (err) {
48+
console.error(err);
49+
res.status(500).send('An error occurred! See logs for more details.');
50+
}
51+
};
52+
// [END functions_bearer_token]

functions/security/package.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "nodejs-docs-samples-functions-security",
3+
"version": "0.0.1",
4+
"private": true,
5+
"license": "Apache-2.0",
6+
"author": "Google LLC",
7+
"repository": {
8+
"type": "git",
9+
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
10+
},
11+
"engines": {
12+
"node": ">=8.0.0"
13+
},
14+
"scripts": {
15+
"test": "mocha test/*.test.js"
16+
},
17+
"dependencies": {
18+
"axios": "^0.19.2",
19+
"eslint-plugin-node": "^11.1.0",
20+
"mocha": "^7.1.1"
21+
},
22+
"devDependencies": {
23+
"assert": "^2.0.0",
24+
"proxyquire": "^2.1.3",
25+
"sinon": "^9.0.1"
26+
}
27+
}

functions/security/test/index.test.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2017 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const proxyquire = require('proxyquire').noCallThru();
18+
const sinon = require('sinon');
19+
const assert = require('assert');
20+
21+
const getSample = () => {
22+
const getMock = sinon
23+
.stub()
24+
.onFirstCall()
25+
.resolves({data: 'some-token'})
26+
.onSecondCall()
27+
.resolves({data: 'function-response'});
28+
29+
const axiosMock = {get: getMock};
30+
31+
const resMock = {};
32+
resMock.status = sinon.stub().returns(resMock);
33+
resMock.send = sinon.stub().returns(resMock);
34+
35+
return {
36+
sample: proxyquire('../', {
37+
axios: axiosMock,
38+
}),
39+
mocks: {
40+
res: resMock,
41+
axios: axiosMock,
42+
},
43+
};
44+
};
45+
46+
describe('functions_bearer_token', () => {
47+
it('should run', async () => {
48+
const {sample, mocks} = getSample();
49+
50+
await sample.callingFunction(null, mocks.res);
51+
52+
assert(mocks.axios.get.calledTwice);
53+
assert.deepEqual(mocks.axios.get.firstCall.args[1], {
54+
headers: {'Metadata-Flavor': 'Google'},
55+
});
56+
57+
assert(mocks.res.send.calledWith('function-response'));
58+
});
59+
});

0 commit comments

Comments
 (0)