Okay, I was able to get the API working by specifying the audience
in the static/src/auth_config.json file.
{
"domain": "YOUR_DOMAIN",
"clientId": "YOUR_CLIENT_ID",
"audience": "YOUR_API_IDENTIFIER"
}
This is part of the tutorial, so you have probably already done this.
My Access Token has the /userinfo endpoint and the API identifier as audiences.
This is what my main.go file looks like:
package main
// Import our dependencies. We'll use the standard HTTP library as well as the gorilla router for this app
import (
"encoding/json"
"errors"
"github.com/auth0/go-jwt-middleware"
"github.com/dgrijalva/jwt-go"
"github.com/gorilla/mux"
"github.com/rs/cors"
"net/http"
)
type Response struct {
Message string `json:"message"`
}
type Jwks struct {
Keys []JSONWebKeys `json:"keys"`
}
type JSONWebKeys struct {
Kty string `json:"kty"`
Kid string `json:"kid"`
Use string `json:"use"`
N string `json:"n"`
E string `json:"e"`
X5c []string `json:"x5c"`
}
/* We will first create a new type called Product
This type will contain information about VR experiences */
type Product struct {
Id int
Name string
Slug string
Description string
}
var products = []Product{
Product{Id: 1, Name: "World of Authcraft", Slug: "world-of-authcraft", Description: "Battle bugs and protect yourself from invaders while you explore a scary world with no security"},
Product{Id: 2, Name: "Ocean Explorer", Slug: "ocean-explorer", Description: "Explore the depths of the sea in this one of a kind underwater experience"},
Product{Id: 3, Name: "Dinosaur Park", Slug: "dinosaur-park", Description: "Go back 65 million years in the past and ride a T-Rex"},
Product{Id: 4, Name: "Cars VR", Slug: "cars-vr", Description: "Get behind the wheel of the fastest cars in the world."},
Product{Id: 5, Name: "Robin Hood", Slug: "robin-hood", Description: "Pick up the bow and arrow and master the art of archery"},
Product{Id: 6, Name: "Real World VR", Slug: "real-world-vr", Description: "Explore the seven wonders of the world in VR"},
}
func main() {
jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
// Verify 'aud' claim
aud := "MY_API_IDENTIFIER"
checkAud := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false)
if !checkAud {
return token, errors.New("Invalid audience.")
}
// Verify 'iss' claim
iss := "https://MY_DOMAIN/"
checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false)
if !checkIss {
return token, errors.New("Invalid issuer.")
}
cert, err := getPemCert(token)
if err != nil {
panic(err.Error())
}
result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
return result, nil
},
SigningMethod: jwt.SigningMethodRS256,
})
r := mux.NewRouter()
r.Handle("/", http.FileServer(http.Dir("./views/")))
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
r.Handle("/products", jwtMiddleware.Handler(ProductsHandler)).Methods("GET")
r.Handle("/products/{slug}/feedback", jwtMiddleware.Handler(AddFeedbackHandler)).Methods("POST")
// For dev only - Set up CORS so React client can consume our API
corsWrapper := cors.New(cors.Options{
AllowedMethods: []string{"GET", "POST"},
AllowedHeaders: []string{"Content-Type", "Origin", "Accept", "*"},
})
http.ListenAndServe(":8080", corsWrapper.Handler(r))
}
var ProductsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
payload, _ := json.Marshal(products)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(payload))
})
var AddFeedbackHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var product Product
vars := mux.Vars(r)
slug := vars["slug"]
for _, p := range products {
if p.Slug == slug {
product = p
}
}
w.Header().Set("Content-Type", "application/json")
if product.Slug != "" {
payload, _ := json.Marshal(product)
w.Write([]byte(payload))
} else {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}
})
func getPemCert(token *jwt.Token) (string, error) {
cert := ""
resp, err := http.Get("https://MY_DOMAIN/.well-known/jwks.json")
if err != nil {
return cert, err
}
defer resp.Body.Close()
var jwks = Jwks{}
err = json.NewDecoder(resp.Body).Decode(&jwks)
if err != nil {
return cert, err
}
for k, _ := range jwks.Keys {
if token.Header["kid"] == jwks.Keys[k].Kid {
cert = "-----BEGIN CERTIFICATE-----\n" + jwks.Keys[k].X5c[0] + "\n-----END CERTIFICATE-----"
}
}
if cert == "" {
err := errors.New("Unable to find appropriate key.")
return cert, err
}
return cert, nil
}
And this is what my go.mod looks like:
module github.com/auth0-blog/go-vr-auth
go 1.14
require (
github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gorilla/handlers v1.4.2
github.com/gorilla/mux v1.7.4
github.com/rs/cors v1.7.0
)