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