Skip to content
This repository was archived by the owner on Oct 6, 2024. It is now read-only.

Commit 56b4fe4

Browse files
author
Nyah Check
committed
Added authentication keys to start retrieving data from the youtube API
1 parent 63d58e0 commit 56b4fe4

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed

auth/auth.go

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// Copyright 2017 YTD Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
// OAuth to Youtube Data API
5+
6+
package auth
7+
8+
import (
9+
"encoding/json"
10+
"errors"
11+
"flag"
12+
"fmt"
13+
"io/ioutil"
14+
"net"
15+
"net/http"
16+
"os"
17+
"os/exec"
18+
"path/filepath"
19+
"runtime"
20+
21+
"code.google.com/p/goauth2/oauth"
22+
)
23+
24+
const missingClientSecretsMessage = ` OAuth 2.0 not configured `
25+
26+
var (
27+
clientSecretsFile = flag.String("secrets", "client_secrets.json", "Client Secrets configuration")
28+
cacheFile = flag.String("cache", "request.token", "Token cache file")
29+
)
30+
31+
// ClientConfig is a data structure definition for the client_secrets.json file.
32+
// The code unmarshals the JSON configuration file into this structure.
33+
type ClientConfig struct {
34+
ClientID string `json:"client_id"`
35+
ClientSecret string `json:"client_secret"`
36+
RedirectURIs []string `json:"redirect_uris"`
37+
AuthURI string `json:"auth_uri"`
38+
TokenURI string `json:"token_uri"`
39+
}
40+
41+
// Config is a root-level configuration object.
42+
type Config struct {
43+
Installed ClientConfig `json:"installed"`
44+
Web ClientConfig `json:"web"`
45+
}
46+
47+
// openURL opens a browser window to the specified location.
48+
// This code originally appeared at:
49+
// http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go
50+
func openURL(url string) error {
51+
var err error
52+
switch runtime.GOOS {
53+
case "linux":
54+
err = exec.Command("xdg-open", url).Start()
55+
case "windows":
56+
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://localhost:4001/").Start()
57+
case "darwin":
58+
err = exec.Command("open", url).Start()
59+
default:
60+
err = fmt.Errorf("Cannot open URL %s on this platform", url)
61+
}
62+
return err
63+
}
64+
65+
// readConfig reads the configuration from clientSecretsFile.
66+
// It returns an oauth configuration object for use with the Google API client.
67+
func readConfig(scope string) (*oauth.Config, error) {
68+
// Read the secrets file
69+
data, err := ioutil.ReadFile(*clientSecretsFile)
70+
if err != nil {
71+
pwd, _ := os.Getwd()
72+
fullPath := filepath.Join(pwd, *clientSecretsFile)
73+
return nil, fmt.Errorf(missingClientSecretsMessage, fullPath)
74+
}
75+
76+
cfg := new(Config)
77+
err = json.Unmarshal(data, &cfg)
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
var redirectUri string
83+
if len(cfg.Web.RedirectURIs) > 0 {
84+
redirectUri = cfg.Web.RedirectURIs[0]
85+
} else if len(cfg.Installed.RedirectURIs) > 0 {
86+
redirectUri = cfg.Installed.RedirectURIs[0]
87+
} else {
88+
return nil, errors.New("Must specify a redirect URI in config file or when creating OAuth client")
89+
}
90+
91+
return &oauth.Config{
92+
ClientId: cfg.Installed.ClientID,
93+
ClientSecret: cfg.Installed.ClientSecret,
94+
Scope: scope,
95+
AuthURL: cfg.Installed.AuthURI,
96+
TokenURL: cfg.Installed.TokenURI,
97+
RedirectURL: redirectUri,
98+
TokenCache: oauth.CacheFile(*cacheFile),
99+
// Get a refresh token so we can use the access token indefinitely
100+
AccessType: "offline",
101+
// If we want a refresh token, we must set this attribute
102+
// to force an approval prompt or the code won't work.
103+
ApprovalPrompt: "force",
104+
}, nil
105+
}
106+
107+
// startWebServer starts a web server that listens on http://localhost:8080.
108+
// The webserver waits for an oauth code in the three-legged auth flow.
109+
func startWebServer() (codeCh chan string, err error) {
110+
listener, err := net.Listen("tcp", "localhost:8080")
111+
if err != nil {
112+
return nil, err
113+
}
114+
codeCh = make(chan string)
115+
go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
116+
code := r.FormValue("code")
117+
codeCh <- code // send code to OAuth flow
118+
listener.Close()
119+
w.Header().Set("Content-Type", "text/plain")
120+
fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", code)
121+
}))
122+
123+
return codeCh, nil
124+
}
125+
126+
// buildOAuthHTTPClient takes the user through the three-legged OAuth flow.
127+
// It opens a browser in the native OS or outputs a URL, then blocks until
128+
// the redirect completes to the /oauth2callback URI.
129+
// It returns an instance of an HTTP client that can be passed to the
130+
// constructor of the YouTube client.
131+
func buildOAuthHTTPClient(scope string) (*http.Client, error) {
132+
config, err := readConfig(scope)
133+
if err != nil {
134+
msg := fmt.Sprintf("Cannot read configuration file: %v", err)
135+
return nil, errors.New(msg)
136+
}
137+
138+
transport := &oauth.Transport{Config: config}
139+
140+
// Try to read the token from the cache file.
141+
// If an error occurs, do the three-legged OAuth flow because
142+
// the token is invalid or doesn't exist.
143+
token, err := config.TokenCache.Token()
144+
if err != nil {
145+
// Start web server.
146+
// This is how this program receives the authorization code
147+
// when the browser redirects.
148+
codeCh, err := startWebServer()
149+
if err != nil {
150+
return nil, err
151+
}
152+
153+
// Open url in browser
154+
url := config.AuthCodeURL("")
155+
err = openURL(url)
156+
if err != nil {
157+
fmt.Println("Visit the URL below to get a code.",
158+
" This program will pause until the site is visted.")
159+
} else {
160+
fmt.Println("Your browser has been opened to an authorization URL.",
161+
" This program will resume once authorization has been provided.\n")
162+
}
163+
fmt.Println(url)
164+
165+
// Wait for the web server to get the code.
166+
code := <-codeCh
167+
168+
// This code caches the authorization code on the local
169+
// filesystem, if necessary, as long as the TokenCache
170+
// attribute in the config is set.
171+
token, err = transport.Exchange(code)
172+
if err != nil {
173+
return nil, err
174+
}
175+
}
176+
177+
transport.Token = token
178+
return transport.Client(), nil
179+
}

auth/client_secret.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"installed":{"client_id":"1023236677559-agfhktgml9nqi1j17ek0vmr6c7cjt87f.apps.googleusercontent.com","project_id":"central-web-175404","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"vqNWDovin1e8vR24HNdxF0_G","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}

auth/ytd-auth.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"access_token":"ya29.GluZBJ_ogDg3hhCcldf56ndk6Hx4PTW9y7yfFhFXlyJ4-4zXeXQFRV01-l8ycMFGaLCfHDfLAKsDw5Z8yM1Ns6WVSCoo9IKGngX1f2dxh9dCyM38QYNDCf9lVNgs","token_type":"Bearer","refresh_token":"1/v6zTvG9qcdstbIdQGGtqMvpk1H2QRxi7Z1ioxF66R7Q","expiry":"2017-07-30T23:29:26.611806229-06:00"}

0 commit comments

Comments
 (0)