From 6fcd70bf808ddf6cdac348438dd3738a0a5b4d4a Mon Sep 17 00:00:00 2001
From: vx3r <vx3r@127-0-0-1.fr>
Date: Thu, 30 Apr 2020 18:14:46 +0900
Subject: [PATCH] implement github oauth, set demo to github auth

---
 .env                   |   4 +-
 auth/auth.go           |  34 ++++++++++++++
 auth/github/github.go  | 101 +++++++++++++++++++++++++++++++++++++++++
 cmd/wg-gen-web/main.go |  50 +++++---------------
 go.mod                 |   6 ---
 5 files changed, 149 insertions(+), 46 deletions(-)

diff --git a/.env b/.env
index d87d58d..5d4e2bc 100644
--- a/.env
+++ b/.env
@@ -32,10 +32,10 @@ OAUTH2_REDIRECT_URL=
 
 # example with github
 OAUTH2_PROVIDER_NAME=github
-OAUTH2_PROVIDER=
+OAUTH2_PROVIDER=https://github.com
 OAUTH2_CLIENT_ID=
 OAUTH2_CLIENT_SECRET=
-OAUTH2_REDIRECT_URL=
+OAUTH2_REDIRECT_URL=https://wg-gen-web-demo.127-0-0-1.fr
 
 # set provider name to fake to disable auth, also the default
 OAUTH2_PROVIDER_NAME=fake
