I have to make an authenticated request to a Cloud run service which uses HTTP/2 method for communication via grpc. The service returns a signedURL for a file and it does the same successfully, when deployed unauthenticated. I added the JWT token to the metadata while making request from client side. But the grpc server is giving the following error -
code: 16,
metadata: Metadata {
_internal_repr: {
'www-authenticate': [Array],
date: [Array],
server: [Array],
'x-envoy-upstream-service-time': [Array],
'content-length': [Array]
},
flags: 0
Your client does not have permission to the requested URL
On cloud run, I have enabled HTTP/2 in the connections tab. The service account associated with this Cloud Run service also has Cloud Run Invoker permissions. Following is the code for instantiating the grpc server -
const grpc = require('grpc')
const protoLoader = require('@grpc/proto-loader')
const packageDefinition = protoLoader.loadSync(
`${__dirname}/${proto_file}`,
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
})
const Proto = grpc.loadPackageDefinition(packageDefinition)
function main () {
const server = new grpc.Server()
server.addService(Proto.${service_name}.service, { ${function_name} })
server.bindAsync(`0.0.0.0:${PORT}`, grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
throw error
}
server.start()
console.log('gRPC Server running!')
})
}
main()
and following is client-side code which is making authenticated requests -
const grpc = require('grpc')
const protoLoader = require('@grpc/proto-loader')
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
const packageDefinition = protoLoader.loadSync(
`${__dirname}/${proto_file}`, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
})
//* Or make the changes for authenticated call to CDN here
const Proto = grpc.loadPackageDefinition(packageDefinition)
async function request() {
return auth.getIdTokenClient(targetAudience);
}
const metadata = new grpc.Metadata();
request().then((token)=> {
metadata.add('Authorization', `Bearer ${token}`);
})
function getFileLink (projectId, fileId, plaintext = null) {
const serverAddress = config.server_address
let credentials
if (plaintext) {
credentials = grpc.credentials.createInsecure()
} else {
credentials = grpc.credentials.createSsl()
}
const cdn = new cdnProto.CDN(serverAddress, grpc.credentials.createSsl())
const request = {
project_id: projectId,
file_id: fileId
}
return new Promise((resolve, reject) => {
cdn.GetFileLink(request, metadata, (error, response) => {
if (error) {
console.log(error)
resolve(null)
} else {
resolve(response)
}
})
})
}
I have resolved this issue. My approach was right, but my way of getting token is incorrect. To get the required auth Token, I have to call another method also.
async function request() {
client = await auth.getIdTokenClient(targetAudience);
return client.idTokenProvider.fetchIdToken(targetAudience)
}