Open arindampal-0 opened 11 months ago
Fetch code from URL before certain timeout:
package main
import (
"fmt"
"net/http"
"net/url"
"os"
"time"
)
func main() {
/* Channel to receive the code from webserver */
codeChan := make(chan string)
/* Timeout duration */
timeout := 10
/* Creating http handler for the callback */
mux := http.NewServeMux()
/* Adding request handler */
mux.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) {
query, _ := url.ParseQuery(r.URL.RawQuery)
code := query.Get("code")
codeChan <- code
w.Write([]byte("<p>Code Fetched from URL, you can close the browser tab now.</p>"))
})
/* Creating the server instance */
server := http.Server{
Addr: ":8000",
Handler: mux,
}
/* Starting the server in a go routine */
go func() {
fmt.Println("Server running...")
server.ListenAndServe()
}()
/* Initializing timeout channel */
timeoutChan := time.After(time.Duration(timeout) * time.Second)
/* variable to store the code from URL */
var code string
/* Wait for either timeout expiry or receive code */
Loop:
for {
select {
case code = <-codeChan:
break Loop
case <-timeoutChan:
break Loop
}
}
/* Close the webserver */
server.Close()
fmt.Println("Server closed.")
/* check if code was received or time expired */
if code == "" {
fmt.Printf("Code was not received before %ds timeout.\n", timeout)
os.Exit(1)
} else {
fmt.Println("code: ", code)
}
}
Opening URL in OS's default browser:
using browser package
Get the package for current project:
go get -u github.com/pkg/browser
Usage:
package main
import "github.com/pkg/browser"
func main() {
/* Open the auth URL in the browser */
URL := "https://github.com/ArindamPal-0"
err := browser.OpenURL(URL)
/* could not open URL from cli */
if err != nil {
fmt.Println("Could not open URL from the terminal, you'll have to copy and open the URL manually.")
}
}
Oauth2 Authentication Code
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"github.com/joho/godotenv"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/drive/v3"
"google.golang.org/api/option"
)
func getClient(config *oauth2.Config) *http.Client {
tokenFile := "token.json"
token, err := getTokenFromFile(tokenFile)
if !token.Valid() {
log.Println("Token Expired.")
}
if err != nil {
log.Println("Could not read token from file")
}
// generate new token if token file does not exists
// or token got expired
if err != nil || !token.Valid() {
log.Println("Generating new token...")
token = getTokenFromWeb(config)
log.Println("Saving generated token to file")
saveToken(tokenFile, token)
}
return config.Client(context.Background(), token)
}
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser and then input the authorization code here: %v\n", authURL)
fmt.Printf("auth code: ")
var authCode string
if _, err := fmt.Scan(&authCode); err != nil {
log.Fatalf("Unable to read authorization code %v", err)
}
token, err := config.Exchange(context.TODO(), authCode)
if err != nil {
log.Fatalf("Unable to retrieve token from web %v", err)
}
return token
}
// Retrieves toke from a local file
func getTokenFromFile(file string) (*oauth2.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
token := &oauth2.Token{}
err = json.NewDecoder(f).Decode(token)
return token, err
}
// Saves a token to a file path
func saveToken(path string, token *oauth2.Token) {
fmt.Printf("Saving credentials file to: %s\n", path)
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatalf("Unable to cache oauth token: %v", err)
}
defer f.Close()
json.NewEncoder(f).Encode(token)
}
func main() {
fmt.Println("GDOWN CLI")
ctx := context.Background()
_ = godotenv.Load()
// if err != nil {
// fmt.Printf("Could not load .env file: %v\n", err)
// }
// TODO: Get these from `credentials.json` file instead of environment variable
CLIENT_ID := os.Getenv("GOOGLE_OAUTH_CLIENT_ID")
CLIENT_SECRET := os.Getenv("GOOGLE_OAUTH_CLIENT_SECRET")
// Check if Client ID and Secret are added
if CLIENT_ID == "" {
log.Fatal("GOOGLE_OAUTH_CLIENT_ID environment variable not present")
}
if CLIENT_SECRET == "" {
log.Fatal("GOOGLE_OAUTH_CLIENT_SECRET environment variable not present")
}
// fmt.Printf("CLIENT_ID: %v\n", CLIENT_ID)
// fmt.Printf("CLIENT_SECRET: %v\n", CLIENT_SECRET)
config := &oauth2.Config{
RedirectURL: "http://localhost:8000/auth/google/callback",
ClientID: CLIENT_ID,
ClientSecret: CLIENT_SECRET,
Scopes: []string{
// See information about your Google Drive files
"https://www.googleapis.com/auth/drive.metadata.readonly",
// See and download all your Google Drive Files
"https://www.googleapis.com/auth/drive.readonly",
},
Endpoint: google.Endpoint,
}
client := getClient(config)
driveService, err := drive.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
log.Fatalf("Unable to retrieve Drive client: %v", err)
}
// file1.txt
fileId := "1NuuL9qNo5BJYnfNqN_lxBOUN0P-AociQ"
file, err := driveService.Files.Get(fileId).Fields("id", "name", "mimeType").Do()
if err != nil {
log.Fatalf("Unable to retrieve file: %v", err)
}
fmt.Printf("fileId: %+v\n", file.Id)
fmt.Printf("fileName: %+v\n", file.Name)
fmt.Printf("fileMimeType: %+v\n", file.MimeType)
}
Create a gdown
subcommand gdown configure login
to complete the login process beforehand so that the login process is not required when downloading a file or folder.
Increasing the time before expiry of token
Google OAuth Authentication is implemented but is incomplete. Many manual steps are required to complete the authentication process like copying the URL from the terminal to the browser copying the code from the browser tab URL and pasting it into the terminal to complete the authentication. The process needs some automation.
Required Features:
Some Resources:
net/http
- Golang docs