1// Package oauth is a library for Go client applications that need to perform OAuth authorization
2// against a server, typically GitHub.com.
3package oauth
4
5import (
6	"errors"
7	"fmt"
8	"io"
9	"net/http"
10	"net/url"
11
12	"github.com/cli/oauth/api"
13	"github.com/cli/oauth/device"
14)
15
16type httpClient interface {
17	PostForm(string, url.Values) (*http.Response, error)
18}
19
20// Host defines the endpoints used to authorize against an OAuth server.
21type Host struct {
22	DeviceCodeURL string
23	AuthorizeURL  string
24	TokenURL      string
25}
26
27// GitHubHost constructs a Host from the given URL to a GitHub instance.
28func GitHubHost(hostURL string) *Host {
29	u, _ := url.Parse(hostURL)
30
31	return &Host{
32		DeviceCodeURL: fmt.Sprintf("%s://%s/login/device/code", u.Scheme, u.Host),
33		AuthorizeURL:  fmt.Sprintf("%s://%s/login/oauth/authorize", u.Scheme, u.Host),
34		TokenURL:      fmt.Sprintf("%s://%s/login/oauth/access_token", u.Scheme, u.Host),
35	}
36}
37
38// Flow facilitates a single OAuth authorization flow.
39type Flow struct {
40	// The hostname to authorize the app with.
41	//
42	// Deprecated: Use Host instead.
43	Hostname string
44	// Host configuration to authorize the app with.
45	Host *Host
46	// OAuth scopes to request from the user.
47	Scopes []string
48	// OAuth application ID.
49	ClientID string
50	// OAuth application secret. Only applicable in web application flow.
51	ClientSecret string
52	// The localhost URI for web application flow callback, e.g. "http://127.0.0.1/callback".
53	CallbackURI string
54
55	// Display a one-time code to the user. Receives the code and the browser URL as arguments. Defaults to printing the
56	// code to the user on Stdout with instructions to copy the code and to press Enter to continue in their browser.
57	DisplayCode func(string, string) error
58	// Open a web browser at a URL. Defaults to opening the default system browser.
59	BrowseURL func(string) error
60	// Render an HTML page to the user upon completion of web application flow. The default is to
61	// render a simple message that informs the user they can close the browser tab and return to the app.
62	WriteSuccessHTML func(io.Writer)
63
64	// The HTTP client to use for API POST requests. Defaults to http.DefaultClient.
65	HTTPClient httpClient
66	// The stream to listen to keyboard input on. Defaults to os.Stdin.
67	Stdin io.Reader
68	// The stream to print UI messages to. Defaults to os.Stdout.
69	Stdout io.Writer
70}
71
72// DetectFlow tries to perform Device flow first and falls back to Web application flow.
73func (oa *Flow) DetectFlow() (*api.AccessToken, error) {
74	accessToken, err := oa.DeviceFlow()
75	if errors.Is(err, device.ErrUnsupported) {
76		return oa.WebAppFlow()
77	}
78	return accessToken, err
79}
80