Securing Google Cloud Endpoints with Auth0

Google Cloud Endpoints (GCE) is an API management system providing features to help you create, maintain, and secure your APIs. GCE uses OpenAPI to define your API's endpoints, input and output, errors, and security description.

For more information on the OpenAPI spec, see the OpenAPI Specification repository on GitHub.

This tutorial will cover how to secure Google Cloud Endpoints with Auth0.

Prerequisites

Before you begin you'll need a deployed GCE API. If you haven't already created an API, complete the Cloud Endpoints Quickstart.

The quickstart will walk you through creating a simple GCE API with a single endpoint, /airportName, that returns the name of an airport from its three-letter IATA code.


curl --request GET \
  --url 'https://your_gce_project.appspot.com/airportName?iataCode=SFO'
var client = new RestClient("https://your_gce_project.appspot.com/airportName?iataCode=SFO");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
package main

import (
	"fmt"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://your_gce_project.appspot.com/airportName?iataCode=SFO"

	req, _ := http.NewRequest("GET", url, nil)

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://your_gce_project.appspot.com/airportName?iataCode=SFO")
  .asString();
var settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://your_gce_project.appspot.com/airportName?iataCode=SFO",
  "method": "GET",
  "headers": {}
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

var options = { method: 'GET',
  url: 'https://your_gce_project.appspot.com/airportName',
  qs: { iataCode: 'SFO' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://your_gce_project.appspot.com/airportName?iataCode=SFO"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];
$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://your_gce_project.appspot.com/airportName?iataCode=SFO",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
import http.client

conn = http.client.HTTPSConnection("your_gce_project.appspot.com")

conn.request("GET", "/airportName?iataCode=SFO")

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))
require 'uri'
require 'net/http'

url = URI("https://your_gce_project.appspot.com/airportName?iataCode=SFO")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body
import Foundation

var request = NSMutableURLRequest(URL: NSURL(string: "https://your_gce_project.appspot.com/airportName?iataCode=SFO")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "GET"

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(httpResponse)
  }
})

dataTask.resume()

Define the API in Auth0

Open Dashboard > APIs and create a new API.

Create API

Make note of the API Audience identifier (http://google_api in the screenshot above) to use in the following step.

Update the API Configuration

Next, we'll update the OpenAPI configuration file for the GCE API. For the sample API created during the quickstart this file is openapi.yaml.

Add Security Definitions

Open the configuration file and add a new securityDefinitions section. In this section, add a new definition (auth0_jwt) with the following fields:

Field Description
authorizationUrl The authorization URL, should be set to "https://YOUR_AUTH0_DOMAIN/authorize"
flow The flow used by the OAuth2 security scheme. Valid values are "implicit", "password", "application" or "accessCode".
type The type of the security scheme. Valid values are "basic", "apiKey" or "oauth2"
x-google-issuer The issuer of a credential, should be set to "https://YOUR_AUTH0_DOMAIN/"
x-google-jwks_uri The URI of the public key set to validate the JSON Web Token signature. Set this to "https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json"
x-google-audiences The API's identifier, make sure this value matches what you defined on the Auth0 dashboard for the API.
securityDefinitions:
  auth0_jwt:
    authorizationUrl: "https://YOUR_AUTH0_DOMAIN/authorize"
    flow: "implicit"
    type: "oauth2"
    x-google-issuer: "https://YOUR_AUTH0_DOMAIN/"
    x-google-jwks_uri: "https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json"
    x-google-audiences: "{YOUR_API_IDENTIFIER}"

Update the Endpoint

Now, update the endpoint by adding a security field with the securityDefinition we created in the previous step.

paths:
  "/airportName":
    get:
      description: "Get the airport name for a given IATA code."
      operationId: "airportName"
      parameters:
        -
          name: iataCode
          in: query
          required: true
          type: string
      responses:
        200:
          description: "Success."
          schema:
            type: string
        400:
          description: "The IATA code is invalid or missing."
      security:
       - auth0_jwt: []

In the above example, the security field tells the GCE proxy that our /airportName path expects to be secured with the auth0-jwt definition.

After updating the OpenAPI configuration, it should look something like this:

---
swagger: "2.0"
info:
  title: "Airport Codes"
  description: "Get the name of an airport from its three-letter IATA code."
  version: "1.0.0"
host: "YOUR_GCE_PROJECT.appspot.com"
schemes:
  - "https"
paths:
  "/airportName":
    get:
      description: "Get the airport name for a given IATA code."
      operationId: "airportName"
      parameters:
        -
          name: iataCode
          in: query
          required: true
          type: string
      responses:
        200:
          description: "Success."
          schema:
            type: string
        400:
          description: "The IATA code is invalid or missing."
      security:
       - auth0_jwt: []
securityDefinitions:
  auth0_jwt:
    authorizationUrl: "https://YOUR_AUTH0_DOMAIN/authorize"
    flow: "implicit"
    type: "oauth2"
    x-google-issuer: "https://YOUR_AUTH0_DOMAIN/"
    x-google-jwks_uri: "https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json"
    x-google-audiences: "{YOUR_API_IDENTIFIER}"

Redeploy the API

Next, redeploy your GCE API to apply the configuration changes. If you followed along with the Cloud Endpoints Quickstart you can redeploy by entering the following in Google's Cloud Shell:

cd endpoints-quickstart/scripts
./deploy_api.sh

Test the API

Once you've redeployed, call the API again with no security.


curl --request GET \
  --url 'https://your_gce_project.appspot.com/airportName?iataCode=SFO'
var client = new RestClient("https://your_gce_project.appspot.com/airportName?iataCode=SFO");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
package main

import (
	"fmt"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://your_gce_project.appspot.com/airportName?iataCode=SFO"

	req, _ := http.NewRequest("GET", url, nil)

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://your_gce_project.appspot.com/airportName?iataCode=SFO")
  .asString();
var settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://your_gce_project.appspot.com/airportName?iataCode=SFO",
  "method": "GET",
  "headers": {}
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

var options = { method: 'GET',
  url: 'https://your_gce_project.appspot.com/airportName',
  qs: { iataCode: 'SFO' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://your_gce_project.appspot.com/airportName?iataCode=SFO"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];
$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://your_gce_project.appspot.com/airportName?iataCode=SFO",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
import http.client

conn = http.client.HTTPSConnection("your_gce_project.appspot.com")

conn.request("GET", "/airportName?iataCode=SFO")

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))
require 'uri'
require 'net/http'

url = URI("https://your_gce_project.appspot.com/airportName?iataCode=SFO")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body
import Foundation

var request = NSMutableURLRequest(URL: NSURL(string: "https://your_gce_project.appspot.com/airportName?iataCode=SFO")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "GET"

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(httpResponse)
  }
})

