mirror of
https://github.com/khairul169/vaulterm.git
synced 2026-09-15 17:03:30 +07:00
feat: add github oauth
This commit is contained in:
+2
-6
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/joho/godotenv"
|
||||
"rul.sh/vaulterm/server/app/auth"
|
||||
"rul.sh/vaulterm/server/app/server"
|
||||
"rul.sh/vaulterm/server/db"
|
||||
"rul.sh/vaulterm/server/middleware"
|
||||
"rul.sh/vaulterm/server/utils"
|
||||
@@ -31,12 +32,7 @@ func NewApp() *fiber.App {
|
||||
app.Use(cors.New())
|
||||
|
||||
// Server info
|
||||
app.Get("/server", func(c *fiber.Ctx) error {
|
||||
return c.JSON(&fiber.Map{
|
||||
"name": "Vaulterm",
|
||||
"version": "0.0.1",
|
||||
})
|
||||
})
|
||||
server.Router(app)
|
||||
|
||||
// Health check
|
||||
app.Get("/health-check", func(c *fiber.Ctx) error {
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/github"
|
||||
"gorm.io/gorm"
|
||||
"rul.sh/vaulterm/server/models"
|
||||
"rul.sh/vaulterm/server/utils"
|
||||
)
|
||||
|
||||
var config *oauth2.Config
|
||||
|
||||
func getGithubConfig() *oauth2.Config {
|
||||
if config != nil {
|
||||
return config
|
||||
}
|
||||
|
||||
config = &oauth2.Config{
|
||||
ClientID: os.Getenv("GITHUB_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
|
||||
Endpoint: github.Endpoint,
|
||||
RedirectURL: "http://localhost:3000/auth/oauth/github/callback",
|
||||
Scopes: []string{"read:user"},
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func githubRedir(c *fiber.Ctx) error {
|
||||
// Redirect to GitHub login page
|
||||
url := getGithubConfig().AuthCodeURL("state", oauth2.AccessTypeOffline)
|
||||
return c.Redirect(url)
|
||||
}
|
||||
|
||||
func githubCallback(c *fiber.Ctx) error {
|
||||
code := c.Query("code")
|
||||
if code == "" {
|
||||
return c.Status(fiber.StatusBadRequest).SendString("Missing code")
|
||||
}
|
||||
|
||||
// Exchange code for a token
|
||||
cfg := getGithubConfig()
|
||||
token, err := cfg.Exchange(c.Context(), code)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).SendString("Failed to exchange token")
|
||||
}
|
||||
|
||||
// Retrieve user info
|
||||
client := cfg.Client(c.Context(), token)
|
||||
resp, err := client.Get("https://api.github.com/user")
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).SendString("Failed to get user info")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode != 200 {
|
||||
return c.Status(fiber.StatusInternalServerError).
|
||||
SendString(fmt.Sprintf("GitHub API error: %s", string(body)))
|
||||
}
|
||||
|
||||
// Parse user info
|
||||
var user struct {
|
||||
Login string `json:"login"`
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &user); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).SendString("Failed to parse user info")
|
||||
}
|
||||
|
||||
repo := NewRepository()
|
||||
accountId := strconv.Itoa(user.ID)
|
||||
userAccount, err := repo.FindUserAccount("github", accountId)
|
||||
|
||||
// Register the user if the account not yet registered
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
acc := models.UserAccount{
|
||||
Type: "github",
|
||||
AccountID: accountId,
|
||||
Username: user.Login,
|
||||
Email: user.Email,
|
||||
}
|
||||
user := models.User{
|
||||
Name: user.Name,
|
||||
Role: models.UserRoleUser,
|
||||
Image: user.AvatarURL,
|
||||
Accounts: []*models.UserAccount{&acc},
|
||||
}
|
||||
|
||||
sessionId, err := repo.CreateUser(&user)
|
||||
if err != nil {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"user": user,
|
||||
"sessionId": sessionId,
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
}
|
||||
sessionId, err := repo.CreateUserSession(&userAccount.User)
|
||||
if err != nil {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"user": userAccount.User,
|
||||
"sessionId": sessionId,
|
||||
})
|
||||
}
|
||||
@@ -23,6 +23,12 @@ func (r *Auth) FindUser(username string, email string) (*models.User, error) {
|
||||
return &user, ret.Error
|
||||
}
|
||||
|
||||
func (r *Auth) FindUserAccount(accountType string, accountId string) (*models.UserAccount, error) {
|
||||
var user models.UserAccount
|
||||
ret := r.db.Where("type = ? AND account_id = ?", accountType, accountId).Joins("User").First(&user)
|
||||
return &user, ret.Error
|
||||
}
|
||||
|
||||
func (r *Auth) CreateUserSession(user *models.User) (string, error) {
|
||||
sessionId, err := lib.GenerateSessionID(20)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,6 +15,10 @@ func Router(app *fiber.App) {
|
||||
router.Get("/user", middleware.Protected(), getUser)
|
||||
router.Post("/register", register)
|
||||
router.Post("/logout", middleware.Protected(), logout)
|
||||
|
||||
oauth := router.Group("/oauth")
|
||||
oauth.Get("/github", githubRedir)
|
||||
oauth.Get("/github/callback", githubCallback)
|
||||
}
|
||||
|
||||
func login(c *fiber.Ctx) error {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func Router(app fiber.Router) {
|
||||
router := app.Group("/server")
|
||||
|
||||
router.Get("/", getServerInfo)
|
||||
router.Get("/config", getConfig)
|
||||
}
|
||||
|
||||
func getServerInfo(c *fiber.Ctx) error {
|
||||
return c.JSON(&fiber.Map{
|
||||
"name": "Vaulterm",
|
||||
"version": "0.0.1",
|
||||
})
|
||||
}
|
||||
|
||||
func getConfig(c *fiber.Ctx) error {
|
||||
config := fiber.Map{
|
||||
"oauth": "github",
|
||||
"github_client_id": os.Getenv("GITHUB_CLIENT_ID"),
|
||||
}
|
||||
return c.JSON(config)
|
||||
}
|
||||
Reference in New Issue
Block a user