Skip to content

Commit

Permalink
add github OAuth pkg & controllers
Browse files Browse the repository at this point in the history
  • Loading branch information
Clivern committed Oct 13, 2018
1 parent 0b154d2 commit 8cbf333
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 27 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,19 @@ Then add your `app_mode`, `app_port`, `app_log_level`, `github_token`, `github_w
"github_token": "...",
"github_webhook_secret": "...",
"repository_author": "Clivern",
"repository_name": "Hamster"
"repository_name": "Hamster",

"app_domain": "example.com",
"github_app_client_id": "..",
"github_app_redirect_uri": "..",
"github_app_allow_signup": "false",
"github_app_scope": "..",
"github_app_client_secret": "..",
}
```

You can config `app_domain` and the rest of github app configs `github_app_*` in case you need a github app not a personal bot.

Add a new webhook from `Settings > Webhooks`, Set the `Payload URL` to be `https://hamster.com/listen`, `Content type` as `JSON` and Add Your Webhook Secret.

And then run the application
Expand Down
8 changes: 7 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,11 @@
"github_token": "..",
"github_webhook_secret": "..",
"repository_author": "..",
"repository_name": ".."
"repository_name": "..",
"app_domain": "example.com",
"github_app_client_id": "..",
"github_app_redirect_uri": "..",
"github_app_allow_signup": "false",
"github_app_scope": "..",
"github_app_client_secret": "..",
}
8 changes: 7 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@ services:
- RepositoryName=Hamster
- AppMode=prod
- AppPort=8080
- AppLogLevel=info
- AppLogLevel=info
- GithubAppClientID=ValueHere
- GithubAppRedirectURI=ValueHere
- GithubAppAllowSignup=false
- GithubAppScope=ValueHere
- GithubAppClientSecret=ValueHere
- AppDomain=example.com
36 changes: 33 additions & 3 deletions internal/app/controller/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,41 @@
package controller

import (
"github.com/clivern/hamster/pkg"
"github.com/gin-gonic/gin"
"os"
)

func Auth(c *gin.Context) {
c.JSON(200, gin.H{
"status": "ok",
})

githubOauth := &pkg.GithubOAuthApp{
ClientID: os.Getenv("GithubAppClientID"),
RedirectURI: os.Getenv("GithubAppRedirectURI"),
AllowSignup: os.Getenv("GithubAppAllowSignup"),
Scope: os.Getenv("GithubAppScope"),
ClientSecret: os.Getenv("GithubAppClientSecret"),
}

state, err := c.Cookie("gh_oauth_state")

if err == nil && state != "" {
githubOauth.SetState(state)
}

ok, err := githubOauth.FetchAccessToken(
c.DefaultQuery("code", ""),
c.DefaultQuery("state", ""),
)

if ok && err == nil {
c.JSON(200, gin.H{
"status": "ok",
"accessToken": githubOauth.GetAccessToken(),
})
} else {
c.JSON(200, gin.H{
"status": "not ok",
"error": err.Error(),
})
}
}
25 changes: 16 additions & 9 deletions internal/app/controller/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,27 @@ import (
"github.com/clivern/hamster/pkg"
"github.com/gin-gonic/gin"
"net/http"
"os"
)

func Login(c *gin.Context) {

githubOauth := &pkg.GithubOAuthApp{
ClientID: "ClientID",
RedirectURI: "RedirectURI",
Scope: "Scope",
State: "State",
AllowSignup: "AllowSignup",
ClientID: os.Getenv("GithubAppClientID"),
RedirectURI: os.Getenv("GithubAppRedirectURI"),
AllowSignup: os.Getenv("GithubAppAllowSignup"),
Scope: os.Getenv("GithubAppScope"),
ClientSecret: os.Getenv("GithubAppClientSecret"),
}

state, err := c.Cookie("gh_oauth_state")

if err != nil || state == "" {
githubOauth.GenerateState()
c.SetCookie("gh_oauth_state", githubOauth.GetState(), 3600, "/", os.Getenv("AppDomain"), true, true)
} else {
githubOauth.SetState(state)
}
githubOauth.AddScope("scope1")
githubOauth.AddScope("scope2")
githubOauth.AddScope("scope3")
githubOauth.GenerateState()

c.HTML(http.StatusOK, "login.tmpl", gin.H{
"title": "Hamster",
Expand Down
26 changes: 19 additions & 7 deletions pkg/configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@ import (
)

type Config struct {
GithubToken string `json:"github_token"`
GithubWebhookSecret string `json:"github_webhook_secret"`
RepositoryAuthor string `json:"repository_author"`
RepositoryName string `json:"repository_name"`
AppMode string `json:"app_mode"`
AppPort string `json:"app_port"`
AppLogLevel string `json:"app_log_level"`
GithubToken string `json:"github_token"`
GithubWebhookSecret string `json:"github_webhook_secret"`
RepositoryAuthor string `json:"repository_author"`
RepositoryName string `json:"repository_name"`
AppMode string `json:"app_mode"`
AppPort string `json:"app_port"`
AppLogLevel string `json:"app_log_level"`
AppDomain string `json:"app_domain"`
GithubAppClientID string `json:"github_app_client_id"`
GithubAppRedirectURI string `json:"github_app_redirect_uri"`
GithubAppAllowSignup string `json:"github_app_allow_signup"`
GithubAppScope string `json:"github_app_scope"`
GithubAppClientSecret string `json:"github_app_client_secret"`
}

func (e *Config) Load(file string) (bool, error) {
Expand Down Expand Up @@ -53,6 +59,12 @@ func (e *Config) Cache() {
os.Setenv("AppMode", e.AppMode)
os.Setenv("AppLogLevel", e.AppLogLevel)
os.Setenv("AppPort", e.AppPort)
os.Setenv("GithubAppClientID", e.GithubAppClientID)
os.Setenv("GithubAppRedirectURI", e.GithubAppRedirectURI)
os.Setenv("GithubAppAllowSignup", e.GithubAppAllowSignup)
os.Setenv("GithubAppScope", e.GithubAppScope)
os.Setenv("GithubAppClientSecret", e.GithubAppClientSecret)
os.Setenv("AppDomain", e.AppDomain)
}
}

Expand Down
69 changes: 64 additions & 5 deletions pkg/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ package pkg
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
)

Expand All @@ -24,10 +27,11 @@ type GithubOAuthApp struct {
State string `json:"state"`
AllowSignup string `json:"allow_signup"`
ClientSecret string `json:"client_secret"`
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
}

type GithubOAuthClient struct {
Code string `json:"code"`
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
}
Expand All @@ -43,6 +47,10 @@ func (e *GithubOAuthApp) GetState() string {
return e.State
}

func (e *GithubOAuthApp) SetState(state string) {
e.State = state
}

func (e *GithubOAuthApp) AddScope(scope string) {
e.Scopes = append(e.Scopes, scope)
e.Scope = strings.Join(e.Scopes, ",")
Expand Down Expand Up @@ -75,13 +83,64 @@ func (e *GithubOAuthApp) RandomString(len int) (string, error) {
return hex.EncodeToString(bytes), nil
}

func (e *GithubOAuthClient) FetchAccessToken(code string, incomingState string, originalState string) (bool, error) {
if incomingState != originalState {
return false, fmt.Errorf("Invalid state provided %s, original one is %s", incomingState, originalState)
func (e *GithubOAuthApp) FetchAccessToken(code string, state string) (bool, error) {

githubOAuthClient := &GithubOAuthClient{}

if state != e.State {
return false, fmt.Errorf(
"Invalid state provided %s, original one is %s",
state,
e.State,
)
}

url := fmt.Sprintf(
"%s?client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&state=%s",
OAuthAccessToken,
e.ClientID,
e.ClientSecret,
code,
e.RedirectURI,
e.State,
)
client := &http.Client{}
req, err := http.NewRequest("POST", url, nil)

if err != nil {
return false, err
}

resp, err := client.Do(req)

if err != nil {
return false, err
}

defer resp.Body.Close()

bodyByte, err := ioutil.ReadAll(resp.Body)

if err != nil {
return false, err
}

err = json.Unmarshal(bodyByte, &githubOAuthClient)

if err != nil {
return false, err
}

e.AccessToken = githubOAuthClient.AccessToken
e.TokenType = githubOAuthClient.TokenType

return true, nil
}

func (e *GithubOAuthClient) GetAccessToken() string {
func (e *GithubOAuthApp) GetAccessToken() string {
return e.AccessToken
}

func (e *GithubOAuthApp) GetTokenType() string {
return e.TokenType
}

0 comments on commit 8cbf333

Please sign in to comment.