Skip to content

Instantly share code, notes, and snippets.

@tcnksm

tcnksm/main.go Secret

Last active August 25, 2017 04:32
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tcnksm/3ca4ad1709da91386c9173ff0d926aa8 to your computer and use it in GitHub Desktop.
Save tcnksm/3ca4ad1709da91386c9173ff0d926aa8 to your computer and use it in GitHub Desktop.
Amazon Alexa Simple Account Linking Server by Golang
package main
import (
"encoding/json"
"log"
"net/http"
"os"
"time"
"golang.org/x/oauth2"
)
const (
DefaultPort = "3000"
)
func main() {
http.HandleFunc("/auth", authHandler)
http.HandleFunc("/token", tokenHandler)
port := DefaultPort
if v := os.Getenv("PORT"); len(v) != 0 {
port = v
}
log.Printf("[INFO] Start listening on %s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Printf("[ERROR] %s", err)
}
}
func authHandler(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if !ok || !(username == AlexaAdminName && password == AlexaAdminPass) {
w.Header().Set("WWW-Authenticate", `Basic realm="alexa-auth"`)
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Unauthorized"))
return
}
if v := r.FormValue("client_id"); v != ClientID {
log.Printf("[ERROR] Invalid client_id: %q", v)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("error"))
return
}
if v := r.FormValue("response_type"); v != "code" {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Bad request\n"))
return
}
state := r.FormValue("state")
if len(state) == 0 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Bad request\n"))
return
}
req, err := http.NewRequest("GET", RedirectURL, nil)
if err != nil {
log.Printf("[ERROR] %s", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal Server Error\n"))
return
}
values := req.URL.Query()
values.Add("state", state)
values.Add("code", AuthorizationCode)
req.URL.RawQuery = values.Encode()
log.Printf("[INFO] location: %s", req.URL.String())
http.Redirect(w, r, req.URL.String(), http.StatusFound)
}
func tokenHandler(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if !ok || !(username == ClientID && password == ClientSecret) {
w.Header().Set("WWW-Authenticate", `Basic realm="alexa-token"`)
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Unauthorized"))
return
}
if v := r.FormValue("grant_type"); v != "authorization_code" {
log.Printf("[ERROR] Invalid grant_type: %s", v)
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Bad request\n"))
return
}
if v := r.FormValue("code"); v != AuthorizationCode {
log.Printf("[ERROR] Invalid code: %s", v)
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Bad request\n"))
return
}
token := oauth2.Token{
AccessToken: AccessToken,
RefreshToken: AccessToken,
TokenType: "Bearer",
Expiry: time.Now().Add(30 * 24 * time.Hour),
}
buf, err := json.Marshal(&token)
if err != nil {
log.Printf("[ERROR] Marshal: %s", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("error"))
return
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(buf)
}
@kakopappa
Copy link

kakopappa commented Jan 24, 2017

hi, thanks for posting this code. can I use port 3000 in production? or do I have to use port 443 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment