-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathexternalauth.go
More file actions
209 lines (189 loc) · 8.03 KB
/
externalauth.go
File metadata and controls
209 lines (189 loc) · 8.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package codersdk
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
)
// EnhancedExternalAuthProvider is a constant that represents enhanced
// support for a type of external authentication. All of the Git providers
// are examples of enhanced, because they support intercepting "git clone".
type EnhancedExternalAuthProvider string
func (e EnhancedExternalAuthProvider) String() string {
return string(e)
}
// Git returns whether the provider is a Git provider.
func (e EnhancedExternalAuthProvider) Git() bool {
switch e {
case EnhancedExternalAuthProviderGitHub,
EnhancedExternalAuthProviderGitLab,
EnhancedExternalAuthProviderBitBucketCloud,
EnhancedExternalAuthProviderBitBucketServer,
EnhancedExternalAuthProviderAzureDevops,
EnhancedExternalAuthProviderAzureDevopsEntra,
EnhancedExternalAuthProviderGitea:
return true
default:
return false
}
}
const (
EnhancedExternalAuthProviderAzureDevops EnhancedExternalAuthProvider = "azure-devops"
// Authenticate to ADO using an app registration in Entra ID
EnhancedExternalAuthProviderAzureDevopsEntra EnhancedExternalAuthProvider = "azure-devops-entra"
EnhancedExternalAuthProviderGitHub EnhancedExternalAuthProvider = "github"
EnhancedExternalAuthProviderGitLab EnhancedExternalAuthProvider = "gitlab"
// EnhancedExternalAuthProviderBitBucketCloud is the Bitbucket Cloud provider.
// Not to be confused with the self-hosted 'EnhancedExternalAuthProviderBitBucketServer'
EnhancedExternalAuthProviderBitBucketCloud EnhancedExternalAuthProvider = "bitbucket-cloud"
EnhancedExternalAuthProviderBitBucketServer EnhancedExternalAuthProvider = "bitbucket-server"
EnhancedExternalAuthProviderSlack EnhancedExternalAuthProvider = "slack"
EnhancedExternalAuthProviderJFrog EnhancedExternalAuthProvider = "jfrog"
EnhancedExternalAuthProviderGitea EnhancedExternalAuthProvider = "gitea"
)
type ExternalAuth struct {
Authenticated bool `json:"authenticated"`
Device bool `json:"device"`
DisplayName string `json:"display_name"`
SupportsRevocation bool `json:"supports_revocation"`
// User is the user that authenticated with the provider.
User *ExternalAuthUser `json:"user"`
// AppInstallable is true if the request for app installs was successful.
AppInstallable bool `json:"app_installable"`
// AppInstallations are the installations that the user has access to.
AppInstallations []ExternalAuthAppInstallation `json:"installations"`
// AppInstallURL is the URL to install the app.
AppInstallURL string `json:"app_install_url"`
}
type ListUserExternalAuthResponse struct {
Providers []ExternalAuthLinkProvider `json:"providers"`
// Links are all the authenticated links for the user.
// If a link has a provider ID that does not exist, then that provider
// is no longer configured, rendering it unusable. It is still valuable
// to include these links so that the user can unlink them.
Links []ExternalAuthLink `json:"links"`
}
type DeleteExternalAuthByIDResponse struct {
// TokenRevoked set to true if token revocation was attempted and was successful
TokenRevoked bool `json:"token_revoked"`
TokenRevocationError string `json:"token_revocation_error,omitempty"`
}
// ExternalAuthLink is a link between a user and an external auth provider.
// It excludes information that requires a token to access, so can be statically
// built from the database and configs.
type ExternalAuthLink struct {
ProviderID string `json:"provider_id"`
CreatedAt time.Time `json:"created_at" format:"date-time"`
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
HasRefreshToken bool `json:"has_refresh_token"`
Expires time.Time `json:"expires" format:"date-time"`
Authenticated bool `json:"authenticated"`
ValidateError string `json:"validate_error"`
}
// ExternalAuthLinkProvider are the static details of a provider.
type ExternalAuthLinkProvider struct {
ID string `json:"id"`
Type string `json:"type"`
Device bool `json:"device"`
DisplayName string `json:"display_name"`
DisplayIcon string `json:"display_icon"`
AllowRefresh bool `json:"allow_refresh"`
AllowValidate bool `json:"allow_validate"`
SupportsRevocation bool `json:"supports_revocation"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
}
type ExternalAuthAppInstallation struct {
ID int `json:"id"`
Account ExternalAuthUser `json:"account"`
ConfigureURL string `json:"configure_url"`
}
type ExternalAuthUser struct {
ID int64 `json:"id"`
Login string `json:"login"`
AvatarURL string `json:"avatar_url"`
ProfileURL string `json:"profile_url"`
Name string `json:"name"`
}
// ExternalAuthDevice is the response from the device authorization endpoint.
// See: https://tools.ietf.org/html/rfc8628#section-3.2
type ExternalAuthDevice struct {
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"`
VerificationURI string `json:"verification_uri"`
ExpiresIn int `json:"expires_in"`
Interval int `json:"interval"`
}
type ExternalAuthDeviceExchange struct {
DeviceCode string `json:"device_code"`
}
func (c *Client) ExternalAuthDeviceByID(ctx context.Context, provider string) (ExternalAuthDevice, error) {
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/external-auth/%s/device", provider), nil)
if err != nil {
return ExternalAuthDevice{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return ExternalAuthDevice{}, ReadBodyAsError(res)
}
var extAuth ExternalAuthDevice
return extAuth, json.NewDecoder(res.Body).Decode(&extAuth)
}
// ExchangeGitAuth exchanges a device code for an external auth token.
func (c *Client) ExternalAuthDeviceExchange(ctx context.Context, provider string, req ExternalAuthDeviceExchange) error {
res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/external-auth/%s/device", provider), req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusNoContent {
return ReadBodyAsError(res)
}
return nil
}
// ExternalAuthByID returns the external auth for the given provider by ID.
func (c *Client) ExternalAuthByID(ctx context.Context, provider string) (ExternalAuth, error) {
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/external-auth/%s", provider), nil)
if err != nil {
return ExternalAuth{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return ExternalAuth{}, ReadBodyAsError(res)
}
var extAuth ExternalAuth
return extAuth, json.NewDecoder(res.Body).Decode(&extAuth)
}
// UnlinkExternalAuthByID deletes the external auth for the given provider by ID
// for the user. This does not revoke the token from the IDP.
func (c *Client) UnlinkExternalAuthByID(ctx context.Context, provider string) (DeleteExternalAuthByIDResponse, error) {
noRevoke := DeleteExternalAuthByIDResponse{TokenRevoked: false}
res, err := c.Request(ctx, http.MethodDelete, fmt.Sprintf("/api/v2/external-auth/%s", provider), nil)
if err != nil {
return noRevoke, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return noRevoke, ReadBodyAsError(res)
}
var resp DeleteExternalAuthByIDResponse
err = json.NewDecoder(res.Body).Decode(&resp)
if err != nil {
return noRevoke, err
}
return resp, nil
}
// ListExternalAuths returns the available external auth providers and the user's
// authenticated links if they exist.
func (c *Client) ListExternalAuths(ctx context.Context) (ListUserExternalAuthResponse, error) {
res, err := c.Request(ctx, http.MethodGet, "/api/v2/external-auth", nil)
if err != nil {
return ListUserExternalAuthResponse{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return ListUserExternalAuthResponse{}, ReadBodyAsError(res)
}
var extAuth ListUserExternalAuthResponse
return extAuth, json.NewDecoder(res.Body).Decode(&extAuth)
}