Implementing Google OTP-Based 2FA in a Golang Gin Framework System
Two-factor authentication (2FA) adds an extra layer of security to the authentication process by requiring users to provide two forms of identification. In this tutorial, we'll implement Google OTP-based 2FA in a Golang Gin framework system.
Prerequisites
Before we start, ensure you have Go installed on your machine. You can download it from the official Go website.
Step 1: Install Required Packages
First, install the necessary packages:
go get github.com/dgryski/dgoogauth
go get github.com/gin-gonic/gin
go get github.com/gin-contrib/sessions
go get github.com/gin-contrib/sessions/cookie
Step 2: Basic Setup
Set up a basic Gin application with session management.
package
mainimport
( "github.com/gin-gonic/gin"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
)func main()
{
r := gin.Default() store := cookie.NewStore([]byte("secret"
)) r.Use(sessions.Sessions("mysession"
, store)) r.GET("/login"
, showLoginPage) r.POST("/login"
, performLogin) r.GET("/2fa"
, show2FA) r.POST("/2fa"
, perform2FA) r.Run(":8080"
)
}
Step 3: Handling Login
Create the login endpoint that handles the initial username and password authentication.
func showLoginPage(c *gin.Context)
{ c.HTML(http.StatusOK, "login.html"
, gin.H{})
}func performLogin(c *gin.Context)
{ username := c.PostForm("username"
) password := c.PostForm("password"
) // Simple user authentication for demonstration purposes
{
if username == "user" && password == "password"
session := sessions.Default(c) session.Set("user"
, username)
session.Save() c.Redirect(http.StatusFound, "/2fa"
) return
} c.HTML(http.StatusUnauthorized, "login.html"
, gin.H{ "error": "Invalid username or password"
,
})
}
Step 4: Implementing 2FA
After successful login, redirect users to the 2FA page where they enter the OTP generated by Google Authenticator.
import
( "net/http"
"github.com/dgryski/dgoogauth"
)var
otpConfig = &dgoogauth.OTPConfig{ Secret: "your-secret-key"
, WindowSize: 3
, HotpCounter: 0
,
}func show2FA(c *gin.Context)
{ c.HTML(http.StatusOK, "2fa.html"
, gin.H{})
}func perform2FA(c *gin.Context)
{ code := c.PostForm("code"
)
ok, err := otpConfig.Authenticate(code) if err != nil
{ c.HTML(http.StatusInternalServerError, "2fa.html"
, gin.H{ "error": "Error validating OTP"
,
}) return
} if
ok {
session := sessions.Default(c) session.Set("authenticated", true
)
session.Save() c.Redirect(http.StatusFound, "/dashboard"
) return
} c.HTML(http.StatusUnauthorized, "2fa.html"
, gin.H{ "error": "Invalid OTP code"
,
})
}
Step 5: Creating Protected Routes
Ensure that users who have successfully passed 2FA can access protected routes.
func dashboard(c *gin.Context)
{
session := sessions.Default(c) authenticated := session.Get("authenticated"
) if authenticated == nil || !authenticated.(bool
) { c.Redirect(http.StatusFound, "/login"
) return
} c.HTML(http.StatusOK, "dashboard.html"
, gin.H{})
}
Final Steps
Create the necessary HTML files (login.html
, 2fa.html
, dashboard.html
) to render the pages. Here’s an example of what login.html
might look like:
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="username" placeholder="Username" required><br>
<input type="password" name="password" placeholder="Password" required><br>
<button type="submit">Login</button>
</form>
</body>
</html>
Similarly, create 2fa.html
and dashboard.html
to handle 2FA input and display the dashboard, respectively.
Sequence Diagram
Conclusion
In this tutorial, we implemented a simple two-factor authentication system using the Gin framework in Golang and Google OTP. While this example is simplified for demonstration purposes, in a production environment, ensure to use more secure practices, such as securely storing secrets and handling errors more robustly. Two-factor authentication significantly enhances security by ensuring that only users who possess the second factor (OTP) can gain access, thereby protecting against compromised credentials.