tvl-depot/users/wpcarro/tools/monzo_ynab/tokens.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

284 lines
8.9 KiB
Go
Raw Normal View History

Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
// Creating a Tokens server to manage my access and refresh tokens. Keeping this
// as a separate server allows me to develop and use the access tokens without
// going through client authorization.
package main
////////////////////////////////////////////////////////////////////////////////
// Dependencies
////////////////////////////////////////////////////////////////////////////////
import (
"auth"
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
"encoding/json"
"fmt"
"io"
"kv"
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
"log"
"net/http"
"net/url"
"os"
"os/signal"
"syscall"
"time"
"utils"
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
)
////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////
// This is the response from Monzo's API after we request an access token
// refresh.
type refreshTokenResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ClientId string `json:"client_id"`
ExpiresIn int `json:"expires_in"`
}
// This is the shape of the request from clients wishing to set state of the
// server.
type setTokensRequest struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
}
// This is our application state.
type state struct {
accessToken string `json:"access_token"`
refreshToken string `json:"refresh_token"`
}
type readMsg struct {
sender chan state
}
type writeMsg struct {
state state
sender chan bool
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
}
type channels struct {
reads chan readMsg
writes chan writeMsg
}
////////////////////////////////////////////////////////////////////////////////
// Top-level Definitions
////////////////////////////////////////////////////////////////////////////////
var chans = &channels{
reads: make(chan readMsg),
writes: make(chan writeMsg),
}
var (
monzoClientId = os.Getenv("monzo_client_id")
monzoClientSecret = os.Getenv("monzo_client_secret")
storePath = os.Getenv("store_path")
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
)
////////////////////////////////////////////////////////////////////////////////
// Utils
////////////////////////////////////////////////////////////////////////////////
// Print the access and refresh tokens for debugging.
func logTokens(access string, refresh string) {
log.Printf("Access: %s\n", access)
log.Printf("Refresh: %s\n", refresh)
}
func (state *state) String() string {
return fmt.Sprintf("state{\n\taccessToken: \"%s\",\n\trefreshToken: \"%s\"\n}\n", state.accessToken, state.refreshToken)
}
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
// Schedule a token refresh for `expiresIn` seconds using the provided
// `refreshToken`. This will update the application state with the access token
// and schedule an additional token refresh for the newly acquired tokens.
func scheduleTokenRefresh(expiresIn int, refreshToken string) {
duration := time.Second * time.Duration(expiresIn)
timestamp := time.Now().Local().Add(duration)
// TODO(wpcarro): Consider adding a more human readable version that will
// log the number of hours, minutes, etc. until the next refresh.
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
log.Printf("Scheduling token refresh for %v\n", timestamp)
time.Sleep(duration)
log.Println("Refreshing tokens now...")
accessToken, refreshToken := refreshTokens(refreshToken)
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
log.Println("Successfully refreshed tokens.")
logTokens(accessToken, refreshToken)
setState(accessToken, refreshToken)
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
}
// Exchange existing credentials for a new access token and `refreshToken`. Also
// schedule the next refresh. This function returns the newly acquired access
// token and refresh token.
func refreshTokens(refreshToken string) (string, string) {
// TODO(wpcarro): Support retries with exponential backoff.
res, err := http.PostForm("https://api.monzo.com/oauth2/token", url.Values{
"grant_type": {"refresh_token"},
"client_id": {monzoClientId},
"client_secret": {monzoClientSecret},
"refresh_token": {refreshToken},
})
if res.StatusCode != http.StatusOK {
// TODO(wpcarro): Considering panicking here.
utils.DebugResponse(res)
}
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
if err != nil {
utils.DebugResponse(res)
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
log.Fatal("The request to Monzo to refresh our access token failed.", err)
}
defer res.Body.Close()
payload := &refreshTokenResponse{}
err = json.NewDecoder(res.Body).Decode(payload)
if err != nil {
log.Fatal("Could not decode the JSON response from Monzo.", err)
}
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
go scheduleTokenRefresh(payload.ExpiresIn, payload.RefreshToken)
// Interestingly, JSON decoding into the refreshTokenResponse can success
// even if the decoder doesn't populate any of the fields in the
// refreshTokenResponse struct. From what I read, it isn't possible to make
// these fields as required using an annotation, so this guard must suffice
// for now.
if payload.AccessToken == "" || payload.RefreshToken == "" {
log.Fatal("JSON parsed correctly but failed to populate token fields.")
}
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
return payload.AccessToken, payload.RefreshToken
}
func persistTokens(access string, refresh string) {
log.Println("Persisting tokens...")
kv.Set(storePath, "monzoAccessToken", access)
kv.Set(storePath, "monzoRefreshToken", refresh)
log.Println("Successfully persisted tokens.")
}
// Listen for SIGINT and SIGTERM signals. When received, persist the access and
// refresh tokens and shutdown the server.
func handleInterrupts() {
// Gracefully handle interruptions.
Debug os.Signal handling Problem: When SIGINT signals we're sent to the token server, it would shut down without completing the shutdown procedure. The shutdown procedure would persist the application state (i.e. access and refresh tokens). This is problematic for the following sequence of events: t0. Access and refresh tokens retrieved from kv.json and used as app state. t1. Tokens are refreshed but not persisted. (I'm still unsure how this happens). Remember that this means the previous access and refresh tokens from t0 are now invalid. t2. User sends a SIGINT. t3. Token server shuts down. t4. Token server is restarted, kv.json is used as the app state even though its tokens are now invalid. t5. Tokens are attempted to refresh, Monzo API rejects the tokens because they're invalid. Now we need to provide the token server with valid access and refresh tokens otherwise we will repeat the loop described above. This means going through the client authorization flow again or copying and pasting the tokens logged from the token server into kv.json. Either scenario is more manual than I'd prefer. Solution: Use a buffered channel to receive the os.Signal. I got this idea after reading these docs: https://golang.org/pkg/os/signal/#Notify and I debugged this issue shortly thereafter. I also rearranged the order of operations in main/0 to ensure that handleInterrupts/0, which registers the event listeners, occurs before scheduleTokenRefresh/2 is called. This allows the token server to gracefully shutdown even if it's in the middle of the scheduleTokenRefresh/2 call.
2020-02-09 23:09:21 +01:00
sigs := make(chan os.Signal, 1)
done := make(chan bool)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigs
log.Printf("Received signal to shutdown. %v\n", sig)
state := getState()
persistTokens(state.accessToken, state.refreshToken)
done <- true
}()
<-done
Debug os.Signal handling Problem: When SIGINT signals we're sent to the token server, it would shut down without completing the shutdown procedure. The shutdown procedure would persist the application state (i.e. access and refresh tokens). This is problematic for the following sequence of events: t0. Access and refresh tokens retrieved from kv.json and used as app state. t1. Tokens are refreshed but not persisted. (I'm still unsure how this happens). Remember that this means the previous access and refresh tokens from t0 are now invalid. t2. User sends a SIGINT. t3. Token server shuts down. t4. Token server is restarted, kv.json is used as the app state even though its tokens are now invalid. t5. Tokens are attempted to refresh, Monzo API rejects the tokens because they're invalid. Now we need to provide the token server with valid access and refresh tokens otherwise we will repeat the loop described above. This means going through the client authorization flow again or copying and pasting the tokens logged from the token server into kv.json. Either scenario is more manual than I'd prefer. Solution: Use a buffered channel to receive the os.Signal. I got this idea after reading these docs: https://golang.org/pkg/os/signal/#Notify and I debugged this issue shortly thereafter. I also rearranged the order of operations in main/0 to ensure that handleInterrupts/0, which registers the event listeners, occurs before scheduleTokenRefresh/2 is called. This allows the token server to gracefully shutdown even if it's in the middle of the scheduleTokenRefresh/2 call.
2020-02-09 23:09:21 +01:00
log.Println("Exiting...")
os.Exit(0)
}
// Set `accessToken` and `refreshToken` on application state.
func setState(accessToken string, refreshToken string) {
msg := writeMsg{state{accessToken, refreshToken}, make(chan bool)}
chans.writes <- msg
<-msg.sender
}
// Return our application state.
func getState() state {
msg := readMsg{make(chan state)}
chans.reads <- msg
return <-msg.sender
}
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
////////////////////////////////////////////////////////////////////////////////
// Main
////////////////////////////////////////////////////////////////////////////////
func main() {
// Manage application state.
go func() {
state := &state{}
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
for {
select {
case msg := <-chans.reads:
log.Println("Reading from state...")
log.Println(state)
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
msg.sender <- *state
case msg := <-chans.writes:
log.Println("Writing to state.")
log.Printf("Old: %s\n", state)
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
*state = msg.state
log.Printf("New: %s\n", state)
// As an attempt to maintain consistency between application
// state and persisted state, everytime we write to the
// application state, we will write to the store.
persistTokens(state.accessToken, state.refreshToken)
msg.sender <- true
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
}
}
}()
// Retrieve cached tokens from store.
accessToken := fmt.Sprintf("%v", kv.Get(storePath, "monzoAccessToken"))
refreshToken := fmt.Sprintf("%v", kv.Get(storePath, "monzoRefreshToken"))
Debug os.Signal handling Problem: When SIGINT signals we're sent to the token server, it would shut down without completing the shutdown procedure. The shutdown procedure would persist the application state (i.e. access and refresh tokens). This is problematic for the following sequence of events: t0. Access and refresh tokens retrieved from kv.json and used as app state. t1. Tokens are refreshed but not persisted. (I'm still unsure how this happens). Remember that this means the previous access and refresh tokens from t0 are now invalid. t2. User sends a SIGINT. t3. Token server shuts down. t4. Token server is restarted, kv.json is used as the app state even though its tokens are now invalid. t5. Tokens are attempted to refresh, Monzo API rejects the tokens because they're invalid. Now we need to provide the token server with valid access and refresh tokens otherwise we will repeat the loop described above. This means going through the client authorization flow again or copying and pasting the tokens logged from the token server into kv.json. Either scenario is more manual than I'd prefer. Solution: Use a buffered channel to receive the os.Signal. I got this idea after reading these docs: https://golang.org/pkg/os/signal/#Notify and I debugged this issue shortly thereafter. I also rearranged the order of operations in main/0 to ensure that handleInterrupts/0, which registers the event listeners, occurs before scheduleTokenRefresh/2 is called. This allows the token server to gracefully shutdown even if it's in the middle of the scheduleTokenRefresh/2 call.
2020-02-09 23:09:21 +01:00
log.Println("Attempting to retrieve cached credentials...")
logTokens(accessToken, refreshToken)
if accessToken == "" || refreshToken == "" {
log.Println("Cached credentials are absent. Authorizing client...")
authCode := auth.GetAuthCode(monzoClientId)
tokens := auth.GetTokensFromAuthCode(authCode, monzoClientId, monzoClientSecret)
setState(tokens.AccessToken, tokens.RefreshToken)
go scheduleTokenRefresh(tokens.ExpiresIn, tokens.RefreshToken)
} else {
setState(accessToken, refreshToken)
// If we have tokens, they may be expiring soon. We don't know because
// we aren't storing the expiration timestamp in the state or in the
// store. Until we have that information, and to be safe, let's refresh
// the tokens.
go scheduleTokenRefresh(0, refreshToken)
}
// Gracefully handle shutdowns.
go handleInterrupts()
Debug os.Signal handling Problem: When SIGINT signals we're sent to the token server, it would shut down without completing the shutdown procedure. The shutdown procedure would persist the application state (i.e. access and refresh tokens). This is problematic for the following sequence of events: t0. Access and refresh tokens retrieved from kv.json and used as app state. t1. Tokens are refreshed but not persisted. (I'm still unsure how this happens). Remember that this means the previous access and refresh tokens from t0 are now invalid. t2. User sends a SIGINT. t3. Token server shuts down. t4. Token server is restarted, kv.json is used as the app state even though its tokens are now invalid. t5. Tokens are attempted to refresh, Monzo API rejects the tokens because they're invalid. Now we need to provide the token server with valid access and refresh tokens otherwise we will repeat the loop described above. This means going through the client authorization flow again or copying and pasting the tokens logged from the token server into kv.json. Either scenario is more manual than I'd prefer. Solution: Use a buffered channel to receive the os.Signal. I got this idea after reading these docs: https://golang.org/pkg/os/signal/#Notify and I debugged this issue shortly thereafter. I also rearranged the order of operations in main/0 to ensure that handleInterrupts/0, which registers the event listeners, occurs before scheduleTokenRefresh/2 is called. This allows the token server to gracefully shutdown even if it's in the middle of the scheduleTokenRefresh/2 call.
2020-02-09 23:09:21 +01:00
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
// Listen to inbound requests.
fmt.Println("Listening on http://localhost:4242 ...")
log.Fatal(http.ListenAndServe(":4242",
http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path == "/refresh-tokens" && req.Method == "POST" {
state := getState()
go scheduleTokenRefresh(0, state.refreshToken)
fmt.Fprintf(w, "Done.")
} else if req.URL.Path == "/set-tokens" && req.Method == "POST" {
// Parse
payload := &setTokensRequest{}
err := json.NewDecoder(req.Body).Decode(payload)
if err != nil {
log.Fatal("Could not decode the user's JSON request.", err)
}
// Update application state
setState(payload.AccessToken, payload.RefreshToken)
// Refresh tokens
go scheduleTokenRefresh(payload.ExpiresIn, payload.RefreshToken)
// Ack
fmt.Fprintf(w, "Done.")
} else if req.URL.Path == "/state" && req.Method == "GET" {
// TODO(wpcarro): Ensure that this returns serialized state.
w.Header().Set("Content-type", "application/json")
state := getState()
payload, _ := json.Marshal(state)
io.WriteString(w, string(payload))
} else {
log.Printf("Unhandled request: %v\n", *req)
}
})))
Create server for managing Monzo credentials I created a server to manage my access and refresh tokens. This server exposes a larger API than it needs to at the moment, but that should change. The goal is to expose a GET at /token to retrieve a valid access token. The server should take care of refreshing tokens before they expire and getting entirely new tokens, should they become so stale that I need to re-authorize my application. A lot of my development of this project has been clumsy. I'm new to Go; I didn't understand OAuth2.0; I'm learning concurrent programming (outside of the context of comfortable Elixir/Erlang). My habits for writing programs in compiled languages feels amateurish. I find myself dropping log.Println's all over the source code when I should be using proper debugging tools like Delve and properly logging with things like httputil.Dump{Request,Response}. The application right now is in a transitional state. There is still plenty of code in main.go that belongs in tokens.go. For instance, the client authorization code belongs in the tokens server. Another question I haven't answered is where is the monzo client that I can use to make function calls like `monzo.Transactions` or `monzo.Accounts`? The benefit of having a tokens server is that it allows me to maintain state of the tokens while I'm developing. This way, I can stop and start main.go without disturbing the state of the access tokens. Of course this isn't the primary benefit, which is to abstract over the OAuth details and expose an API that gives me an access token whenever I request one. The first benefit that I listed could and perhaps should be solved by introducing some simple persistence. I'd like to write the access tokens to disk when I shutdown the tokens server and read them from disk when I start the tokens server. This will come. I could have done this before introducing the tokens server, and it would have saved me a few hours I think. Where has my time gone? Mostly I've been re-authorizing my client unnecessarily. This process is expensive because it opens a web browser, asks me to enter my email address, sends me an email, I then click the link in that email. Overall this takes maybe 1-3 minutes in total. Before my tokens server existed, however, I was doing this about 10-20 times per hour. It's a little disappointing that I didn't rectify this earlier. I'd like to remain vigilant and avoid making similar workflow mistakes as I move ahead.
2020-02-09 02:07:36 +01:00
}