When developing a web application using GoFiber in Golang, sending a JSON Web Token (JWT) in the response headers and ensuring the client automatically includes it in subsequent requests as a Bearer
token is a common authentication pattern. This approach avoids the use of cookies, relying solely on headers for token management. While the browser doesn't automatically persist headers across requests like cookies, client-side JavaScript can be used to achieve the desired behavior.
The server-side implementation involves generating a JWT upon successful user authentication (e.g., login) and setting it in the Authorization
header of the response. Here's a detailed breakdown:
First, you need a function to generate the JWT. This function typically takes user-identifying information (e.g., user ID, username) and an expiration time as input. It uses a secret key to sign the token, ensuring its integrity. The github.com/golang-jwt/jwt/v5
library is commonly used for this purpose. Make sure to install it using:
go get github.com/golang-jwt/jwt/v5
Here's an example of a generateToken
function:
import (
"time"
"github.com/golang-jwt/jwt/v5"
)
var secretKey = []byte("your_secret_key") // Replace with a strong, randomly generated key
func generateToken(userID string) (string, error) {
claims := jwt.MapClaims{
"user_id": userID,
"exp": time.Now().Add(time.Hour * 1).Unix(), // Token expires in 1 hour
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(secretKey)
}
Important: Replace "your_secret_key"
with a strong, randomly generated secret key. Never hardcode this in production; use environment variables or a secure configuration management system.
The login handler is responsible for authenticating the user (e.g., verifying username and password) and, upon successful authentication, generating a JWT and setting it in the response header. Here's an example using GoFiber:
import (
"github.com/gofiber/fiber/v2"
)
func login(c *fiber.Ctx) error {
// Authentication logic here (e.g., verify username/password)
userID := "12345" // Replace with actual user ID
token, err := generateToken(userID)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Could not generate token")
}
c.Set("Authorization", "Bearer "+token) // Set JWT in Authorization header
return c.JSON(fiber.Map{
"message": "Login successful",
})
}
In this example, the c.Set("Authorization", "Bearer "+token)
line sets the JWT in the Authorization
header with the Bearer
scheme. This is the standard way to send JWTs in HTTP headers.
Here's how you would set up your GoFiber application to use the login handler:
func main() {
app := fiber.New()
app.Post("/login", login)
app.Listen(":3000")
}
For protected routes, you can use a JWT middleware to validate the token in the Authorization
header. This middleware will extract the token, verify its signature, and potentially attach user information to the context. Here's an example of a basic middleware:
import (
"strings"
jwtware "github.com/gofiber/contrib/jwt"
)
func JWTMiddleware(c *fiber.Ctx) error {
authHeader := c.Get("Authorization")
if authHeader == "" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Missing authorization token"})
}
token := strings.TrimPrefix(authHeader, "Bearer ")
claims, err := validateToken(token) // Implement validateToken function
if err != nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid token"})
}
c.Locals("user", claims) // Attach claims to context
return c.Next()
}
func validateToken(tokenString string) (jwt.MapClaims, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return secretKey, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims, nil
}
return nil, jwt.ErrTokenMalformed
}
You can then apply this middleware to protected routes:
app.Use(JWTMiddleware)
app.Get("/restricted", func(c *fiber.Ctx) error {
return c.SendString("You are authenticated and can access this route")
})
Alternatively, you can use the github.com/gofiber/contrib/jwt
middleware:
app.Use(jwtware.New(jwtware.Config{
SigningKey: secretKey,
}))
This middleware automatically handles token verification.
While the server sends the JWT in the response header, the browser doesn't automatically include it in subsequent requests. You need client-side JavaScript to handle this. Here's how:
After a successful login, you need to retrieve the JWT from the Authorization
header of the response. You can do this using the fetch
API or a library like Axios:
fetch('/login', {
method: 'POST',
// ... your login data ...
})
.then(response => {
if (!response.ok) {
throw new Error('Login failed');
}
return response.json();
})
.then(data => {
const token = response.headers.get('Authorization');
if (token) {
localStorage.setItem('jwt', token); // Store the JWT
// ... redirect to the appropriate page ...
}
})
.catch(error => {
console.error('Error during login:', error);
});
This code snippet retrieves the Authorization
header, extracts the JWT, and stores it in localStorage
. Note: For highly sensitive data, consider using sessionStorage
or a more secure storage mechanism instead of localStorage
.
For subsequent requests to protected routes, you need to include the JWT in the Authorization
header. Here's how you can do it:
fetch('/protected-route', {
headers: {
'Authorization': localStorage.getItem('jwt')
}
})
// ... rest of your fetch call ...
This code snippet retrieves the JWT from localStorage
and includes it in the Authorization
header of the request.
If you're using a library like Axios, you can configure it to automatically include the JWT in all requests. Here's an example:
import axios from 'axios';
axios.interceptors.request.use(config => {
const token = localStorage.getItem('jwt');
if (token) {
config.headers.Authorization = token;
}
return config;
});
// Example request
axios.get('/protected-route')
.then(response => {
// ... handle response ...
});
This code snippet uses an Axios interceptor to add the JWT to the Authorization
header of every request.
localStorage
for highly sensitive data.By implementing these steps, you can effectively send a JWT in the response headers and ensure that the client automatically includes it in subsequent requests, avoiding the use of cookies and providing a secure and seamless authentication experience.
This approach ensures that the client sets the Bearer
header automatically when making subsequent requests, without requiring any manual intervention from the user.
Additional resources: