Static pages, AWS API Gateway, Custom Authorizers and JWT tokens: how?

In Is it safe to expose client_id, client_secret, and audience on HTML pages?, I asked if it is safe to expose client_id and client_secret in a static site’s pages or scripts and scripts and @luis.rudge kindly answered that, in short, no:

client_id: yes. client_secret, absolutely not. We encode tokens with that secret, so, anyone having this secret can emit tokens that your application will think it’s safe.

Also, from a web page, you shouldn’t be calling the oauth/token endpoint. Please follow our jquery quick starts:

A corollary of that is that I can’t send POST requests to an AWS API Gateway based API that is protected using a Custom Authorizer like this:

var settings = {
  "async": true,
  "crossDomain": true,
  "url": "",
  "method": "POST",
  "headers": {
    "content-type": "application/json"
  "data": "{\"client_id\":\"...\",\"client_secret\":\"...\",\"audience\":\"...\",\"grant_type\":\"client_credentials\"}"

$.ajax(settings).done(function (response) {

On the other hand, my custom authorizer, expects a JWT in the request to authorize a request to my API.

How can a single page application produce this JWT to pass it to the API in AJAX request Authorization headers?

Apparently the JWT is stored in local storage under id_token (authResult.idToken), however when I create the Authorization header like this to call my endpoint:

    type: "POST",
    url: "...",
    data: JSON.stringify(payload),
    headers: {
      "Authorization": `Bearer ${localStorage.getItem('id_token')}`
    success: function (response) {
        // ...

The API Gateway custom Authorizer expects a kid property to be present in the header which is not.

I added some log statements to that code and here’s what is received by the authorizer:

// Decoded token:
    "header": {
        "typ": "JWT",
        "alg": "HS256"
    "payload": {
        "given_name": "...",
        "nickname": "...",
        "name": "...",
        "picture": ".../photo.jpg",
        "gender": "male",
        "locale": "en",
        "updated_at": "2018-05-27T13:28:20.644Z",
        "email": "",
        "email_verified": true,
        "iss": ".../",
        "sub": "google-oauth2|...",
        "aud": "Mtg...lwG",
        "iat": ...,
        "exp": ...,
        "at_hash": "-dbk...M5A",
        "c_hash": "S-Fw...aPw",
        "nonce": "UCS...0A0-"
    "signature": "mln...jdM"

// KID
KID undefined

What am I missing here?

I found the solution to this and posted the details here.