Skip to content

Instantly share code, notes, and snippets.

@audrenbdb
Last active December 8, 2021 07:05
Show Gist options
  • Save audrenbdb/a1bca04ac459e6fa6580c5953de35ece to your computer and use it in GitHub Desktop.
Save audrenbdb/a1bca04ac459e6fa6580c5953de35ece to your computer and use it in GitHub Desktop.
Minimum OAuth2 local client
<!DOCTYPE html>
<html>
<body>
<a
href="http://localhost:9096/oauth/authorize?client_id=paulthebest&redirect_uri=http://localhost:8080/oauth/redirect&response_type=code">
Login
</a>
</body>
</html>
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
)
const clientID = "paulthebest"
func main() {
fs := http.FileServer(http.Dir("public"))
http.Handle("/", fs)
// We will be using `httpClient` to make external HTTP requests later in our code
httpClient := http.Client{}
// Create a new redirect route route
http.HandleFunc("/oauth/redirect", func(w http.ResponseWriter, r *http.Request) {
// First, we need to get the value of the `code` query param
err := r.ParseForm()
if err != nil {
fmt.Fprintf(os.Stdout, "could not parse query: %v", err)
w.WriteHeader(http.StatusBadRequest)
}
code := r.Form.Get("code")
reqURL := fmt.Sprintf("http://localhost:9096/oauth/token?client_id=%s&code=%s&grant_type=authorization_code&redirect_uri=http://localhost:8080/oauth/redirect", clientID, code)
req, err := http.NewRequest(http.MethodPost, reqURL, nil)
if err != nil {
fmt.Fprintf(os.Stdout, "could not create HTTP request: %v", err)
w.WriteHeader(http.StatusBadRequest)
}
// We set this header since we want the response
// as JSON
req.Header.Set("accept", "application/json")
// Send out the HTTP request
res, err := httpClient.Do(req)
if err != nil {
fmt.Fprintf(os.Stdout, "could not send HTTP request: %v", err)
w.WriteHeader(http.StatusInternalServerError)
}
defer res.Body.Close()
// Parse the request body into the `OAuthAccessResponse` struct
var t OAuthAccessResponse
b, err := io.ReadAll(res.Body)
err = json.Unmarshal(b, &t)
if err != nil {
log.Printf("error decoding sakura response: %v", err)
if e, ok := err.(*json.SyntaxError); ok {
log.Printf("syntax error at byte offset %d", e.Offset)
}
log.Printf("sakura response: %q", res.Body)
}
// Finally, send a response to redirect the user to the "welcome" page
// with the access token
w.Header().Set("Location", "/welcome.html?access_token="+t.AccessToken)
w.WriteHeader(http.StatusFound)
})
http.ListenAndServe(":8080", nil)
}
type OAuthAccessResponse struct {
AccessToken string `json:"access_token"`
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Hello</title>
</head>
<body></body>
<script>
// We can get the token from the "access_token" query
// param, available in the browsers "location" global
const query = window.location.search.substring(1);
const token = query.split("access_token=")[1];
// Call the user info API using the fetch browser library
fetch("http://localhost:9096/user", {
headers: {
Accept: "application/json",
Authorization: "Bearer " + token,
},
})
// Parse the response as JSON
.then((res) => res.json())
.then((res) => {
const nameNode = document.createTextNode(`Welcome, ${res.first_name} ${res.last_name}`)
document.body.appendChild(nameNode);
});
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment