Skip to content

Commit 6dfce11

Browse files
jamierocksmislav
andcommitted
Allow for arbitrary OAuth hosts
Not all OAuth hosts use the same routes as GitHub, for example: - Microsoft use /oauth2/v2.0/devicecode - Google use /device/code - Auth0 use /oauth/device/code Similar differences are present for the authorise and access token routes too. This commit introduces a concept of a Host, which is a container for the endpoints that the library uses. After this, the Hostname field is deprecated. Co-authored-by: Mislav Marohnić <[email protected]>
1 parent 63e88b3 commit 6dfce11

File tree

5 files changed

+38
-18
lines changed

5 files changed

+38
-18
lines changed

examples_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
// flow support is globally available, but enables logging in to hosted GitHub instances as well.
1111
func Example() {
1212
flow := &Flow{
13-
Hostname: "github.com",
13+
Host: GitHubHost("https://github.com"),
1414
ClientID: os.Getenv("OAUTH_CLIENT_ID"),
1515
ClientSecret: os.Getenv("OAUTH_CLIENT_SECRET"), // only applicable to web app flow
1616
CallbackURI: "http://127.0.0.1/callback", // only applicable to web app flow

go.sum

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
github.com/cli/browser v1.0.0 h1:RIleZgXrhdiCVgFBSjtWwkLPUCWyhhhN5k5HGSBt1js=
22
github.com/cli/browser v1.0.0/go.mod h1:IEWkHYbLjkhtjwwWlwTHW2lGxeS5gezEQBMLTwDHf5Q=
3+
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
34
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=

oauth.go

+23-13
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,32 @@ type httpClient interface {
1717
PostForm(string, url.Values) (*http.Response, error)
1818
}
1919

20+
// Host defines the endpoints used to authorize against an OAuth server.
21+
type 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.
28+
func 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+
2038
// Flow facilitates a single OAuth authorization flow.
2139
type Flow struct {
22-
// The host to authorize the app with.
40+
// The hostname to authorize the app with.
41+
//
42+
// Deprecated: Use Host instead.
2343
Hostname string
44+
// Host configuration to authorize the app with.
45+
Host *Host
2446
// OAuth scopes to request from the user.
2547
Scopes []string
2648
// OAuth application ID.
@@ -47,18 +69,6 @@ type Flow struct {
4769
Stdout io.Writer
4870
}
4971

50-
func deviceInitURL(host string) string {
51-
return fmt.Sprintf("https://%s/login/device/code", host)
52-
}
53-
54-
func webappInitURL(host string) string {
55-
return fmt.Sprintf("https://%s/login/oauth/authorize", host)
56-
}
57-
58-
func tokenURL(host string) string {
59-
return fmt.Sprintf("https://%s/login/oauth/access_token", host)
60-
}
61-
6272
// DetectFlow tries to perform Device flow first and falls back to Web application flow.
6373
func (oa *Flow) DetectFlow() (*api.AccessToken, error) {
6474
accessToken, err := oa.DeviceFlow()

oauth_device.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ func (oa *Flow) DeviceFlow() (*api.AccessToken, error) {
2828
if stdout == nil {
2929
stdout = os.Stdout
3030
}
31+
host := oa.Host
32+
if host == nil {
33+
host = GitHubHost("https://" + oa.Hostname)
34+
}
3135

32-
code, err := device.RequestCode(httpClient, deviceInitURL(oa.Hostname), oa.ClientID, oa.Scopes)
36+
code, err := device.RequestCode(httpClient, host.DeviceCodeURL, oa.ClientID, oa.Scopes)
3337
if err != nil {
3438
return nil, err
3539
}
@@ -54,7 +58,7 @@ func (oa *Flow) DeviceFlow() (*api.AccessToken, error) {
5458
return nil, fmt.Errorf("error opening the web browser: %w", err)
5559
}
5660

57-
return device.PollToken(httpClient, tokenURL(oa.Hostname), oa.ClientID, code)
61+
return device.PollToken(httpClient, host.TokenURL, oa.ClientID, code)
5862
}
5963

6064
func waitForEnter(r io.Reader) error {

oauth_webapp.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ import (
1212
// WebAppFlow starts a local HTTP server, opens the web browser to initiate the OAuth Web application
1313
// flow, blocks until the user completes authorization and is redirected back, and returns the access token.
1414
func (oa *Flow) WebAppFlow() (*api.AccessToken, error) {
15+
host := oa.Host
16+
if host == nil {
17+
host = GitHubHost("https://" + oa.Hostname)
18+
}
19+
1520
flow, err := webapp.InitFlow()
1621
if err != nil {
1722
return nil, err
@@ -23,7 +28,7 @@ func (oa *Flow) WebAppFlow() (*api.AccessToken, error) {
2328
Scopes: oa.Scopes,
2429
AllowSignup: true,
2530
}
26-
browserURL, err := flow.BrowserURL(webappInitURL(oa.Hostname), params)
31+
browserURL, err := flow.BrowserURL(host.AuthorizeURL, params)
2732
if err != nil {
2833
return nil, err
2934
}
@@ -47,5 +52,5 @@ func (oa *Flow) WebAppFlow() (*api.AccessToken, error) {
4752
httpClient = http.DefaultClient
4853
}
4954

50-
return flow.AccessToken(httpClient, tokenURL(oa.Hostname), oa.ClientSecret)
55+
return flow.AccessToken(httpClient, host.TokenURL, oa.ClientSecret)
5156
}

0 commit comments

Comments
 (0)