Auth0 with Go - Gin backend with JWT middleware
Tags: auth0, go, jwt, security
Published: 2022-10-28 12:28:08

Hi all, hope you are having a good day!
we will see how to validate the JWTs using Auth0 Golang JWT middleware using Gin Web Framework. Gin is a web framework written in Go (Golang). In deep-dive, we will see how to integrate the Auth0 Golang JWT middleware to verify JWTs

this blog is alter version of the @ksivamuthu [blog ](https://dev.to/ksivamuthu/auth0-jwt-middleware-in-go-gin-web-framework-37mj)
Thanks to @ksivamuthu, for writing an amazing blog, I am writing this blog as I could not find exact solution to my problem in the internet, hope it helps you today 

if you follow the the above mentioned blog you would know about basics about Auth0, gin framework

basically 
the user flow is like this 
(refer CLI as CLIENT) and the black box as server 
I am using a proxy in between (optional)

 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2gflel1cswmohshkmplo.JPG)

first follow this [blog](https://developer.auth0.com/resources/code-samples/api/standard-library/basic-role-based-access-control)

you have to create an app first and I am using command line app so I have created a native app and we have to create users and user roles and assign to the users and for this example I have created a user and a role which has permission "crud:admin-settings" in Auth0 app and update your call back url in the app, I am using "http://localhost:4242/auth" as call back, make sure everything is setup before using Auth0


you call Auth0 API for login and gives a token as a response if you login successfully

basically we have to use this JWT token for our endpoints, but before actually calling our handlers we need to validate the token and permissions 

example backed router go code

```
package api

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"

	"github.com/gin-gonic/gin" 
	"github.com/lestrrat-go/jwx/jwk"
	"github.com/lestrrat-go/jwx/jwt"
)

func StartServer() {

	router := gin.Default() 
 

	ruoterGroup := router.Group("api/v1/product") //grouping the route end point for the handlers
	 
	ruoterGroup.GET("", AuthorizeHandler(permission), GetHandler)
	ruoterGroup.POST("", AuthorizeHandler(permission), AddHandler)
	ruoterGroup.PUT("", AuthorizeHandler(permission), UpdateHandler)
	ruoterGroup.DELETE("", AuthorizeHandler(permission), DeleteHandler)

	baseURL := :"localhost:8080"
	router.Run(baseURL)
}
```

and now you see we have a server created in gin with respective handlers, and the AuthorizeHandler shall validate the token and check permission, here you can have two separate functions to do it but I created only one

now at a common place I have this functions defined

```
const (
	authHeader = "Authorization"
	permClaim  = "permissions"
)

var permission = "crud:admin-settings" //your permissions here
var auth0Domain = "dev-******.us.auth0.com" //your auth0 domain here
var audience = "http://auth0-local.com"

func AuthorizeHandler(permission string) gin.HandlerFunc {
	return func(c *gin.Context) {
		token, err := extractToken(c, auth0Domain)
		if err != nil {
			fmt.Println(err.Error())
		}
		if token == nil {
			fmt.Printf("failed to find token in context\n")
			c.JSON(http.StatusUnauthorized, "user hasn't logged in yet")
			c.Abort()
			return
		}
		if !tokenHasPermission(token, permission) {
			fmt.Printf("permission check failed\n")
			c.AbortWithStatusJSON(500, "error occurred when authorizing user")
			return
		}
		c.Next()
	}
}

func tokenHasPermission(token jwt.Token, permission string) bool {
	claims := token.PrivateClaims()
	tkPermissions, ok := claims[permClaim]
	if !ok {
		return false
	}
	tkPermList, ok := tkPermissions.([]interface{})
	if !ok {
		return false
	}
	for _, perm := range tkPermList {
		if perm == permission {
			return true
		}
	}
	return false
}

func extractToken(c *gin.Context, auth0Domain string) (jwt.Token, error) {
	// fetchTenantKeys fetch and parse the tenant JSON Web Keys (JWK). The keys
	// are used for JWT token validation during requests authorization.
	tenantKeys, err := jwk.Fetch(context.Background(),
		fmt.Sprintf("https://%s/.well-known/jwks.json", auth0Domain))
	if err != nil {
		log.Fatalf("failed to parse tenant json web keys: %s\n", err)
	}
	authorization := c.GetHeader(authHeader)
	if authorization == "" {
		return nil, errors.New("authorization header missing")
	}
	bearerAndToken := strings.Split(authorization, " ")
	if len(bearerAndToken) < 2 {
		return nil, errors.New("malformed authorization header: " + authorization)
	}
	token, err := jwt.Parse([]byte(bearerAndToken[1]), jwt.WithKeySet(tenantKeys),
		jwt.WithValidate(true), jwt.WithAudience(audience))
	if err != nil {
		return nil, err
	}
	return token, nil
}

```

and finally in my client I have this piece of code to get the token, basically it will open browser and goes to auth0 website and does the authentication and gives back the token which you can use for further rest API calls

```
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"

	cv "github.com/nirasan/go-oauth-pkce-code-verifier"
	"github.com/skratchdot/open-golang/open"
)

func main()  {
	//typically this values will come from config file
	AuthorizeUser("ghxc6OoNP**************HnLdqJN", "dev-*******.auth0.com","http://localhost:4242/auth")
}

// AuthorizeUser implements the PKCE OAuth2 flow.
func AuthorizeUser(clientID string, authDomain string, redirectURL string) {
	// initialize the code verifier
	var CodeVerifier, _ = cv.CreateCodeVerifier()

	// Create code_challenge with S256 method
	codeChallenge := CodeVerifier.CodeChallengeS256()

	// construct the authorization URL (with Auth0 as the authorization provider)
	authorizationURL := fmt.Sprintf(
		"https://%s/authorize?"+
			"&scope=openid"+
			"&response_type=code&client_id=%s"+
			"&code_challenge=%s"+
			"&code_challenge_method=S256&redirect_uri=%s",
		authDomain, clientID, codeChallenge, redirectURL)

	// start a web server to listen on a callback URL
	server := &http.Server{Addr: redirectURL}

	// define a handler that will get the authorization code, call the token endpoint, and close the HTTP server
	http.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
		// get the authorization code
		code := r.URL.Query().Get("code")
		if code == "" {
			fmt.Println("snap: Url Param 'code' is missing")
			io.WriteString(w, "Error: could not find 'code' URL parameter\n")

			// close the HTTP server and return
			cleanup(server)
			return
		}

		// trade the authorization code and the code verifier for an access token
		codeVerifier := CodeVerifier.String()
		token, err := getAccessToken(clientID, codeVerifier, code, redirectURL)
		if err != nil {
			fmt.Println("snap: could not get access token")
			io.WriteString(w, "Error: could not retrieve access token\n")

			// close the HTTP server and return
			cleanup(server)
			return
		}

		//you can see the token
		fmt.Println(token) 

		io.WriteString(w, `
		<html>
			<body>
				<h1>Login successful!</h1>
				<h2>You can close this window</h2>
			</body>
		</html>`)

		fmt.Println("Successfully logged into snapmaster API.")

		// close the HTTP server
		cleanup(server)
	})

	// parse the redirect URL for the port number
	u, err := url.Parse(redirectURL)
	if err != nil {
		fmt.Printf("snap: bad redirect URL: %s\n", err)
		os.Exit(1)
	}

	// set up a listener on the redirect port
	port := fmt.Sprintf(":%s", u.Port())
	l, err := net.Listen("tcp", port)
	if err != nil {
		fmt.Printf("snap: can't listen to port %s: %s\n", port, err)
		os.Exit(1)
	}

	// open a browser window to the authorizationURL
	err = open.Start(authorizationURL)
	if err != nil {
		fmt.Printf("snap: can't open browser to URL %s: %s\n", authorizationURL, err)
		os.Exit(1)
	}

	// start the blocking web server loop
	// this will exit when the handler gets fired and calls server.Close()
	server.Serve(l)
}

// getAccessToken trades the authorization code retrieved from the first OAuth2 leg for an access token
func getAccessToken(clientID string, codeVerifier string, authorizationCode string, callbackURL string) (string, error) {
	// set the url and form-encoded data for the POST to the access token endpoint
	fmt.Println("authorizationCode:", authorizationCode)
	url := "https://dev-*******.us.auth0.com/oauth/token" //your auth0 domain
	data := fmt.Sprintf(
		"grant_type=authorization_code&client_id=%s"+
			"&code_verifier=%s"+
			"&code=%s"+
			"&redirect_uri=%s",
		clientID, codeVerifier, authorizationCode, callbackURL)
	payload := strings.NewReader(data)

	// create the request and execute it
	req, _ := http.NewRequest("POST", url, payload)
	req.Header.Add("content-type", "application/x-www-form-urlencoded")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		fmt.Printf("snap: HTTP error: %s", err)
		return "", err
	}

	// process the response
	defer res.Body.Close()
	var responseData map[string]interface{}
	body, _ := ioutil.ReadAll(res.Body)

	// unmarshal the json into a string map
	err = json.Unmarshal(body, &responseData)
	if err != nil {
		fmt.Printf("snap: JSON error: %s", err)
		return "", err
	} 
	// retrieve the access token out of the map, and return to caller
	//accessToken := responseData["access_token"].(string)
	return "", nil
}

// cleanup closes the HTTP server
func cleanup(server *http.Server) {
	// we run this as a goroutine so that this function falls through and
	// the socket to the browser gets flushed/closed before the server goes away
	go server.Close()
}
```

hopefully this will help some one 

thanks for reading, pls comment if you have any questions, 
have a great day