Introduction to OAuth 2.0 and JWT in Go
Modern web applications and APIs require a reliable and secure authentication and authorization system. The two most popular standards in this field are OAuth 2.0 and JWT (JSON Web Tokens). In the Go language (Golang), implementing these mechanisms becomes especially effective thanks to built-in concurrency support, high performance, and a rich ecosystem of libraries.
In this article, we will thoroughly examine how to implement OAuth 2.0 and JWT authorization in Go. You will learn how to set up a server, generate and verify tokens, and protect your endpoints. The material will be useful for both beginner and experienced Go developers.
Basics of OAuth 2.0 and JWT
What is OAuth 2.0?
OAuth 2.0 is an authorization protocol that allows applications to obtain limited access to user resources without transmitting a password. Instead, access tokens are used. The main participants in the process are:
- Resource Owner — the user who owns the data
- Client — the application requesting access
- Authorization Server — the server that issues tokens
- Resource Server — the server that provides protected resources
What is JWT?
JWT (JSON Web Token) is a compact and self-contained format for transmitting data between parties as a JSON object. The token consists of three parts separated by dots:
header.payload.signatureAdvantages of JWT:
- Does not require storing sessions on the server (stateless)
- Contains all necessary information (claims)
- Signed for integrity verification
Implementing JWT Authorization in Go
To work with JWT in Go, we will use the popular library golang-jwt/jwt/v5. Install it with the command:
go get github.com/golang-jwt/jwt/v5Generating a JWT Token
Let's create a function to generate access and refresh tokens:
package auth
import ( "time" "github.com/golang-jwt/jwt/v5")
var jwtSecret = []byte("your-secret-key")
type Claims struct { UserID uint `json:"user_id"` Email string `json:"email"` jwt.RegisteredClaims}
func GenerateAccessToken(userID uint, email string) (string, error) { claims := &Claims{ UserID: userID, Email: email, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "myapp", }, }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(jwtSecret)}
func GenerateRefreshToken(userID uint) (string, error) { claims := &Claims{ UserID: userID, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), }, }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(jwtSecret)}Verifying a JWT Token
Middleware for verifying the token in HTTP requests:
package middleware
import ( "net/http" "strings" "github.com/gin-gonic/gin" "myapp/auth")
func AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "missing authorization header"}) c.Abort() return }
tokenString := strings.TrimPrefix(authHeader, "Bearer ") if tokenString == authHeader {