Trying to get a access token

I have an app and an API

Just in case the techs for this matter:

app = ReactJS
api = golang

I have configured an application and set up my react app so I am able to log in, that works fine

I have configured the API to use some middleware to check for an access token, as expected I get a 401

I now want to have my react app pass the access token to the API so that logged-in users may create resources via the API, this is where I am hitting an issue.

I am following: Auth0 React SDK Quickstarts: Call an API

the response I get when making the call for an auth token is Error: Consent required.

I assume this is because my callback URL is: http://localhost:4200 in my application setup?

Whilst I understand if this is the case it is a little bit of a pain for development purposes in my case, I am assuming that I could simply alias localhost:4200 to say http://some-url/ and change the callback url in the applications?

If there is another option then that would be great too

Just coming back here to save someone some time maybe.

I changed etc/hosts to resolve my hostname to 127.0.01

Once i had done that the client errored as it required a secure protocol, for me this meant serving my app locally over HTTPS using a self-signed cert and swapping my ws protocol over to wss.

Once i had done this I was able to get the token which I am now hooking up to see if I will be allowed access to my protected resource.

1 Like

Thanks for sharing your solution, @sam.blackwell.1988!

Thankyou to you and your team for the rather nice service, being a lone developer having a free tier makes this authentication as a service option a reality for me, saving me a whole bunch of headaches.

1 Like

I’m happy to hear our service is helping you! Thanks for saying so! :slightly_smiling_face:

One question whilst you are here if possible?

I am now getting Invalid audience. as a response from my middleware located in my api, I am almost certain I have set this correctly.

i was following this : Authentication in Golang and React using JWTs

Hm, I’m not sure what is causing the error. I did find this topic (it’s a bit older, though): Go Backend Quickstart: Invalid Audience Claim - #2 by jmangelo

It may be helpful to go to https://jwt.io/ and decode the JWT to see if the audience parameter is coming in as exactly your API identifier.

So far i have found that the error is coming from this code:

checkAud := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false)
			if !checkAud {

				return token, errors.New("Invalid audience.")
			}

The code is located in the tutorial I posted, so i can only guess that either something is wrong with the code in that tutorial or the value for aud is wrong.

I see. I’ll take a look at the tutorial and see if I run into the same thing to investigate this a bit more!

1 Like

You might be on to something, when decoding the token i see that there are two audiances in there, the question for me is what to do with that.

ok so i have narrowed it down to this:

func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
	aud, ok := m["aud"].([]string)
	if !ok {
		strAud, ok := m["aud"].(string)
		if !ok {
			return false
		}
		aud = append(aud, strAud)
	}

	return verifyAud(aud, cmp, req)
}

Now as strange as it might be I am doing this as part of an effort to pick up go so I could be really off here.

strAud, ok := m["aud"].(string)

as the value of the key “aud” is an array this fails. the function returns and then it hits the error block.

So this is the why, now to figure out the how to solve it :slight_smile:

ok so it seems like i have 2 audiences in my JWT token, one i was expecting but the other one i have no idea about.

the problem is that when you follow that tutorial it seems like the jwt-go library doesnt expect the aud key to be an array.

That tutorial will always cause an error due to this.

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
)
1 Like

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.