How to Implement Golang JWT Authentication and Authorization

Auth0 go sdk quickstarts: login

Start by adding a go.mod file to list all the dependencies to be used.

We can now make the dependencies available for us by running the following shell command:

Create a .env file within the root of your project directory to store the app configuration, and fill in the
environment variables:

Create a file called auth.go in the platform/authenticator folder. In this package you’ll create a method to
configure and return OAuth2 and
oidc clients, and another one to verify an ID Token.

// platform/authenticator/auth.go

package authenticator

import (
	"context"
	"errors"
	"os"

	"github.com/coreos/go-oidc/v3/oidc"
	"golang.org/x/oauth2"
)

// Authenticator is used to authenticate our users.
type Authenticator struct {
	*oidc.Provider
	oauth2.Config
}

// New instantiates the *Authenticator.
func New() (*Authenticator, error) {
	provider, err := oidc.NewProvider(
		context.Background(),
		"https://" os.Getenv("AUTH0_DOMAIN") "/",
	)
	if err != nil {
		return nil, err
	}

	conf := oauth2.Config{
		ClientID:     os.Getenv("AUTH0_CLIENT_ID"),
		ClientSecret: os.Getenv("AUTH0_CLIENT_SECRET"),
		RedirectURL:  os.Getenv("AUTH0_CALLBACK_URL"),
		Endpoint:     provider.Endpoint(),
		Scopes:       []string{oidc.ScopeOpenID, "profile"},
	}

	return &Authenticator{
		Provider: provider,
		Config:   conf,
	}, nil
}

// VerifyIDToken verifies that an *oauth2.Token is a valid *oidc.IDToken.
func (a *Authenticator) VerifyIDToken(ctx context.Context, token *oauth2.Token) (*oidc.IDToken, error) {
	rawIDToken, ok := token.Extra("id_token").(string)
	if !ok {
		return nil, errors.New("no id_token field in oauth2 token")
	}

	oidcConfig := &oidc.Config{
		ClientID: a.ClientID,
	}

	return a.Verifier(oidcConfig).Verify(ctx, rawIDToken)
}

Create a file called router.go in the platform/router folder. In this package you’ll create a method to configure
and return our routes using github.com/gin-gonic/gin. You will be passing an
instance of Authenticator to the method, so it can be used within the login and callback handlers.

// platform/router/router.go

package router

import (
	"encoding/gob"
	"net/http"

	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"

	"01-Login/platform/authenticator"
	"01-Login/platform/middleware"
	"01-Login/web/app/callback"
	"01-Login/web/app/login"
	"01-Login/web/app/logout"
	"01-Login/web/app/user"
)

// New registers the routes and returns the router.
func New(auth *authenticator.Authenticator) *gin.Engine {
	router := gin.Default()

	// To store custom types in our cookies,
	// we must first register them using gob.Register
	gob.Register(map[string]interface{}{})

	store := cookie.NewStore([]byte("secret"))
	router.Use(sessions.Sessions("auth-session", store))

	router.Static("/public", "web/static")
	router.LoadHTMLGlob("web/template/*")

	router.GET("/", func(ctx *gin.Context) {
		ctx.HTML(http.StatusOK, "home.html", nil)
	})
	router.GET("/login", login.Handler(auth))
	router.GET("/callback", callback.Handler(auth))
	router.GET("/user", user.Handler)
	router.GET("/logout", logout.Handler)

	return router
}

Next, let’s create our application’s entry point main.go and wire everything up together:

Create a file called login.go in the web/app/login folder, and add a Handler function to handle the /login
route.

// web/app/login/login.go

package login

import (
	"crypto/rand"
	"encoding/base64"
	"net/http"

	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"

	"01-Login/platform/authenticator"
)

// Handler for our login.
func Handler(auth *authenticator.Authenticator) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		state, err := generateRandomState()
		if err != nil {
			ctx.String(http.StatusInternalServerError, err.Error())
			return
		}

		// Save the state inside the session.
		session := sessions.Default(ctx)
		session.Set("state", state)
		if err := session.Save(); err != nil {
			ctx.String(http.StatusInternalServerError, err.Error())
			return
		}

		ctx.Redirect(http.StatusTemporaryRedirect, auth.AuthCodeURL(state))
	}
}

func generateRandomState() (string, error) {
	b := make([]byte, 32)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}

	state := base64.StdEncoding.EncodeToString(b)

	return state, nil
}

Add a link to /login route in the home.html template.

Похожее:  PHP: rfc:password_hash

Once users have authenticated using Auth0’s Universal Login Page, they’ll return to the app at the /callback
route that will be handled in the following Handler function:

// web/app/callback/callback.go

package callback

import (
	"net/http"

	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"

	"01-Login/platform/authenticator"
)

// Handler for our callback.
func Handler(auth *authenticator.Authenticator) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		session := sessions.Default(ctx)
		if ctx.Query("state") != session.Get("state") {
			ctx.String(http.StatusBadRequest, "Invalid state parameter.")
			return
		}

		// Exchange an authorization code for a token.
		token, err := auth.Exchange(ctx.Request.Context(), ctx.Query("code"))
		if err != nil {
			ctx.String(http.StatusUnauthorized, "Failed to exchange an authorization code for a token.")
			return
		}

		idToken, err := auth.VerifyIDToken(ctx.Request.Context(), token)
		if err != nil {
			ctx.String(http.StatusInternalServerError, "Failed to verify ID Token.")
			return
		}

		var profile map[string]interface{}
		if err := idToken.Claims(&profile); err != nil {
			ctx.String(http.StatusInternalServerError, err.Error())
			return
		}

		session.Set("access_token", token.AccessToken)
		session.Set("profile", profile)
		if err := session.Save(); err != nil {
			ctx.String(http.StatusInternalServerError, err.Error())
			return
		}

		// Redirect to logged in page.
		ctx.Redirect(http.StatusTemporaryRedirect, "/user")
	}
}

