Skip to content

Instantly share code, notes, and snippets.

@woloski
Last active Aug 29, 2015
Embed
What would you like to do?
Go + Auth0

Install deps

go get github.com/gorilla/mux
go get github.com/astaxie/beego/session

Run

go run main.go

Open browser at http://localhost:8080

This sample has a pre-configured client id and secret. You can configure it with your own.

Note: the session depedency is optional. It's used here to store the user profile and tokens but it can be easily replaced with something else.

package main
import (
"net/http"
"net/url"
"flag"
"log"
"encoding/json"
"io/ioutil"
"strings"
"encoding/base64"
"fmt"
"github.com/gorilla/mux"
"github.com/astaxie/beego/session"
)
var (
// Register new app at https://auth0.com and provide
// clientId (-id), clientSecret (-secret) and callbackURL (-redirect)
// as imput arguments.
domain = "contoso.auth0.com"
clientId = "OGoZgeM7K56GflfNwWCwEIMg9kzQ5Ome"
clientSecret = "tfK3imkasjtRFRR7ss-iRSX2BiclNWwNYqNdzQ85jIJzuQg2biSQw2WjShCOitT9"
callbackURL = "http://localhost:8080/callback"
)
var sessionManager *session.Manager
func init() {
sessionManager, _ = session.NewManager("memory", `{"cookieName":"gosessionid","gclifetime":3600}`)
go sessionManager.GC()
}
var router *mux.Router
func main() {
flag.Parse()
router = mux.NewRouter()
http.HandleFunc("/", router.ServeHTTP)
router.HandleFunc("/", homeHandler).Methods("GET")
router.HandleFunc("/callback", callbackHandler).Methods("GET")
http.ListenAndServe(":8080", nil)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
sess := sessionManager.SessionStart(w, r)
defer sess.SessionRelease(w)
profile, ok := sess.Get("profile").(map[string]interface{})
if ok && profile != nil {
fmt.Fprintf(w,
`<html>
<body>
<div>Welcome %v</div>
<div><pre><code>%#v</code></pre></div>
</body>
</html>`, profile["name"], profile)
} else {
fmt.Fprintf(w,
`<html>
<body>
<script src="https://cdn.auth0.com/w2/auth0-widget-5.2.min.js"></script>
<script type="text/javascript">
var widget = new Auth0Widget({
domain: '%v',
clientID: '%v',
callbackURL: '%v'
});
</script>
<button onclick="widget.signin({ scope: 'openid profile' })">Login</button>
</body>
</html>`, domain, clientId, callbackURL)
}
}
func callbackHandler(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code")
// state can be sent on the login request to keep some value across the redirect flow
// for instance a return url. It's also used for CSRF
// state := r.URL.Query().Get("state")
token, err := ExchangeCode(code);
if err != nil {
fmt.Fprintf(w,
`<html><body>Error: %v</body></html>`, err)
return
}
id_token := token["id_token"].(string)
parts := strings.Split(id_token, ".")
var claimBytes []byte
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
log.Fatalf("Error decoding token: %v", string(parts[1]))
}
var user map[string]interface{}
if err = json.Unmarshal(claimBytes, &user); err != nil {
log.Fatalf("Error parsing json: %v", string(claimBytes))
}
// store profile and token in session
sess := sessionManager.SessionStart(w, r)
defer sess.SessionRelease(w)
sess.Set("profile", user)
sess.Set("id_token", token["access_token"])
sess.Set("access_token", token["id_token"])
// redirect to home (or get a return url from session or state)
http.Redirect(w, r, "/", http.StatusMovedPermanently)
}
func ExchangeCode(code string) (map[string]interface{}, error) {
params := url.Values{}
client := &http.Client{}
req, err := http.NewRequest("POST", "https://" + domain + "/oauth/token" , nil)
if err != nil { return nil, err }
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
params.Set("grant_type", "authorization_code")
params.Set("code", code)
params.Set("client_id", clientId)
params.Set("client_secret", clientSecret)
params.Set("redirect_uri", callbackURL)
encParams := params.Encode()
reader := strings.NewReader(encParams)
req.Body = ioutil.NopCloser(reader)
req.ContentLength = int64(len(encParams))
resp, err := client.Do(req);
if err != nil { return nil, err }
raw, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil { return nil, err }
if resp.StatusCode != 200 {
return nil, &AuthenticationError{err_code: "invalid_code", err_description: string(raw)}
}
var token map[string]interface{}
if err := json.Unmarshal(raw, &token); err != nil {
return nil, &AuthenticationError{err_code: "invalid_token", err_description: "unable to parse to json. raw: " + string(raw)}
}
return token, nil
}
type AuthenticationError struct {
err_code string
err_description string
}
func (e AuthenticationError) Error() string {
return fmt.Sprintf("%v: %v", e.err_code, e.err_description)
}
func DecodeSegment(seg string) ([]byte, error) {
if l := len(seg) % 4; l > 0 {
seg += strings.Repeat("=", 4-l)
}
return base64.URLEncoding.DecodeString(seg)
}
@jfromaniello

This comment has been minimized.

Copy link

@jfromaniello jfromaniello commented Oct 1, 2014

🐦

    sess.Set("id_token", token["access_token"])
    sess.Set("access_token", token["id_token"])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment