generationtux/brizo

View on GitHub
app/handlers/web/auth.go

Summary

Maintainability
A
3 hrs
Test Coverage
package web

import (
    "log"
    "net/http"
    "strings"
    "time"

    "github.com/generationtux/brizo/auth"
    "github.com/generationtux/brizo/database"
    githuboauth "github.com/google/go-github/github"
    "github.com/mholt/binding"
    "golang.org/x/oauth2"
    "golang.org/x/oauth2/github"
)

var (
    oauthConf = &oauth2.Config{
        ClientID:     "",
        ClientSecret: "",
        Scopes:       []string{"user", "user:email", "repo"},
        Endpoint:     github.Endpoint,
    }
    csrfCookieName   = "brizo_csrf_token"
    oauthStateString = auth.GetOAuthStateString()
)

func setCsrfCookie(w http.ResponseWriter, r *http.Request) {
    isLocal := (strings.Contains(r.Host, "localhost") || strings.Contains(r.Host, "127.0.0.1"))
    csrfCookie := http.Cookie{
        Name:     csrfCookieName,
        Value:    oauthStateString,
        Path:     "/",
        HttpOnly: true,
        Secure:   !isLocal,
    }
    http.SetCookie(w, &csrfCookie)
}

func expireCsrfCookie(w http.ResponseWriter, r *http.Request) {
    isLocal := (strings.Contains(r.Host, "localhost") || strings.Contains(r.Host, "127.0.0.1"))
    csrfCookie := http.Cookie{
        Name:     csrfCookieName,
        Value:    "",
        Path:     "/",
        HttpOnly: true,
        Secure:   !isLocal,
        Expires:  time.Unix(0, 0),
    }
    http.SetCookie(w, &csrfCookie)
}

// AuthGithubHandler for requesting oauth access from Github
func AuthGithubHandler(w http.ResponseWriter, r *http.Request) {
    auth.HydrateOAuthConfig(oauthConf)
    setCsrfCookie(w, r)
    url := oauthConf.AuthCodeURL(oauthStateString, oauth2.AccessTypeOnline)
    http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}

func authErrorRedirect(w http.ResponseWriter, r *http.Request) {
    endpoint := "/app/login?err=1"
    http.Redirect(w, r, endpoint, http.StatusTemporaryRedirect)
}

func authDenyRedirect(w http.ResponseWriter, r *http.Request) {
    endpoint := "/app/login?err=2"
    http.Redirect(w, r, endpoint, http.StatusTemporaryRedirect)
}

// AuthGithubCallbackHandler for handling oauth access response from Github
// any errors here should return a redirect back to the apps login form to display the error
func AuthGithubCallbackHandler(w http.ResponseWriter, r *http.Request) {
    auth.HydrateOAuthConfig(oauthConf)
    oAuthCallbackForm := new(auth.OAuthCallbackForm)
    errs := binding.Bind(r, oAuthCallbackForm)
    if errs.Len() > 0 {
        authErrorRedirect(w, r)
        return
    }

    csrfCookie, err := r.Cookie(csrfCookieName)
    if err != nil {
        log.Println(err)
        authErrorRedirect(w, r)
        return
    }
    expireCsrfCookie(w, r)

    if oAuthCallbackForm.State != csrfCookie.Value {
        log.Printf("invalid oauth state, expected '%s', got '%s'\n", csrfCookie.Value, oAuthCallbackForm.State)
        authErrorRedirect(w, r)
        return
    }

    token, err := oauthConf.Exchange(oauth2.NoContext, oAuthCallbackForm.Code)
    if err != nil {
        log.Printf("oauthConf.Exchange() failed with '%s'\n", err)
        authErrorRedirect(w, r)
        return
    }

    oauthClient := oauthConf.Client(oauth2.NoContext, token)
    client := githuboauth.NewClient(oauthClient)
    user, _, err := client.Users.Get("")

    if err != nil {
        log.Println("unable to get user details")
        authErrorRedirect(w, r)
        return
    }

    emails, _, err := client.Users.ListEmails(&githuboauth.ListOptions{})
    var email string
    for _, userEmail := range emails {
        if *userEmail.Primary {
            email = *userEmail.Email
        }
    }

    if err != nil {
        log.Println("unable to get user email")
        authErrorRedirect(w, r)
        return
    }

    db, err := database.Connect()
    defer db.Close()
    if err != nil {
        log.Println(err)
        authErrorRedirect(w, r)
        return
    }

    var jwtToken string
    var jwtError error
    if auth.IsFirstUser(db) {
        brizoUser, err := auth.CreateNewGithubUser(db, user, email, token.AccessToken)

        if err != nil {
            log.Println(err)
            authErrorRedirect(w, r)
            return
        }

        jwtToken, jwtError = auth.CreateJWTToken(brizoUser)
    } else if auth.GithubUserAllowed(db, *user.Login) {
        brizoUser := auth.BuildUserFromGithubUser(user, email, token.AccessToken)
        success, err := auth.UpdateUser(db, &brizoUser)

        if success == false || err != nil {
            authErrorRedirect(w, r)
            return
        }
        jwtToken, jwtError = auth.CreateJWTToken(brizoUser)
    } else {
        // user is not allowed
        authDenyRedirect(w, r)
    }

    if jwtError != nil {
        log.Println(jwtError)
        authErrorRedirect(w, r)
    }

    http.Redirect(w, r, "/app/auth?token="+jwtToken, http.StatusTemporaryRedirect)
    return
}