Because Google's official document doesn't explain how to do it in Node.js, it was difficult for me to access Cloud IAP-protected GAE resources from Node.js.
In short, I could do that by following this example. In this blog post, I'll leave a record of what I learned on the way there.
What is Cloud IAP
Cloud Identity-Aware Proxy (Cloud IAP) is a service of Google Cloud Platform that provides authentication to resources on GCP.
Because only authenticated requests reach an app behind Cloud IAP, you don't need to implement authentication to the app by yourself.
With Cloud IAP, you can authorize only the users linked to a specific G Suite account. So it's especially useful when creating a company internal system.
Information required for authentication
To let Cloud IAP authenticate your request, three pieces of information are required:
- Client ID for OAuth2 authentication in Cloud IAP
- Service account email address
- Service account private key
If you give them to GCP, it's pretty intuitive that GCP can check
- What you're trying to access
- Who is accessing the site and is it him?
- Does he have access to it?
You will also obviously need to set up permissions on your service account in advance.
Authenticate with Node.js
First of all, you need to create a service account and add it to the "IAP-secured Web App User". While creating the account, you can obtain its Client ID.
- Service account email address.
- Service account private key
They are included in the JSON file that you can download when you issue a key on the service account page. Make sure that you don't git commit your private key because you should never spill it to the outside world. This is necessary regardless of the programming language.
Then, we can use google-auth-library to authenticate with Node.js.
The google-auth-library automatically creates a client based on our environment, but in this case, we'll create a JWT
instance by ourself:
const { JWT } = require("google-auth-library")
const client = new JWT({
email: SERVICE_ACCOUNT_EMAIL, // 2. Service account email address
key: SERVICE_ACCOUNT_PRIVATE_KEY, // 3. Service account private key
additionalClaims: {
target_audience: OAUTH2CLIENT_ID, // 1. Client ID for OAuth2 authentication in Cloud IAP
},
})
As you can see, the client
has the three information described above.
To access a resource, we need to get an access token and assemble an Authorization
header, and there is a handy method called getRequestHeaders()
.
// URL of resource protected by Cloud IAP
const url = "http://example.appspot.com/path/to/endpoint"
// { Authorization: 'Bearer <access_token_value>' }
const headers = await client.getRequestHeaders(url)
If you are using axios, you can use the url
and headers
as:
const axios = require("axios")
axios.request({ url, headers })
However, this is quite common, so client
provides a method to send a request which internally uses gaxios that is compatible with axios.
// Almost same as the sample code using axios above.
client.request({ url })
Consequently, you can access a resource protected by Cloud IAP as follows:
const { JWT } = require("google-auth-library")
const OAUTH2_CLIENT_ID = "..."
// Downloaded credentials JSON file.
const credentials = require("./service-account-credentials.json")
const SERVICE_ACCOUNT_EMAIL = credentials.client_email
const SERVICE_ACCOUNT_PRIVATE_KEY = credentials.private_key
async function main() {
const client = new JWT({
email: SERVICE_ACCOUNT_EMAIL,
key: SERVICE_ACCOUNT_PRIVATE_KEY,
additionalClaims: {
target_audience: OAUTH2_CLIENT_ID,
},
})
const url = "http://example.appspot.com"
const result = await client.request({ url })
console.log(result.data)
}
main().catch(console.error)