diff --git a/auth/auth.go b/auth/auth.go
index d4dadae..f8d3e5d 100644
--- a/auth/auth.go
+++ b/auth/auth.go
@@ -1,8 +1,14 @@
 package auth
 
 import (
+	"fmt"
+	log "github.com/sirupsen/logrus"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth/fake"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth/github"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth/oauth2oidc"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
 	"golang.org/x/oauth2"
+	"os"
 )
 
 type Auth interface {
@@ -11,3 +17,31 @@ type Auth interface {
 	Exchange(code string) (*oauth2.Token, error)
 	UserInfo(oauth2Token *oauth2.Token) (*model.User, error)
 }
+
+func GetAuthProvider() (Auth, error) {
+	var oauth2Client Auth
+	var err error
+
+	switch os.Getenv("OAUTH2_PROVIDER_NAME") {
+	case "fake":
+		log.Warn("Oauth is set to fake, no actual authentication will be performed")
+		oauth2Client = &fake.Fake{}
+
+	case "oauth2oidc":
+		log.Warn("Oauth is set to oauth2oidc, must be RFC implementation on server side")
+		oauth2Client = &oauth2oidc.Oauth2idc{}
+
+	case "github":
+		log.Warn("Oauth is set to github, no openid will be used")
+		oauth2Client = &github.Github{}
+
+	case "google":
+		return nil, fmt.Errorf("auth provider name %s not yet implemented", os.Getenv("OAUTH2_PROVIDER_NAME"))
+	default:
+		return nil, fmt.Errorf("auth provider name %s unknown", os.Getenv("OAUTH2_PROVIDER_NAME"))
+	}
+
+	err = oauth2Client.Setup()
+
+	return oauth2Client, err
+}
diff --git a/auth/github/github.go b/auth/github/github.go
index d2e73c2..5e16b21 100644
--- a/auth/github/github.go
+++ b/auth/github/github.go
@@ -1 +1,102 @@
 package github
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
+	"golang.org/x/oauth2"
+	oauth2Github "golang.org/x/oauth2/github"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"time"
+)
+
+type Github struct{}
+
+var (
+	oauth2Config *oauth2.Config
+)
+
+// Setup validate provider
+func (o *Github) Setup() error {
+	oauth2Config = &oauth2.Config{
+		ClientID:     os.Getenv("OAUTH2_CLIENT_ID"),
+		ClientSecret: os.Getenv("OAUTH2_CLIENT_SECRET"),
+		RedirectURL:  os.Getenv("OAUTH2_REDIRECT_URL"),
+		Scopes:       []string{"user"},
+		Endpoint:     oauth2Github.Endpoint,
+	}
+
+	return nil
+}
+
+// CodeUrl get url to redirect client for auth
+func (o *Github) CodeUrl(state string) string {
+	return oauth2Config.AuthCodeURL(state)
+}
+
+// Exchange exchange code for Oauth2 token
+func (o *Github) Exchange(code string) (*oauth2.Token, error) {
+	oauth2Token, err := oauth2Config.Exchange(context.TODO(), code)
+	if err != nil {
+		return nil, err
+	}
+
+	return oauth2Token, nil
+}
+
+// UserInfo get token user
+func (o *Github) UserInfo(oauth2Token *oauth2.Token) (*model.User, error) {
+	// https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
+
+	// we have the token, lets get user information
+	req, err := http.NewRequest("GET", "https://api.github.com/user", nil)
+	if err != nil {
+		return nil, err
+	}
+
+	req.Header.Set("Authorization", fmt.Sprintf("token %s", oauth2Token.AccessToken))
+	client := &http.Client{}
+	resp, err := client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+
+	if resp.StatusCode != 200 {
+		return nil, fmt.Errorf("http status %s expect 200 OK", resp.Status)
+	}
+
+	bodyBytes, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	var data map[string]interface{}
+	err = json.Unmarshal(bodyBytes, &data)
+	if err != nil {
+		return nil, err
+	}
+
+	// get some infos about user
+	user := &model.User{}
+
+	if val, ok := data["name"]; ok && val != nil {
+		user.Name = val.(string)
+	}
+	if val, ok := data["email"]; ok && val != nil {
+		user.Email = val.(string)
+	}
+	if val, ok := data["html_url"]; ok && val != nil {
+		user.Profile = val.(string)
+	}
+
+	// openid specific
+	user.Sub = "github is not an openid provider"
+	user.Issuer = "https://github.com"
+	user.IssuedAt = time.Now().UTC()
+
+	return user, nil
+}
diff --git a/cmd/wg-gen-web/main.go b/cmd/wg-gen-web/main.go
index 1422208..6a79f7b 100644
--- a/cmd/wg-gen-web/main.go
+++ b/cmd/wg-gen-web/main.go
@@ -11,8 +11,6 @@ import (
 	log "github.com/sirupsen/logrus"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth"
-	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth/fake"
-	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth/oauth2oidc"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/core"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/util"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/version"
@@ -20,6 +18,7 @@ import (
 	"net/http"
 	"os"
 	"path/filepath"
+	"strings"
 	"time"
 )
 
@@ -106,45 +105,14 @@ func main() {
 	// serve static files
 	app.Use(static.Serve("/", static.LocalFile("./ui/dist", false)))
 
-	// setup Oauth2 client if enabled
-	var oauth2Client auth.Auth
-
-	switch os.Getenv("OAUTH2_PROVIDER_NAME") {
-	case "fake":
-		log.Warn("Oauth is set to fake, no actual authentication will be performed")
-		oauth2Client = &fake.Fake{}
-		err = oauth2Client.Setup()
-		if err != nil {
-			log.WithFields(log.Fields{
-				"OAUTH2_PROVIDER_NAME": "oauth2oidc",
-				"err":                  err,
-			}).Fatal("failed to setup Oauth2")
-		}
-	case "oauth2oidc":
-		log.Warn("Oauth is set to oauth2oidc, must be RFC implementation on server side")
-		oauth2Client = &oauth2oidc.Oauth2idc{}
-		err = oauth2Client.Setup()
-		if err != nil {
-			log.WithFields(log.Fields{
-				"OAUTH2_PROVIDER_NAME": "oauth2oidc",
-				"err":                  err,
-			}).Fatal("failed to setup Oauth2")
-		}
-	default:
+	// setup Oauth2 client
+	oauth2Client, err := auth.GetAuthProvider()
+	if err != nil {
 		log.WithFields(log.Fields{
-			"OAUTH2_PROVIDER_NAME": os.Getenv("OAUTH2_PROVIDER_NAME"),
-		}).Fatal("auth provider name unknown")
+			"err": err,
+		}).Fatal("failed to setup Oauth2")
 	}
 
-	if os.Getenv("OAUTH2_ENABLE") == "true" {
-		oauth2Client = &oauth2oidc.Oauth2idc{}
-		err = oauth2Client.Setup()
-		if err != nil {
-			log.WithFields(log.Fields{
-				"err": err,
-			}).Fatal("failed to setup Oauth2")
-		}
-	}
 	app.Use(func(ctx *gin.Context) {
 		ctx.Set("oauth2Client", oauth2Client)
 		ctx.Next()
@@ -167,6 +135,12 @@ func main() {
 			return
 		}
 
+		// avoid 401 page for refresh after logout
+		if !strings.Contains(c.Request.URL.Path, "/api/") {
+			c.Redirect(301, "/index.html")
+			return
+		}
+
 		c.AbortWithStatus(http.StatusUnauthorized)
 		return
 	})
diff --git a/go.mod b/go.mod
index 1d31fa6..1aba240 100644
--- a/go.mod
+++ b/go.mod
@@ -3,17 +3,11 @@ module gitlab.127-0-0-1.fr/vx3r/wg-gen-web
 go 1.14
 
 require (
-	github.com/appleboy/gin-jwt/v2 v2.6.3 // indirect
-	github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
 	github.com/coreos/go-oidc v2.2.1+incompatible
 	github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e
 	github.com/gin-contrib/cors v1.3.1
-	github.com/gin-contrib/sessions v0.0.3
 	github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
-	github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607 // indirect
 	github.com/gin-gonic/gin v1.6.2
-	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
-	github.com/gorilla/sessions v1.2.0 // indirect
 	github.com/joho/godotenv v1.3.0
 	github.com/patrickmn/go-cache v2.1.0+incompatible
 	github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
-- 
GitLab