OAuth is a mechanism that allows a user to authorize your application to access his/her data from another service without giving you their authentication details.
For a banking monitoring application (e.g. your application reads the user’s banking data and displays it within some useful diagrams), the steps could look something like the following:
http://127.0.0.1:5000/
http://127.0.0.1:5000/github_oauth_cb
Client ID
and Client Secret
(provided by GitHub when registering your app)If you restart your application (see below for code), and the user goes back to the /login
page, then clicking on the login button will attempt to access the token and realise it already exists and so redirects back to the login page again. Check the console/terminal output for user details that have been printed.
The user should now be able to see your application listed in their authorized applications: https://github.com/settings/applications
https://blog.kowalczyk.info/article/f/Accessing-GitHub-API-from-Go.html
package main
import (
"fmt"
"net/http"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
githuboauth "golang.org/x/oauth2/github"
)
var (
// You must register the app at https://github.com/settings/applications
// Set callback to http://127.0.0.1:5000/github_oauth_cb
// Set ClientId and ClientSecret values taken from GitHub registration page
// Note: revoked the already details
oauthConf = &oauth2.Config{
ClientID: "x",
ClientSecret: "x",
Scopes: []string{"user:email", "repo"},
Endpoint: githuboauth.Endpoint,
}
// random string for oauth2 API calls to protect against CSRF
oauthStateString = "thisshouldberandom"
)
const htmlIndex = `<html><body>
Login with <a href="/login">GitHub</a>
</body></html>
`
// /
func handleMain(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write([]byte(htmlIndex))
}
// /login redirects to GitHub’s authorization page:
// GitHub will show authorization page to your user
// If the user authorizes your app, GitHub will re-direct to OAuth callback
func handleGitHubLogin(w http.ResponseWriter, r *http.Request) {
url := oauthConf.AuthCodeURL(oauthStateString, oauth2.AccessTypeOnline)
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
// /github_oauth_cb. Called by GitHub after authorization is granted
// This handler accesses the GitHub token, and converts the token into a http client
// We then use that http client to list GitHub information about the user
func handleGitHubCallback(w http.ResponseWriter, r *http.Request) {
state := r.FormValue("state")
if state != oauthStateString {
fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
code := r.FormValue("code")
token, err := oauthConf.Exchange(oauth2.NoContext, code)
if err != nil {
fmt.Printf("oauthConf.Exchange() failed with '%s'\n", err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
oauthClient := oauthConf.Client(oauth2.NoContext, token)
client := github.NewClient(oauthClient)
user, _, err := client.Users.Get("")
if err != nil {
fmt.Printf("client.Users.Get() faled with '%s'\n", err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
fmt.Printf("Logged in as GitHub user: %s\n", *user.Login)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}
func main() {
http.HandleFunc("/", handleMain)
http.HandleFunc("/login", handleGitHubLogin)
http.HandleFunc("/github_oauth_cb", handleGitHubCallback)
fmt.Print("Started running on http://127.0.0.1:5000\n")
fmt.Println(http.ListenAndServe(":5000", nil))
}
If you want your user state to be persistent, then you would need to store it in a permanent/persistent store; there are lots of options for that (e.g. a distributed redis cluster - so the data is available across machines and allows your web servers to scale horizontally)
This also means you’ll need to handle your own authentication and / or authorization in your web app to use that persistent data in order to make those upstream requests on behalf of a specific user.
You could also keep a cookie in the browser (encrypted and hashed) with the token and some basic user info. But I’m not overly confident with using cookies.