Begin supporting Monzo OAuth 2.0 login flow

What's done:
- Basic support of the client authorization grant stage of the OAuth login
  flow:
  - Open Google Chrome to point the user to Monzo's client authorization page.
  - Created a web server to retrieve the authorization code from Monzo.

What's left:
- Pulling the authorization grant (i.e. code) from Monzo's request and
  exchanging it for an access token and a refresh token, which can be used to
  make subsequent requests.

Unanswered question:
- Assuming this is a stateless app, where should I store the access token and
  refresh token to avoid the authorization flow. I'd like to avoid the client
  authorization flow because ideally I could run this app as a job that runs
  periodically throughout the day without requiring my interactions with it.

Some interesting notes:
- Notice how in the .envrc file, it's possible to make calls to `pass`. This
  allows me to check in the .envrc files without obscuring their content. It
  also allows me to consume these values in my app by using
  `os.Getenv("client_secret")`, which I find straightforward. Overall, I'm quite
  pleased to have stumbled upon this pattern - assuming that it's secure.
This commit is contained in:
William Carroll 2020-02-04 22:54:47 +00:00
parent cce926d60f
commit 70034d4cb9
2 changed files with 43 additions and 0 deletions

2
monzo-ynab/.envrc Normal file
View file

@ -0,0 +1,2 @@
export client_id="$(pass show finance/monzo/client-id)"
export client_secret="$(pass show finance/monzo/client-secret)"

41
monzo-ynab/main.go Normal file
View file

@ -0,0 +1,41 @@
// Creating a job to import Monzo transactions into YNAB.
//
// This is going to run N times per 24 hours.
package main
import (
"fmt"
"log"
"net/http"
"os"
"os/exec"
)
var (
clientId = os.Getenv("client_id")
clientSecret = os.Getenv("client_secret")
)
const (
state = "xyz123"
redirectUri = "http://localhost:8080/authorize"
)
func handleRedirect(w http.ResponseWriter, r *http.Request) {
fmt.Println(r)
fmt.Fprintf(w, "Ackified")
}
func authorizeClient() {
url :=
fmt.Sprintf("https://auth.monzo.com/?client_id=%s&redirect_uri=%s&response_type=code&state=:state",
clientId, redirectUri, state)
exec.Command("google-chrome", url).Start()
}
func main() {
authorizeClient()
http.HandleFunc("/authorize", handleRedirect)
go log.Fatal(http.ListenAndServe(":8080", nil))
}