<p>I recently enabled IAP in GKE cluster. </p>

<ul>
<li>Cluster Version: 1.15.11-gke.11</li>
</ul>

<p>I followed the instructions here: <a href="https://cloud.google.com/iap/docs/enabling-kubernetes-howto" rel="noreferrer">https://cloud.google.com/iap/docs/enabling-kubernetes-howto</a></p>

<p>Service config is as follows:</p>

<pre><code>---
apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: foo-bc-iap
  namespace: foo-test
spec:
  iap:
    enabled: true
    oauthclientCredentials:
      secretName: iap-client-secret
---
apiVersion: v1
kind: Service
metadata:
  name: foo-internal-service
  namespace: foo-test
  annotations:
    cloud.google.com/backend-config: '{"ports":{"80":"foo-bc-iap"}}'
spec:
  type: NodePort # To create Ingress using the service.
  selector:
    app: foo-test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8081
</code></pre>

<p>The credential I used was OAuth 2.0 Client ID (Type: Web Application).</p>

<p>After making sure the IAP-protected API endpoint works differently when I activate IAP on the Kubernetes service, I wrote the following test program to make sure the endpoint is accessible from the service account given in the JSON file 'account.json'. </p>

<p>In writing this sample application, I consulted this doc: <a href="https://cloud.google.com/iap/docs/authentication-howto#iap_make_request-go" rel="noreferrer">https://cloud.google.com/iap/docs/authentication-howto#iap_make_request-go</a></p>

<ul>
<li>google.golang.org/api v0.23.0</li>
<li>go 1.12</li>
</ul>

<pre><code>func (m *myApp) testAuthz(ctx *cli.Context) error {
    audience := "&lt;The client ID of the credential mentioned above&gt;"
    serviceAccountOption := idtoken.WithCredentialsFile("account.json")

    client, err := idtoken.NewClient(ctx.Context, audience, serviceAccountOption)
    if err != nil {
        return fmt.Errorf("idtoken.NewClient: %v", err)
    }

    requestBody := `{
        &lt;some JSON payload&gt;
    }`

    request, err := http.NewRequest("POST", "https://my.iap.protected/endpoint",
        bytes.NewBuffer([]byte(requestBody)))
    if err != nil {
        return fmt.Errorf("http.NewRequest: %v", err)
    }

    request.Header.Add("Content-Type", "application/json")

    response, err := client.Do(request)
    if err != nil {
        return fmt.Errorf("client.Do: %v", err)
    }
    defer response.Body.Close()

    fmt.Printf("request header = %#v\n", response.Request.Header)
    fmt.Printf("response header = %#v\n", response.Header)

    body, err := ioutil.ReadAll(response.Body)
    if err != nil {
        return fmt.Errorf("ioutil.ReadAll: %v", err)
    }

    fmt.Printf("%d: %s\n", response.StatusCode, string(body))

    return nil
}
</code></pre>

<p>But when I run this, I could only see the following response.</p>

<pre><code>request header = http.Header{"Authorization":[]string{"Bearer &lt;jwt token&gt;"}, "Content-Type":[]string{"application/json"}, "X-Cloud-Trace-Context":[]string{"c855757f20d155da1140fad1508ae3e5/17413578722158830486;o=0"}}

response header = http.Header{"Alt-Svc":[]string{"clear"}, "Content-Length":[]string{"49"}, "Content-Type":[]string{"text/html; charset=UTF-8"}, "Date":[]string{"Wed, 06 May 2020 22:17:43 GMT"}, "X-Goog-Iap-Generated-Response":[]string{"true"}}

401: Invalid IAP credentials: JWT signature is invalid
</code></pre>

<p><strong>As you can see here, the access was denied</strong>.</p>

<p>So I thought that the signature used to sign the JWT token in the header might be wrong. </p>

<p>But I made sure the following using jwt.io:</p>

<ul>
<li>The JWT token used in the header is signed by the private key of the caller's service account</li>
<li>The JWT token used in the header can be verified by the public key of the caller's service account</li>
<li>The JWT token was signed using RS256 algorithm</li>
</ul>

<p>And I also looked into the token:</p>

<pre><code>{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "&lt;the service account's private key ID&gt;"
}

{
  "iss": "&lt;email address of the service account&gt;",
  "aud": "",
  "exp": 1588806087,
  "iat": 1588802487,
  "sub": "&lt;email address of the service acocunt&gt;"
}
</code></pre>

<p>Nothing quite odd. </p>

<p>So I'm not sure what's going on here. <strong>If I disable IAP, the endpoint return the right response</strong>.</p>

<p>Can anyone give me some hint about what I am doing wrong?</p>

<p>I tried your code, and I found that it doesn't work with <code>google.golang.org/api v0.23.0</code>, but it <em>does</em> work with <code>google.golang.org/api v0.24.0</code> (latest one at the time of writing).</p>

<p>It is indeed a bug, the <a href="https://github.com/googleapis/google-api-go-client/blob/master/CHANGES.md" rel="nofollow noreferrer">release notes</a> mention the following:</p>

<pre><code>When provided, use the TokenSource from options for NewTransport. This fixes a bug in idtoken.NewClient where the wrong TokenSource was being used for authentication.
</code></pre>

<p>Interestingly, 0.23.0 is sending a token signed <em>with the service account's private key</em> with the following claims:</p>

<pre><code>{
  "iss":"xx@xx.iam.gserviceaccount.com",
  "aud":"",
  "exp":1589596554,
  "iat":1589592954,
  "sub":"xx@xx.iam.gserviceaccount.com"
}
</code></pre>

<p>and 0.24.0 sends a token signed <em>with google's private key</em>. (internally the previous token is exchanged for a google-signed token)</p>

<pre><code>{
    "aud":"xxxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
    "azp":"xx@xx.iam.gserviceaccount.com",
    "email":"xx@xx.iam.gserviceaccount.com",
    "email_verified":true,
    "exp":1589596508,
    "iat":1589592908,
    "iss":"https://accounts.google.com",
    "sub":"11524xxxxxxxxxxxxxxxx"
}
</code></pre>
