Created
October 4, 2012 05:17
-
-
Save kisielk/3831610 to your computer and use it in GitHub Desktop.
An example of Google Persona login in Go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// An example of implementing Mozilla's Persona authentication system. | |
// follows the instructions from https://developer.mozilla.org/en-US/docs/Persona/Quick_Setup | |
// and doesn't do much else. | |
package main | |
import ( | |
"encoding/json" | |
"github.com/gorilla/mux" | |
"github.com/gorilla/sessions" | |
"html/template" | |
"log" | |
"net/http" | |
"net/url" | |
) | |
// The main page template. | |
var tmpl = ` | |
<html> | |
<head> | |
<title>Persona Test</title> | |
<script src="https://login.persona.org/include.js"></script> | |
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script> | |
</head> | |
<body> | |
<div> | |
{{if .Email}} | |
Signed in as {{.Email}}: <span id="signout"><a href="#">Sign out</a></span> | |
{{else}} | |
Not signed in: <span id="signin"><a href="#">Sign in</a></span> | |
{{end}} | |
<script> | |
var signinLink = document.getElementById('signin'); | |
if (signinLink) { | |
signinLink.onclick = function() { navigator.id.request(); }; | |
}; | |
var signoutLink = document.getElementById('signout'); | |
if (signoutLink) { | |
signoutLink.onclick = function() { navigator.id.logout(); }; | |
}; | |
{{if .Email}} | |
var currentUser = {{.Email}}; | |
{{else}} | |
var currentUser = null; | |
{{end}} | |
navigator.id.watch({ | |
loggedInUser: currentUser, | |
onlogin: function(assertion) { | |
// A user has logged in! Here you need to: | |
// 1. Send the assertion to your backend for verification and to create a session. | |
// 2. Update your UI. | |
$.ajax({ /* <-- This example uses jQuery, but you can use whatever you'd like */ | |
type: 'POST', | |
url: '/auth/login', // This is a URL on your website. | |
data: {assertion: assertion}, | |
success: function(res, status, xhr) { window.location.reload(); }, | |
error: function(res, status, xhr) { alert("login failure" + res); } | |
}); | |
}, | |
onlogout: function() { | |
// A user has logged out! Here you need to: | |
// Tear down the user's session by redirecting the user or making a call to your backend. | |
// Also, make that loggedInUser will get set to null on the next page load. | |
// (That's a literal JavaScript null. Not false, 0, or undefined. null.) | |
$.ajax({ | |
type: 'POST', | |
url: '/auth/logout', // This is a URL on your website. | |
success: function(res, status, xhr) { window.location.reload(); }, | |
error: function(res, status, xhr) { alert("logout failure" + res); } | |
}); | |
} | |
}); | |
</script> | |
</body> | |
</html> | |
` | |
// the audience string as required by the Persona API. Should be adjusted as appropriate. | |
var audience = "http://localhost:8080" | |
// a session cookie store | |
var sessionStore = sessions.NewCookieStore([]byte("something-very-secret")) | |
// the name of the session cookie. Should be adjusted as appropriate | |
var sessionName = "persona" | |
// serverError is a helper function for printing server error messages | |
// note that we're printing error messages directly to the client, | |
// this is probably not a good idea in production. | |
func serverError(w http.ResponseWriter, s string) { | |
log.Println("server error:", s) | |
http.Error(w, "server error: "+s, http.StatusInternalServerError) | |
} | |
// badRequest returns a bad request status to the client with a message | |
func badRequest(w http.ResponseWriter, s string) { | |
http.Error(w, s, http.StatusBadRequest) | |
} | |
// handleLogin is the handler for /auth/login | |
func handleLogin(w http.ResponseWriter, req *http.Request) { | |
assertion := req.FormValue("assertion") | |
if assertion == "" { | |
badRequest(w, "missing assertion") | |
return | |
} | |
resp, err := http.PostForm("https://verifier.login.persona.org/verify", | |
url.Values{"assertion": {assertion}, "audience": {audience}}) | |
if err != nil { | |
serverError(w, err.Error()) | |
} | |
defer resp.Body.Close() | |
dec := json.NewDecoder(resp.Body) | |
var response struct { | |
Status string `json:"status"` | |
Email string `json:"email"` | |
Audience string `json:"audience"` | |
Expires uint64 `json:"expires"` | |
Issuer string `json:"issuer"` | |
Reason string `json:"reason"` // Will only be set if Status != "okay" | |
} | |
if err := dec.Decode(&response); err != nil { | |
serverError(w, err.Error()) | |
} | |
if response.Status != "okay" { | |
serverError(w, "authentication failed: "+response.Reason) | |
} | |
session, err := sessionStore.Get(req, sessionName) | |
if err != nil { | |
serverError(w, err.Error()) | |
} | |
session.Values["email"] = response.Email | |
if err := session.Save(req, w); err != nil { | |
serverError(w, err.Error()) | |
} | |
} | |
// handleLogout is the handler for /auth/logout | |
func handleLogout(w http.ResponseWriter, req *http.Request) { | |
session, err := sessionStore.Get(req, sessionName) | |
if err != nil { | |
serverError(w, err.Error()) | |
} | |
delete(session.Values, "email") | |
if err := session.Save(req, w); err != nil { | |
serverError(w, err.Error()) | |
} | |
} | |
// handle is the main page handler | |
func handle(w http.ResponseWriter, req *http.Request) { | |
t, err := template.New("main").Parse(tmpl) | |
if err != nil { | |
serverError(w, err.Error()) | |
return | |
} | |
session, err := sessionStore.Get(req, sessionName) | |
if err != nil { | |
serverError(w, err.Error()) | |
} | |
err = t.ExecuteTemplate(w, "main", map[string]interface{}{"Email": session.Values["email"]}) | |
if err != nil { | |
serverError(w, err.Error()) | |
return | |
} | |
} | |
func main() { | |
r := mux.NewRouter() | |
r.HandleFunc("/", handle) | |
r.HandleFunc("/auth/login", handleLogin).Methods("POST") | |
r.HandleFunc("/auth/logout", handleLogout).Methods("POST") | |
http.Handle("/", r) | |
log.Fatal(http.ListenAndServe(":8080", nil)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment