Store OAuth token in a session cookie.

Not the final design (it'll instead be persisted to the App Engine data store,
and the session will just have the user ID), but this has most of the
scaffolding for session cookies.
This commit is contained in:
Mihai Parparita 2014-07-26 15:34:47 -07:00
parent df3c31c1d0
commit 2391a279f9
4 changed files with 92 additions and 10 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
app/config/*oauth*.json app/config/*oauth*.json
app/config/session.json

View file

@ -0,0 +1,6 @@
{
"AuthenticationKey": "REPLACE_ME_WITH_A_32_BYTE_BASE_64_ENCODED_KEY_BYES",
"EncryptionKey": "REPLACE_ME_WITH_A_32_BYTE_BASE_64_ENCODED_KEY_BYTES",
"CookieName": "session",
"TokenKey": "token_temp"
}

View file

@ -10,7 +10,6 @@ import (
"log" "log"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url"
"sort" "sort"
"strings" "strings"
"time" "time"
@ -22,10 +21,20 @@ import (
"code.google.com/p/goauth2/oauth" "code.google.com/p/goauth2/oauth"
"github.com/google/go-github/github" "github.com/google/go-github/github"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gorilla/sessions"
) )
var router *mux.Router var router *mux.Router
var githubOauthConfig oauth.Config var githubOauthConfig oauth.Config
var sessionStore *sessions.CookieStore
var sessionConfig SessionConfig
type SessionConfig struct {
AuthenticationKey string
EncryptionKey string
CookieName string
TokenKey string
}
type RepoDigest struct { type RepoDigest struct {
Repo *github.Repository Repo *github.Repository
@ -82,6 +91,32 @@ func (digest *Digest) Fetch(repos []github.Repository, githubClient *github.Clie
return nil return nil
} }
func initSessionConfig() {
configBytes, err := ioutil.ReadFile("config/session.json")
if err != nil {
log.Panicf("Could not read session config: %s", err.Error())
}
err = json.Unmarshal(configBytes, &sessionConfig)
if err != nil {
log.Panicf("Could not parse session config %s: %s", configBytes, err.Error())
}
authenticationKey, err := base64.StdEncoding.DecodeString(sessionConfig.AuthenticationKey)
if err != nil {
log.Panicf("Could not decode session config authentication key %s: %s", sessionConfig.AuthenticationKey, err.Error())
}
encryptionKey, err := base64.StdEncoding.DecodeString(sessionConfig.EncryptionKey)
if err != nil {
log.Panicf("Could not decode session config encryption key %s: %s", sessionConfig.EncryptionKey, err.Error())
}
sessionStore = sessions.NewCookieStore(authenticationKey, encryptionKey)
sessionStore.Options.Path = "/"
sessionStore.Options.MaxAge = 86400 * 30
sessionStore.Options.HttpOnly = true
sessionStore.Options.Secure = !appengine.IsDevAppServer()
}
func initGithubOAuthConfig() { func initGithubOAuthConfig() {
path := "config/github-oauth" path := "config/github-oauth"
if appengine.IsDevAppServer() { if appengine.IsDevAppServer() {
@ -94,7 +129,7 @@ func initGithubOAuthConfig() {
} }
err = json.Unmarshal(configBytes, &githubOauthConfig) err = json.Unmarshal(configBytes, &githubOauthConfig)
if err != nil { if err != nil {
log.Panicf("Could not parse GitHut OAuth %s", err.Error()) log.Panicf("Could not parse GitHub OAuth config %s: %s", configBytes, err.Error())
} }
githubOauthConfig.Scope = "repo" githubOauthConfig.Scope = "repo"
githubOauthConfig.AuthURL = "https://github.com/login/oauth/authorize" githubOauthConfig.AuthURL = "https://github.com/login/oauth/authorize"
@ -102,20 +137,43 @@ func initGithubOAuthConfig() {
} }
func init() { func init() {
initSessionConfig()
initGithubOAuthConfig() initGithubOAuthConfig()
router = mux.NewRouter() router = mux.NewRouter()
router.HandleFunc("/", indexHandler).Name("index") router.HandleFunc("/", indexHandler).Name("index")
router.HandleFunc("/session/sign-in", signInHandler).Name("sign-in")
router.HandleFunc("/session/sign-out", signOutHandler).Name("sign-out")
router.HandleFunc("/github/callback", githubOAuthCallbackHandler) router.HandleFunc("/github/callback", githubOAuthCallbackHandler)
http.Handle("/", router) http.Handle("/", router)
} }
var indexTemplate = template.Must(template.ParseFiles("templates/index.html")) var indexTemplate = template.Must(template.ParseFiles("templates/index.html"))
var indexSignedOutTemplate = template.Must(template.ParseFiles("templates/index-signed-out.html"))
func signInHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, githubOauthConfig.AuthCodeURL(""), http.StatusFound)
}
func signOutHandler(w http.ResponseWriter, r *http.Request) {
session, _ := sessionStore.Get(r, sessionConfig.CookieName)
session.Options.MaxAge = 0
session.Save(r, w)
indexUrl, _ := router.Get("index").URL()
http.Redirect(w, r, indexUrl.String(), http.StatusFound)
}
func indexHandler(w http.ResponseWriter, r *http.Request) { func indexHandler(w http.ResponseWriter, r *http.Request) {
tokenEncoded := r.FormValue("token") session, _ := sessionStore.Get(r, sessionConfig.CookieName)
if tokenEncoded == "" { tokenEncoded, ok := session.Values[sessionConfig.TokenKey].(string)
http.Redirect(w, r, githubOauthConfig.AuthCodeURL(""), http.StatusFound) if !ok {
signInUrl, _ := router.Get("sign-in").URL()
var signedOutParams = map[string]string{
"SignInUrl": signInUrl.String(),
}
if err := indexSignedOutTemplate.Execute(w, signedOutParams); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return return
} }
token, err := decodeOAuthToken(tokenEncoded) token, err := decodeOAuthToken(tokenEncoded)
@ -200,11 +258,13 @@ func githubOAuthCallbackHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
redirectUrl, _ := router.GetRoute("index").URL()
redirectParams := url.Values{} session, _ := sessionStore.Get(r, sessionConfig.CookieName)
redirectParams.Add("token", tokenEncoded) session.Values[sessionConfig.TokenKey] = tokenEncoded
redirectUrl.RawQuery = redirectParams.Encode() session.Save(r, w)
http.Redirect(w, r, redirectUrl.String(), http.StatusFound) log.Printf("session.Values: %s", session.Values)
indexUrl, _ := router.Get("index").URL()
http.Redirect(w, r, indexUrl.String(), http.StatusFound)
} }
func githubOAuthTransport(r *http.Request) *oauth.Transport { func githubOAuthTransport(r *http.Request) *oauth.Transport {

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GitHop</title>
<link rel="stylesheet" href="/static/main.css">
</head>
<body>
<h1>GitHop!</h1>
<a href="{{.SignInUrl}}">
Sign In
</a>
</body>
</html>