1// Copyright 2011 Gary Burd 2// 3// Licensed under the Apache License, Version 2.0 (the "License"): you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12// License for the specific language governing permissions and limitations 13// under the License. 14 15package main 16 17import ( 18 "encoding/json" 19 "flag" 20 "fmt" 21 "io/ioutil" 22 "log" 23 "net/http" 24 "net/url" 25 "text/template" 26 27 "github.com/garyburd/go-oauth/examples/session" 28 "github.com/garyburd/go-oauth/oauth" 29) 30 31// Session state keys. 32var ( 33 tempCredKey = "tempCred" 34 tokenCredKey = "tokenCred" 35) 36 37var oauthClient = oauth.Client{ 38 TemporaryCredentialRequestURI: "https://api.dropbox.com/1/oauth/request_token", 39 ResourceOwnerAuthorizationURI: "https://www.dropbox.com/1/oauth/authorize", 40 TokenRequestURI: "https://api.dropbox.com/1/oauth/access_token", 41 SignatureMethod: oauth.PLAINTEXT, // Dropbox also works with HMACSHA1 42} 43 44var credPath = flag.String("config", "config.json", "Path to configuration file containing the application's credentials.") 45 46func readCredentials() error { 47 b, err := ioutil.ReadFile(*credPath) 48 if err != nil { 49 return err 50 } 51 return json.Unmarshal(b, &oauthClient.Credentials) 52} 53 54// serveLogin gets the OAuth temp credentials and redirects the user to the 55// OAuth server's authorization page. 56func serveLogin(w http.ResponseWriter, r *http.Request) { 57 // Dropbox supports the older OAuth 1.0 specification where the callback URL 58 // is passed to the authorization endpoint. 59 callback := "http://" + r.Host + "/callback" 60 tempCred, err := oauthClient.RequestTemporaryCredentials(nil, "", nil) 61 if err != nil { 62 http.Error(w, "Error getting temp cred, "+err.Error(), 500) 63 return 64 } 65 s := session.Get(r) 66 s[tempCredKey] = tempCred 67 if err := session.Save(w, r, s); err != nil { 68 http.Error(w, "Error saving session , "+err.Error(), 500) 69 return 70 } 71 http.Redirect(w, r, oauthClient.AuthorizationURL(tempCred, url.Values{"oauth_callback": {callback}}), 302) 72} 73 74// serveOAuthCallback handles callbacks from the OAuth server. 75func serveOAuthCallback(w http.ResponseWriter, r *http.Request) { 76 s := session.Get(r) 77 tempCred, _ := s[tempCredKey].(*oauth.Credentials) 78 if tempCred == nil || tempCred.Token != r.FormValue("oauth_token") { 79 http.Error(w, "Unknown oauth_token.", 500) 80 return 81 } 82 tokenCred, _, err := oauthClient.RequestToken(nil, tempCred, r.FormValue("oauth_verifier")) 83 if err != nil { 84 http.Error(w, "Error getting request token, "+err.Error(), 500) 85 return 86 } 87 delete(s, tempCredKey) 88 s[tokenCredKey] = tokenCred 89 if err := session.Save(w, r, s); err != nil { 90 http.Error(w, "Error saving session , "+err.Error(), 500) 91 return 92 } 93 http.Redirect(w, r, "/", 302) 94} 95 96// serveLogout clears the authentication cookie. 97func serveLogout(w http.ResponseWriter, r *http.Request) { 98 s := session.Get(r) 99 delete(s, tokenCredKey) 100 if err := session.Save(w, r, s); err != nil { 101 http.Error(w, "Error saving session , "+err.Error(), 500) 102 return 103 } 104 http.Redirect(w, r, "/", 302) 105} 106 107// authHandler reads the auth cookie and invokes a handler with the result. 108type authHandler struct { 109 handler func(w http.ResponseWriter, r *http.Request, c *oauth.Credentials) 110 optional bool 111} 112 113func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 114 cred, _ := session.Get(r)[tokenCredKey].(*oauth.Credentials) 115 if cred == nil && !h.optional { 116 http.Error(w, "Not logged in.", 403) 117 return 118 } 119 h.handler(w, r, cred) 120} 121 122// respond responds to a request by executing the html template t with data. 123func respond(w http.ResponseWriter, t *template.Template, data interface{}) { 124 w.Header().Set("Content-Type", "text/html; charset=utf-8") 125 if err := t.Execute(w, data); err != nil { 126 log.Print(err) 127 } 128} 129 130func serveHome(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { 131 if r.URL.Path != "/" { 132 http.NotFound(w, r) 133 return 134 } 135 if cred == nil { 136 respond(w, homeLoggedOutTmpl, nil) 137 } else { 138 respond(w, homeTmpl, nil) 139 } 140} 141 142func serveInfo(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { 143 resp, err := oauthClient.Get(nil, cred, "https://api.dropbox.com/1/account/info", nil) 144 if err != nil { 145 http.Error(w, "Error getting info: "+err.Error(), 500) 146 return 147 } 148 defer resp.Body.Close() 149 b, err := ioutil.ReadAll(resp.Body) 150 if err != nil { 151 http.Error(w, "Error reading body:"+err.Error(), 500) 152 return 153 } 154 if resp.StatusCode != 200 { 155 http.Error(w, fmt.Sprintf("Get account/info returned status %d, %s", resp.StatusCode, b), 500) 156 return 157 } 158 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 159 w.Write(b) 160} 161 162var httpAddr = flag.String("addr", ":8080", "HTTP server address") 163 164func main() { 165 flag.Parse() 166 if err := readCredentials(); err != nil { 167 log.Fatalf("Error reading configuration, %v", err) 168 } 169 http.Handle("/", &authHandler{handler: serveHome, optional: true}) 170 http.Handle("/info", &authHandler{handler: serveInfo}) 171 http.HandleFunc("/login", serveLogin) 172 http.HandleFunc("/logout", serveLogout) 173 http.HandleFunc("/callback", serveOAuthCallback) 174 if err := http.ListenAndServe(*httpAddr, nil); err != nil { 175 log.Fatalf("Error listening, %v", err) 176 } 177} 178 179var ( 180 homeLoggedOutTmpl = template.Must(template.New("loggedout").Parse( 181 `<html> 182<head> 183</head> 184<body> 185<a href="/login">login</a> 186</body> 187</html>`)) 188 189 homeTmpl = template.Must(template.New("home").Parse( 190 `<html> 191<head> 192</head> 193<body> 194<p><a href="/info">info</a> 195<p><a href="/logout">logout</a> 196</body> 197</html>`)) 198) 199