all
This commit is contained in:
215
getway/main.go
Normal file
215
getway/main.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/google/go-github/v32/github"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/github"
|
||||
)
|
||||
|
||||
var dbConn *sql.DB
|
||||
var jwtSecret = []byte(os.Getenv("JWT_SECRET"))
|
||||
|
||||
func main() {
|
||||
initDB()
|
||||
|
||||
router := gin.Default()
|
||||
|
||||
router.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"http://localhost:8080"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"},
|
||||
AllowHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
||||
AllowCredentials: true,
|
||||
}))
|
||||
|
||||
router.GET("/auth/github", githubLoginHandler)
|
||||
router.GET("/auth/github/callback", githubCallbackHandler)
|
||||
|
||||
router.Run(":8000")
|
||||
}
|
||||
|
||||
func githubLoginHandler(c *gin.Context) {
|
||||
state := generateState()
|
||||
code := generateCode()
|
||||
|
||||
err := storeStateToDB(state, code)
|
||||
if err != nil {
|
||||
log.Println("Error storing state to DB:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "Internal server error"})
|
||||
return
|
||||
}
|
||||
|
||||
oauthConfig := &oauth2.Config{
|
||||
ClientID: os.Getenv("GITHUB_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
|
||||
RedirectURL: "http://localhost:8000/auth/github/callback",
|
||||
Scopes: []string{"user:email"},
|
||||
Endpoint: github.Endpoint,
|
||||
}
|
||||
|
||||
url := oauthConfig.AuthCodeURL(state)
|
||||
c.Redirect(http.StatusFound, url)
|
||||
}
|
||||
|
||||
func githubCallbackHandler(c *gin.Context) {
|
||||
state := c.Query("state")
|
||||
code := c.Query("code")
|
||||
|
||||
if !verifyState(state, code) {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "Invalid state"})
|
||||
return
|
||||
}
|
||||
|
||||
oauthConfig := &oauth2.Config{
|
||||
ClientID: os.Getenv("GITHUB_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
|
||||
RedirectURL: "http://localhost:8000/auth/github/callback",
|
||||
Scopes: []string{"user:email"},
|
||||
Endpoint: github.Endpoint,
|
||||
}
|
||||
|
||||
token, err := oauthConfig.Exchange(c.Request.Context(), code)
|
||||
if err != nil {
|
||||
log.Println("Error exchanging token:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "Internal server error"})
|
||||
return
|
||||
}
|
||||
|
||||
client := github.NewClient(oauthConfig.Client(c.Request.Context(), token))
|
||||
user, _, err := client.Users.Get(c.Request.Context(), "")
|
||||
if err != nil {
|
||||
log.Println("Error getting user:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "Internal server error"})
|
||||
return
|
||||
}
|
||||
|
||||
err = storeUserToDB(user)
|
||||
if err != nil {
|
||||
log.Println("Error storing user to DB:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "Internal server error"})
|
||||
return
|
||||
}
|
||||
|
||||
jwtToken, err := generateJWTToken(user.ID)
|
||||
if err != nil {
|
||||
log.Println("Error generating JWT token:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "Internal server error"})
|
||||
return
|
||||
}
|
||||
|
||||
c.SetCookie("token", jwtToken, 60*60*24, "/", "localhost", false, true)
|
||||
c.Redirect(http.StatusFound, "http://localhost:8080/#/")
|
||||
}
|
||||
|
||||
func initDB() {
|
||||
cfg, err := mysql.ParseDSN(os.Getenv("MYSQL_DSN"))
|
||||
if err != nil {
|
||||
log.Fatal("Error parsing MySQL DSN:", err)
|
||||
}
|
||||
|
||||
dbConn, err = sql.Open("mysql", cfg.FormatDSN())
|
||||
if err != nil {
|
||||
log.Fatal("Error opening database:", err)
|
||||
}
|
||||
|
||||
err = dbConn.Ping()
|
||||
if err != nil {
|
||||
log.Fatal("Error connecting to database:", err)
|
||||
}
|
||||
|
||||
log.Println("Database connection established")
|
||||
}
|
||||
|
||||
func generateState() string {
|
||||
return generateRandomString(20)
|
||||
}
|
||||
|
||||
func generateCode() string {
|
||||
return generateRandomString(20)
|
||||
}
|
||||
|
||||
func generateRandomString(length int) string {
|
||||
byteArr := make([]byte, length)
|
||||
_, err := rand.Read(byteArr)
|
||||
if err != nil {
|
||||
log.Fatal("Error generating random string:", err)
|
||||
}
|
||||
|
||||
return base64.URLEncoding.EncodeToString(byteArr)
|
||||
}
|
||||
|
||||
func storeStateToDB(state, code string) error {
|
||||
query := "INSERT INTO oauth_state (state, code) VALUES (?, ?)"
|
||||
stmt, err := dbConn.Prepare(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec(state, code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyState(state, code string) bool {
|
||||
query := "SELECT COUNT(*) FROM oauth_state WHERE state = ? AND code = ?"
|
||||
row := dbConn.QueryRow(query, state, code)
|
||||
|
||||
var count int
|
||||
err := row.Scan(&count)
|
||||
if err != nil {
|
||||
log.Println("Error verifying state:", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func storeUserToDB(user *github.User) error {
|
||||
query := "INSERT INTO users (id, login, email) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE login = VALUES(login), email = VALUES(email)"
|
||||
stmt, err := dbConn.Prepare(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec(user.GetID(), user.GetLogin(), user.GetEmail())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateJWTToken(userID int64) (string, error) {
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
claims["userID"] = userID
|
||||
claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
|
||||
|
||||
jwtToken, err := token.SignedString(jwtSecret)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return jwtToken, nil
|
||||
}
|
||||
Reference in New Issue
Block a user