dataTask.resume()

You'll get the following response:

{
 "code": 16,
 "message": "JWT validation failed: Missing or invalid credentials",
 "details": [
  {
   "@type": "type.googleapis.com/google.rpc.DebugInfo",
   "stackEntries": [],
   "detail": "auth"
  }
 ]
}

Which is exactly what we want!

Now go to the Test page of your Google Endpoints API definition on the Auth0 Dashboard, and copy the access_token:

Copy Token

Perform a GET request to your API with an Authorization Header of Bearer {ACCESS_TOKEN} to obtain authorized access:


curl --request GET \
  --url 'https://your_gce_project.appspot.com/airportName?iataCode=SFO' \
  --header 'authorization: Bearer {ACCESS_TOKEN}'
var client = new RestClient("https://your_gce_project.appspot.com/airportName?iataCode=SFO");
var request = new RestRequest(Method.GET);
request.AddHeader("authorization", "Bearer {ACCESS_TOKEN}");
IRestResponse response = client.Execute(request);
package main

import (
	"fmt"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://your_gce_project.appspot.com/airportName?iataCode=SFO"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("authorization", "Bearer {ACCESS_TOKEN}")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://your_gce_project.appspot.com/airportName?iataCode=SFO")
  .header("authorization", "Bearer {ACCESS_TOKEN}")
  .asString();
var settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://your_gce_project.appspot.com/airportName?iataCode=SFO",
  "method": "GET",
  "headers": {
    "authorization": "Bearer {ACCESS_TOKEN}"
  }
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

var options = { method: 'GET',
  url: 'https://your_gce_project.appspot.com/airportName',
  qs: { iataCode: 'SFO' },
  headers: { authorization: 'Bearer {ACCESS_TOKEN}' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer {ACCESS_TOKEN}" };

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://your_gce_project.appspot.com/airportName?iataCode=SFO"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
[request setAllHTTPHeaderFields:headers];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];
$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://your_gce_project.appspot.com/airportName?iataCode=SFO",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "authorization: Bearer {ACCESS_TOKEN}"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
import http.client

conn = http.client.HTTPSConnection("your_gce_project.appspot.com")

headers = { 'authorization': "Bearer {ACCESS_TOKEN}" }

conn.request("GET", "/airportName?iataCode=SFO", headers=headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))
require 'uri'
require 'net/http'

url = URI("https://your_gce_project.appspot.com/airportName?iataCode=SFO")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(url)
request["authorization"] = 'Bearer {ACCESS_TOKEN}'

response = http.request(request)
puts response.read_body
import Foundation

let headers = ["authorization": "Bearer {ACCESS_TOKEN}"]

var request = NSMutableURLRequest(URL: NSURL(string: "https://your_gce_project.appspot.com/airportName?iataCode=SFO")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "GET"
request.allHTTPHeaderFields = headers

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(httpResponse)
  }
})

dataTask.resume()

And that's it!