First request

To return an authenticated stream, we need to check whether authentication is required (a status code of 401 on an HTTP request means just that). If we've already authenticated, then the request will complete as normal and we can just return the request body. If authentication is required, then we must initiate the process by loading a web browser at the correct URL to ask the user for permission; this is completed by a helper function, openBrowser(), which can be found in this book's source code repository.

When the browser window opens, the user will be told about the permission being requested and, assuming they accept, the page will redirect to a callback URL. We need to set up a simple web server locally to handle this redirect. To do so, we register a handler at the /oauth/callback path and wait for a connection on port 19999.

The server is started, which will cause the function to block until we shut it down later:

func authStream(url string) io.ReadCloser {
ret, err := client.Get(url)

if err == nil && ret.StatusCode != 401 {
return ret.Body
}

fmt.Println("Requesting authorization")
openbrowser(conf.AuthCodeURL("state", oauth2.AccessTypeOffline))

http.HandleFunc("/oauth/callback", callbackHandler)
server = &http.Server{Addr: ":19999", Handler: nil}
server.ListenAndServe()

return retReader
}

The callback handler is relatively simple. It's responsible for extracting the authorization code from the redirection and, using this code, requesting a reusable token from the server that sent the single-use code (this is handled by conf.Exchange()). Upon the exchange completing, we try once again to connect to the URL originally specified; if we, succeed, then the return stream is set and if not, we fail with the appropriate error. Whatever the outcome, we prompt the user to close the browser window (as web page security dictates this can't be done automatically). After we've returned this content to the user, we'll shut down the server. This returns control to the original authStream() function, which will return the newly authenticated request stream:

func callbackHandler(w http.ResponseWriter, r *http.Request) {
queryParts, _ := url.ParseQuery(r.URL.RawQuery)

authCode := queryParts["code"][0]
tok, err := conf.Exchange(ctx, authCode)
if err != nil {
log.Fatal(err)
}
client = conf.Client(ctx, tok)

ret, err := client.Get("https://www.googleapis.com/gmail/v1/users/me/messages")
if err != nil {
fmt.Fprint(w, "<p><strong>Authentication Failed</strong></p>")
fmt.Fprintf(w, "<p style="color: red">%s</p>", err.Error())
fmt.Fprint(w, "<p>Please close this window and try again.</p>")
log.Fatal(err)
} else {
fmt.Fprint(w, "<p><strong>Authentication Completed</strong></p>")
fmt.Fprint(w, "<p>Please close this window.</p>")

retReader = ret.Body
}

server.Shutdown(context.Background())
}

The last piece of this puzzle is to set up the OAuth2 configuration and context. We'll be requesting read-only authentication scope from the Gmail API and specifying our local server for the callback URL. You'll need to provide values for CLIENT_ID and CLIENT_SECRET for this to operate correctly. Much of the configuration is helpfully provided by the google.Endpoint definition from the golang.org/x/oauth2/google package:

func setupOAuth() {
// Your credentials should be obtained from the Google Developer Console
// (https://console.developers.google.com).
conf = &oauth2.Config{
ClientID: "CLIENT_ID",
ClientSecret: "CLIENT_SECRET",
Scopes: []string{"https://www.googleapis.com/auth/gmail.readonly"},
Endpoint: google.Endpoint,
RedirectURL: "http://127.0.0.1:19999/oauth/callback",
}
ctx = context.WithValue(context.Background(), oauth2.HTTPClient, client)
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.37.126