You can access the user information via the profile you stored in the session previously.

For information about the userinfo hash, see User Profile.

To log the user out, clear the data from the session and redirect the user to the Auth0 logout endpoint. You can find
more information about this in the logout documentation.

Create a file called logout.go in the folder web/app/logout/logout.go, and add the function Handler to redirect
the user to Auth0’s logout endpoint.

Create a file called user.js in the folder web/static/js, and add the code to remove the cookie from a logged-in
user.

Create a middleware that will check if the user is authenticated or not based on the profile session key:

Finally, set up this middleware for any route that needs authentication by adding it to the router.

Configure oauth on google cloud platform

We first need to create a Project on Google Cloud Platform (GCP).

After the Project is created successfully, we need to proceed to OAuth Consent Screen to enter the information of our Golang web application, as shown in the following screenshot.

Setting up the OAuth consent screen for our Golang web application.

Connecting to database

The best practice would be to add the code related to the Database connection to your .env file but for simplicity purpose, I have implemented it in main.go itself.

As said before, I’ll be using the Postgres database. Add the following code to establish a database connection.

Похожее:  Secure Your Api Using Jwt in Golang · schadokar.dev

Create a directory

Create a directory called jwt-practice.

Create some structures

Let’s get our hands on to create some structs.

Exploring json web token

Under this section, we will comprehensively understand what is JWT, how does JSON Web token look like, and what JSON web token consists of.

Generate jwt

Write the following function to create Golang JWT:

The GenerateJWT() function takes email and role as input. Creates a token by HS256 signing method and adds authorized email, role, and exp into claims. Claims are pieces of information added into tokens.

Handlers

We will then have the following three new handlers needed in server.go.

mux.HandleFunc("/login", handleRequestWithLog(handleLoginRequest))
mux.HandleFunc("/logout", handleRequestWithLog(handleLogoutRequest))
mux.HandleFunc("/oauth2callback", handleRequestWithLog(oauthCallbackHandler))

Handling post authentication routes

Now that all logged in clients have session information stored on their end as cookies, we can use it to:

Let’s write our Welcome handler to do just that:

How does a json web token look like?

The above token is invalid. It cannot be used for production.

How the jwt signature works

So if the header and signature of a JWT can be accessed by anyone, what actually makes a JWT secure? The answer lies in how the third portion (the signature) is generated.

Implementation in go

Now that we’ve seen how JWT based authentication works, let’s implement it using Go.

Implementing golang jwt authentication and authorization

Follow these steps for Golang JWT Authentication and Authorization-

Planning the structure#

In this tutorial we’ll write code in one file. In production you would want to split this into multiple files.

Let’s start with a basic go web app structure:

Now we’ll set up a simple site:

We will also need:

  • The home page, where we will click the login button from.
  • The page handling redirection to the google service.
  • The callback page handling the information we get from Google.

So let’s set up the base structure for that:

Running our application

To run this application, build and run the Go binary:

Safely storing the client id and secret#

There are many ways to safely store the client ID and secret. In production you should make sure that the client secret remains secret.

Похожее:  Http ладошки дети личный кабинет

In this tutorial we won’t cover this. Instead, we will store those variables as system environment variables. Now:

  • Create an environment variable called googlekey holding your client ID.
  • Create an environment variable called googlesecret holding your client secret.

Verifying a jwt

To verify a JWT, the server generates the signature once again using the header and payload from the incoming JWT, and its secret key. If the newly generated signature matches the one on the JWT, then the JWT is considered valid.

Now, if you are someone trying to issue a fake token, you can easily generate the header and payload, but without knowing the key, there is no way to generate a valid signature. If you try to tamper with the existing payload of a valid JWT, the signatures will no longer match.

What comprises a json web token?

A JSON Web Token consists of three parts which are separated using .(dot) :

Writing the application logic#

Before starting remember to import the golang.org/x/oauth2 package.To begin with, let’s write the home page handler:

Next we need to create a variable we’ll use for storing data and communicating with Google and the random state variable:

Conclusion

I hope this blog has helped you with Golang JWT Authentication and Authorization. The process of authentication and authorization is crucial step for developing any web application. If you are looking for a helping hand to implement Golang JWT, then hire Golang developer to leverage our top-of-the-line Golang development expertise.

Initializing with go.mod

Initialize it with go.mod, for dependency management, using –

Downloading dependencies

Next, we will download the required dependencies. We will use

Conclusion#

That’s all we have to do to integrate OAuth2 into our Golang application. I hope that I helped someone with this problems as I really couldn’t find beginner-suited, detailed resources about OAuth2 in Go.

Now go and build something amazing!

Create router and initialize the routes

In this step, we will create a router and initialize routes. Add this code in your main.go

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (1 оценок, среднее: 4,00 из 5)
Загрузка...

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *