Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit13842a3

Browse files
committed
feat(coderd): add OIDC ID token support with CODER_WORKSPACE_OWNER_OIDC_ID_TOKEN env var
- Add oauth_id_token column to user_links table (migration 402)- Capture and store ID token during OIDC authentication- Implement token refresh with ID token preservation- Add obtainOIDCIdToken() function for token retrieval- Pass ID token to provisioner via proto metadata- Expose as CODER_WORKSPACE_OWNER_OIDC_ID_TOKEN environment variable- Fix OAuthIdToken -> OAuthIDToken field naming (Go conventions)- Add OAuthIDToken to all UpdateUserLinkParams/InsertUserLinkParams structs- Update TypeScript and Go proto bindings- Regenerate database queries with correct column orderingThis enables Azure OIDC authentication which requires the ID tokenfor subsequent API calls.
1 parentffc3e81 commit13842a3

File tree

17 files changed

+127
-12
lines changed

17 files changed

+127
-12
lines changed

‎coderd/coderdtest/oidctest/helper.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func (*LoginHelper) ExpireOauthToken(t *testing.T, db database.Store, user *code
8888
OAuthExpiry:time.Now().Add(time.Hour*-1),
8989
UserID:link.UserID,
9090
LoginType:link.LoginType,
91+
OAuthIDToken:link.OAuthIDToken,
9192
Claims: database.UserLinkClaims{},
9293
})
9394
require.NoError(t,err,"expire user link")

‎coderd/database/dbgen/dbgen.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,7 @@ func UserLink(t testing.TB, db database.Store, orig database.UserLink) database.
10371037
OAuthAccessTokenKeyID:takeFirst(orig.OAuthAccessTokenKeyID, sql.NullString{}),
10381038
OAuthRefreshToken:takeFirst(orig.OAuthRefreshToken,uuid.NewString()),
10391039
OAuthRefreshTokenKeyID:takeFirst(orig.OAuthRefreshTokenKeyID, sql.NullString{}),
1040+
OAuthIDToken:takeFirst(orig.OAuthIDToken),
10401041
OAuthExpiry:takeFirst(orig.OAuthExpiry,dbtime.Now().Add(time.Hour*24)),
10411042
Claims:orig.Claims,
10421043
})

‎coderd/database/dump.sql‎

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Remove oauth_id_token column from user_links table
2+
ALTERTABLE user_links DROP COLUMN oauth_id_token;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Add oauth_id_token column to user_links table to support ID token storage for OIDC providers like Azure
2+
ALTERTABLE user_links ADD COLUMN oauth_id_tokentext DEFAULT''::textNOT NULL;

‎coderd/database/models.go‎

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries.sql.go‎

Lines changed: 19 additions & 7 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries/user_links.sql‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ INSERT INTO
3232
oauth_refresh_token,
3333
oauth_refresh_token_key_id,
3434
oauth_expiry,
35+
oauth_id_token,
3536
claims
3637
)
3738
VALUES
38-
( $1, $2, $3, $4, $5, $6, $7, $8, $9 ) RETURNING*;
39+
( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10 ) RETURNING*;
3940

4041
-- name: UpdateUserLinkedID :one
4142
UPDATE
@@ -54,9 +55,10 @@ SET
5455
oauth_refresh_token= $3,
5556
oauth_refresh_token_key_id= $4,
5657
oauth_expiry= $5,
57-
claims= $6
58+
oauth_id_token= $6,
59+
claims= $7
5860
WHERE
59-
user_id= $7AND login_type= $8 RETURNING*;
61+
user_id= $8AND login_type= $9 RETURNING*;
6062

6163
-- name: OIDCClaimFields :many
6264
-- OIDCClaimFields returns a list of distinct keys in the the merged_claims fields.

‎coderd/httpmw/apikey.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
354354
OAuthAccessTokenKeyID: sql.NullString{},// dbcrypt will update as required
355355
OAuthRefreshToken:link.OAuthRefreshToken,
356356
OAuthRefreshTokenKeyID: sql.NullString{},// dbcrypt will update as required
357+
OAuthIDToken:link.OAuthIDToken,
357358
OAuthExpiry:link.OAuthExpiry,
358359
// Refresh should keep the same debug context because we use
359360
// the original claims for the group/role sync.

‎coderd/provisionerdserver/provisionerdserver.go‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,13 +544,18 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
544544
}
545545

546546
varworkspaceOwnerOIDCAccessTokenstring
547+
varworkspaceOwnerOIDCIdTokenstring
547548
// The check `s.OIDCConfig != nil` is not as strict, since it can be an interface
548549
// pointing to a typed nil.
549550
if!reflect.ValueOf(s.OIDCConfig).IsNil() {
550551
workspaceOwnerOIDCAccessToken,err=obtainOIDCAccessToken(ctx,s.Database,s.OIDCConfig,owner.ID)
551552
iferr!=nil {
552553
returnnil,failJob(fmt.Sprintf("obtain OIDC access token: %s",err))
553554
}
555+
workspaceOwnerOIDCIdToken,err=obtainOIDCIdToken(ctx,s.Database,s.OIDCConfig,owner.ID)
556+
iferr!=nil {
557+
returnnil,failJob(fmt.Sprintf("obtain OIDC ID token: %s",err))
558+
}
554559
}
555560

