Skip to content

Commit 863c2e7

Browse files
authored
feat: allow storing extra oauth token properties in the database (#10152)
1 parent 35538e1 commit 863c2e7

File tree

25 files changed

+223
-60
lines changed

25 files changed

+223
-60
lines changed

cli/server.go

+2
Original file line numberDiff line numberDiff line change
@@ -2251,6 +2251,8 @@ func parseExternalAuthProvidersFromEnv(prefix string, environ []string) ([]coder
22512251
provider.NoRefresh = b
22522252
case "SCOPES":
22532253
provider.Scopes = strings.Split(v.Value, " ")
2254+
case "EXTRA_TOKEN_KEYS":
2255+
provider.ExtraTokenKeys = strings.Split(v.Value, " ")
22542256
case "APP_INSTALL_URL":
22552257
provider.AppInstallURL = v.Value
22562258
case "APP_INSTALLATIONS_URL":

coderd/apidoc/docs.go

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderdtest/oidctest/idp.go

+12
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ type FakeIDP struct {
6868
// "Authorized Redirect URLs". This can be used to emulate that.
6969
hookValidRedirectURL func(redirectURL string) error
7070
hookUserInfo func(email string) (jwt.MapClaims, error)
71+
hookMutateToken func(token map[string]interface{})
7172
fakeCoderd func(req *http.Request) (*http.Response, error)
7273
hookOnRefresh func(email string) error
7374
// Custom authentication for the client. This is useful if you want
@@ -112,6 +113,14 @@ func WithRefresh(hook func(email string) error) func(*FakeIDP) {
112113
}
113114
}
114115

116+
// WithExtra returns extra fields that be accessed on the returned Oauth Token.
117+
// These extra fields can override the default fields (id_token, access_token, etc).
118+
func WithMutateToken(mutateToken func(token map[string]interface{})) func(*FakeIDP) {
119+
return func(f *FakeIDP) {
120+
f.hookMutateToken = mutateToken
121+
}
122+
}
123+
115124
func WithCustomClientAuth(hook func(t testing.TB, req *http.Request) (url.Values, error)) func(*FakeIDP) {
116125
return func(f *FakeIDP) {
117126
f.hookAuthenticateClient = hook
@@ -621,6 +630,9 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
621630
"expires_in": int64((time.Minute * 5).Seconds()),
622631
"id_token": f.encodeClaims(t, claims),
623632
}
633+
if f.hookMutateToken != nil {
634+
f.hookMutateToken(token)
635+
}
624636
// Store the claims for the next refresh
625637
f.refreshIDTokenClaims.Store(refreshToken, claims)
626638

coderd/database/dbfake/dbfake.go

+2
Original file line numberDiff line numberDiff line change
@@ -4246,6 +4246,7 @@ func (q *FakeQuerier) InsertExternalAuthLink(_ context.Context, arg database.Ins
42464246
OAuthRefreshToken: arg.OAuthRefreshToken,
42474247
OAuthRefreshTokenKeyID: arg.OAuthRefreshTokenKeyID,
42484248
OAuthExpiry: arg.OAuthExpiry,
4249+
OAuthExtra: arg.OAuthExtra,
42494250
}
42504251
q.externalAuthLinks = append(q.externalAuthLinks, gitAuthLink)
42514252
return gitAuthLink, nil
@@ -5301,6 +5302,7 @@ func (q *FakeQuerier) UpdateExternalAuthLink(_ context.Context, arg database.Upd
53015302
gitAuthLink.OAuthRefreshToken = arg.OAuthRefreshToken
53025303
gitAuthLink.OAuthRefreshTokenKeyID = arg.OAuthRefreshTokenKeyID
53035304
gitAuthLink.OAuthExpiry = arg.OAuthExpiry
5305+
gitAuthLink.OAuthExtra = arg.OAuthExtra
53045306
q.externalAuthLinks[index] = gitAuthLink
53055307

53065308
return gitAuthLink, nil

coderd/database/dbgen/dbgen.go

+2
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ func UserLink(t testing.TB, db database.Store, orig database.UserLink) database.
514514
}
515515

516516
func ExternalAuthLink(t testing.TB, db database.Store, orig database.ExternalAuthLink) database.ExternalAuthLink {
517+
msg := takeFirst(&orig.OAuthExtra, &pqtype.NullRawMessage{})
517518
link, err := db.InsertExternalAuthLink(genCtx, database.InsertExternalAuthLinkParams{
518519
ProviderID: takeFirst(orig.ProviderID, uuid.New().String()),
519520
UserID: takeFirst(orig.UserID, uuid.New()),
@@ -524,6 +525,7 @@ func ExternalAuthLink(t testing.TB, db database.Store, orig database.ExternalAut
524525
OAuthExpiry: takeFirst(orig.OAuthExpiry, dbtime.Now().Add(time.Hour*24)),
525526
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
526527
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
528+
OAuthExtra: *msg,
527529
})
528530

529531
require.NoError(t, err, "insert external auth link")

coderd/database/dump.sql

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE external_auth_links DROP COLUMN "oauth_extra";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE external_auth_links ADD COLUMN "oauth_extra" jsonb;

coderd/database/models.go

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

+35-24
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/externalauth.sql

+6-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ INSERT INTO external_auth_links (
1414
oauth_access_token_key_id,
1515
oauth_refresh_token,
1616
oauth_refresh_token_key_id,
17-
oauth_expiry
17+
oauth_expiry,
18+
oauth_extra
1819
) VALUES (
1920
$1,
2021
$2,
@@ -24,7 +25,8 @@ INSERT INTO external_auth_links (
2425
$6,
2526
$7,
2627
$8,
27-
$9
28+
$9,
29+
$10
2830
) RETURNING *;
2931

3032
-- name: UpdateExternalAuthLink :one
@@ -34,5 +36,6 @@ UPDATE external_auth_links SET
3436
oauth_access_token_key_id = $5,
3537
oauth_refresh_token = $6,
3638
oauth_refresh_token_key_id = $7,
37-
oauth_expiry = $8
39+
oauth_expiry = $8,
40+
oauth_extra = $9
3841
WHERE provider_id = $1 AND user_id = $2 RETURNING *;

coderd/database/sqlc.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ overrides:
5353
oauth_id_token: OAuthIDToken
5454
oauth_refresh_token: OAuthRefreshToken
5555
oauth_refresh_token_key_id: OAuthRefreshTokenKeyID
56+
oauth_extra: OAuthExtra
5657
parameter_type_system_hcl: ParameterTypeSystemHCL
5758
userstatus: UserStatus
5859
gitsshkey: GitSSHKey

coderd/externalauth.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/coder/coder/v2/coderd/httpapi"
1515
"github.com/coder/coder/v2/coderd/httpmw"
1616
"github.com/coder/coder/v2/codersdk"
17+
"github.com/sqlc-dev/pqtype"
1718
)
1819

1920
// @Summary Get external auth by ID
@@ -132,6 +133,8 @@ func (api *API) postExternalAuthDeviceByID(rw http.ResponseWriter, r *http.Reque
132133
OAuthRefreshToken: token.RefreshToken,
133134
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will set as required
134135
OAuthExpiry: token.Expiry,
136+
// No extra data from device auth!
137+
OAuthExtra: pqtype.NullRawMessage{},
135138
})
136139
if err != nil {
137140
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@@ -150,6 +153,7 @@ func (api *API) postExternalAuthDeviceByID(rw http.ResponseWriter, r *http.Reque
150153
OAuthRefreshToken: token.RefreshToken,
151154
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
152155
OAuthExpiry: token.Expiry,
156+
OAuthExtra: pqtype.NullRawMessage{},
153157
})
154158
if err != nil {
155159
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@@ -201,7 +205,15 @@ func (api *API) externalAuthCallback(externalAuthConfig *externalauth.Config) ht
201205
apiKey = httpmw.APIKey(r)
202206
)
203207

204-
_, err := api.Database.GetExternalAuthLink(ctx, database.GetExternalAuthLinkParams{
208+
extra, err := externalAuthConfig.GenerateTokenExtra(state.Token)
209+
if err != nil {
210+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
211+
Message: "Failed to generate token extra.",
212+
Detail: err.Error(),
213+
})
214+
return
215+
}
216+
_, err = api.Database.GetExternalAuthLink(ctx, database.GetExternalAuthLinkParams{
205217
ProviderID: externalAuthConfig.ID,
206218
UserID: apiKey.UserID,
207219
})
@@ -224,6 +236,7 @@ func (api *API) externalAuthCallback(externalAuthConfig *externalauth.Config) ht
224236
OAuthRefreshToken: state.Token.RefreshToken,
225237
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will set as required
226238
OAuthExpiry: state.Token.Expiry,
239+
OAuthExtra: extra,
227240
})
228241
if err != nil {
229242
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@@ -242,6 +255,7 @@ func (api *API) externalAuthCallback(externalAuthConfig *externalauth.Config) ht
242255
OAuthRefreshToken: state.Token.RefreshToken,
243256
OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required
244257
OAuthExpiry: state.Token.Expiry,
258+
OAuthExtra: extra,
245259
})
246260
if err != nil {
247261
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{

0 commit comments

Comments
 (0)