Skip to content
This repository was archived by the owner on Aug 12, 2025. It is now read-only.

Commit 8045ac2

Browse files
Create oauth2.go file
Update authorization example to show how to obtain code via web server or via command-line prompt.
1 parent 1b73a4c commit 8045ac2

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed

go/oauth2.go

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
"net"
8+
"net/http"
9+
"net/url"
10+
"os"
11+
"os/exec"
12+
"os/user"
13+
"path/filepath"
14+
"runtime"
15+
16+
"golang.org/x/net/context"
17+
"golang.org/x/oauth2"
18+
)
19+
20+
// This variable indicates whether the script should launch a web server to
21+
// initiate the authorization flow or just display the URL in the terminal
22+
// window. Note the following instructions based on this setting:
23+
// * launchWebServer = true
24+
// 1. Use OAuth2 credentials for a web application
25+
// 2. Define authorized redirect URIs for the credential in the Google APIs
26+
// Console and set the RedirectURL property on the config object to one
27+
// of those redirect URIs. For example:
28+
// config.RedirectURL = "http://localhost:8090"
29+
// 3. In the startWebServer function below, update the URL in this line
30+
// to match the redirect URI you selected:
31+
// listener, err := net.Listen("tcp", "localhost:8090")
32+
// The redirect URI identifies the URI to which the user is sent after
33+
// completing the authorization flow. The listener then captures the
34+
// authorization code in the URL and passes it back to this script.
35+
// * launchWebServer = false
36+
// 1. Use OAuth2 credentials for an installed application. (When choosing
37+
// the application type for the OAuth2 client ID, select "Other".)
38+
// 2. Set the redirect URI to "urn:ietf:wg:oauth:2.0:oob", like this:
39+
// config.RedirectURL = "urn:ietf:wg:oauth:2.0:oob"
40+
// 3. When running the script, complete the auth flow. Then copy the
41+
// authorization code from the browser and enter it on the command line.
42+
const launchWebServer = false
43+
44+
const missingClientSecretsMessage = `
45+
Please configure OAuth 2.0
46+
To make this sample run, you need to populate the client_secrets.json file
47+
found at:
48+
%v
49+
with information from the {{ Google Cloud Console }}
50+
{{ https://cloud.google.com/console }}
51+
For more information about the client_secrets.json file format, please visit:
52+
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
53+
`
54+
55+
// getClient uses a Context and Config to retrieve a Token
56+
// then generate a Client. It returns the generated Client.
57+
func getClient(ctx context.Context, config *oauth2.Config) *http.Client {
58+
cacheFile, err := tokenCacheFile()
59+
if err != nil {
60+
log.Fatalf("Unable to get path to cached credential file. %v", err)
61+
}
62+
tok, err := tokenFromFile(cacheFile)
63+
if err != nil {
64+
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
65+
if launchWebServer {
66+
fmt.Println("Trying to get token from web")
67+
tok, err = getTokenFromWeb(config, authURL)
68+
} else {
69+
fmt.Println("Trying to get token from prompt")
70+
tok, err = getTokenFromPrompt(config, authURL)
71+
}
72+
if err == nil {
73+
saveToken(cacheFile, tok)
74+
}
75+
}
76+
return config.Client(ctx, tok)
77+
}
78+
79+
// startWebServer starts a web server that listens on http://localhost:8080.
80+
// The webserver waits for an oauth code in the three-legged auth flow.
81+
func startWebServer() (codeCh chan string, err error) {
82+
listener, err := net.Listen("tcp", "localhost:8090")
83+
if err != nil {
84+
return nil, err
85+
}
86+
codeCh = make(chan string)
87+
88+
go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
89+
code := r.FormValue("code")
90+
codeCh <- code // send code to OAuth flow
91+
listener.Close()
92+
w.Header().Set("Content-Type", "text/plain")
93+
fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", code)
94+
}))
95+
96+
return codeCh, nil
97+
}
98+
99+
// openURL opens a browser window to the specified location.
100+
// This code originally appeared at:
101+
// http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go
102+
func openURL(url string) error {
103+
var err error
104+
switch runtime.GOOS {
105+
case "linux":
106+
err = exec.Command("xdg-open", url).Start()
107+
case "windows":
108+
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://localhost:4001/").Start()
109+
case "darwin":
110+
err = exec.Command("open", url).Start()
111+
default:
112+
err = fmt.Errorf("Cannot open URL %s on this platform", url)
113+
}
114+
return err
115+
}
116+
117+
func exchangeToken(config *oauth2.Config, code string) (*oauth2.Token, error) {
118+
tok, err := config.Exchange(oauth2.NoContext, code)
119+
if err != nil {
120+
log.Fatalf("Unable to retrieve token %v", err)
121+
}
122+
return tok, nil
123+
}
124+
125+
// getTokenFromPrompt uses Config to request a Token and prompts the user
126+
// to enter the token on the command line. It returns the retrieved Token.
127+
func getTokenFromPrompt(config *oauth2.Config, authURL string) (*oauth2.Token, error) {
128+
var code string
129+
fmt.Printf("Go to the following link in your browser. After completing " +
130+
"the authorization flow, enter the authorization code on the command " +
131+
"line: \n%v\n", authURL)
132+
133+
if _, err := fmt.Scan(&code); err != nil {
134+
log.Fatalf("Unable to read authorization code %v", err)
135+
}
136+
fmt.Println(authURL)
137+
return exchangeToken(config, code)
138+
}
139+
140+
// getTokenFromWeb uses Config to request a Token.
141+
// It returns the retrieved Token.
142+
func getTokenFromWeb(config *oauth2.Config, authURL string) (*oauth2.Token, error) {
143+
codeCh, err := startWebServer()
144+
if err != nil {
145+
fmt.Printf("Unable to start a web server.")
146+
return nil, err
147+
}
148+
149+
err = openURL(authURL)
150+
if err != nil {
151+
log.Fatalf("Unable to open authorization URL in web server: %v", err)
152+
} else {
153+
fmt.Println("Your browser has been opened to an authorization URL.",
154+
" This program will resume once authorization has been provided.\n")
155+
fmt.Println(authURL)
156+
}
157+
158+
// Wait for the web server to get the code.
159+
code := <-codeCh
160+
return exchangeToken(config, code)
161+
}
162+
163+
// tokenCacheFile generates credential file path/filename.
164+
// It returns the generated credential path/filename.
165+
func tokenCacheFile() (string, error) {
166+
usr, err := user.Current()
167+
if err != nil {
168+
return "", err
169+
}
170+
tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
171+
os.MkdirAll(tokenCacheDir, 0700)
172+
return filepath.Join(tokenCacheDir,
173+
url.QueryEscape("youtube-go.json")), err
174+
}
175+
176+
// tokenFromFile retrieves a Token from a given file path.
177+
// It returns the retrieved Token and any read error encountered.
178+
func tokenFromFile(file string) (*oauth2.Token, error) {
179+
f, err := os.Open(file)
180+
if err != nil {
181+
return nil, err
182+
}
183+
t := &oauth2.Token{}
184+
err = json.NewDecoder(f).Decode(t)
185+
defer f.Close()
186+
return t, err
187+
}
188+
189+
// saveToken uses a file path to create a file and store the
190+
// token in it.
191+
func saveToken(file string, token *oauth2.Token) {
192+
fmt.Println("trying to save token")
193+
fmt.Printf("Saving credential file to: %s\n", file)
194+
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
195+
if err != nil {
196+
log.Fatalf("Unable to cache oauth token: %v", err)
197+
}
198+
defer f.Close()
199+
json.NewEncoder(f).Encode(token)
200+
}

0 commit comments

Comments
 (0)