556561
varsessionTokenstring
@@ -724,6 +729,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
724729
WorkspaceOwnerName:owner.Name,
725730
WorkspaceOwnerGroups:ownerGroupNames,
726731
WorkspaceOwnerOidcAccessToken:workspaceOwnerOIDCAccessToken,
732+
WorkspaceOwnerOidcIdToken:workspaceOwnerOIDCIdToken,
727733
WorkspaceId:workspace.ID.String(),
728734
WorkspaceOwnerId:owner.ID.String(),
729735
TemplateId:template.ID.String(),
@@ -3145,6 +3151,9 @@ func obtainOIDCAccessToken(ctx context.Context, db database.Store, oidcConfig pr
31453151
link.OAuthRefreshToken=token.RefreshToken
31463152
link.OAuthExpiry=token.Expiry
31473153

3154+
// Extract the ID token from the refreshed token if available
3155+
idToken,_:=token.Extra("id_token").(string)
3156+
31483157
link,err=db.UpdateUserLink(ctx, database.UpdateUserLinkParams{
31493158
UserID:userID,
31503159
LoginType:database.LoginTypeOIDC,
@@ -3153,6 +3162,7 @@ func obtainOIDCAccessToken(ctx context.Context, db database.Store, oidcConfig pr
31533162
OAuthRefreshToken:link.OAuthRefreshToken,
31543163
OAuthRefreshTokenKeyID: sql.NullString{},// set by dbcrypt if required
31553164
OAuthExpiry:link.OAuthExpiry,
3165+
OAuthIDToken:idToken,
31563166
Claims:link.Claims,
31573167
})
31583168
iferr!=nil {
@@ -3163,6 +3173,61 @@ func obtainOIDCAccessToken(ctx context.Context, db database.Store, oidcConfig pr
31633173
returnlink.OAuthAccessToken,nil
31643174
}
31653175

3176+
// obtainOIDCIdToken returns a valid OpenID Connect ID token
3177+
// for the user if it's able to obtain one, otherwise it returns an empty string.
3178+
// The ID token is used by some providers like Azure for authentication.
3179+
funcobtainOIDCIdToken(ctx context.Context,db database.Store,oidcConfig promoauth.OAuth2Config,userID uuid.UUID) (string,error) {
3180+
link,err:=db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{
3181+
UserID:userID,
3182+
LoginType:database.LoginTypeOIDC,
3183+
})
3184+
iferrors.Is(err,sql.ErrNoRows) {
3185+
return"",nil
3186+
}
3187+
iferr!=nil {
3188+
return"",xerrors.Errorf("get owner oidc link: %w",err)
3189+
}
3190+
3191+
// If the token is expired and we have a refresh token, refresh it
3192+
iflink.OAuthExpiry.Before(dbtime.Now())&&!link.OAuthExpiry.IsZero()&&link.OAuthRefreshToken!="" {
3193+
token,err:=oidcConfig.TokenSource(ctx,&oauth2.Token{
3194+
AccessToken:link.OAuthAccessToken,
3195+
RefreshToken:link.OAuthRefreshToken,
3196+
Expiry:link.OAuthExpiry,
3197+
}).Token()
3198+
iferr!=nil {
3199+
// If OIDC fails to refresh, we return an empty string and don't fail.
3200+
// There isn't a way to hard-opt in to OIDC from a template, so we don't
3201+
// want to fail builds if users haven't authenticated for a while or something.
3202+
return"",nil
3203+
}
3204+
3205+
// Extract the ID token from the refreshed token
3206+
idToken,_:=token.Extra("id_token").(string)
3207+
link.OAuthAccessToken=token.AccessToken
3208+
link.OAuthRefreshToken=token.RefreshToken
3209+
link.OAuthExpiry=token.Expiry
3210+
link.OAuthIDToken=idToken
3211+
3212+
link,err=db.UpdateUserLink(ctx, database.UpdateUserLinkParams{
3213+
UserID:userID,
3214+
LoginType:database.LoginTypeOIDC,
3215+
OAuthAccessToken:link.OAuthAccessToken,
3216+
OAuthAccessTokenKeyID: sql.NullString{},// set by dbcrypt if required
3217+
OAuthRefreshToken:link.OAuthRefreshToken,
3218+
OAuthRefreshTokenKeyID: sql.NullString{},// set by dbcrypt if required
3219+
OAuthExpiry:link.OAuthExpiry,
3220+
OAuthIDToken:link.OAuthIDToken,
3221+
Claims:link.Claims,
3222+
})
3223+
iferr!=nil {
3224+
return"",xerrors.Errorf("update user link: %w",err)
3225+
}
3226+
}
3227+
3228+
returnlink.OAuthIDToken,nil
3229+
}
3230+
31663231
funcconvertLogLevel(logLevel sdkproto.LogLevel) (database.LogLevel,error) {
31673232
switchlogLevel {
31683233
casesdkproto.LogLevel_TRACE:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp