ObtainOIDCAccessToken returns a valid OpenID Connect access token for the user if it's able to obtain one, otherwise it returns an empty string.
(ctx context.Context, logger slog.Logger, db database.Store, oidcConfig promoauth.OAuth2Config, userID uuid.UUID)
| 3179 | // ObtainOIDCAccessToken returns a valid OpenID Connect access token |
| 3180 | // for the user if it's able to obtain one, otherwise it returns an empty string. |
| 3181 | func ObtainOIDCAccessToken(ctx context.Context, logger slog.Logger, db database.Store, oidcConfig promoauth.OAuth2Config, userID uuid.UUID) (string, error) { |
| 3182 | link, err := db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ |
| 3183 | UserID: userID, |
| 3184 | LoginType: database.LoginTypeOIDC, |
| 3185 | }) |
| 3186 | if errors.Is(err, sql.ErrNoRows) { |
| 3187 | return "", nil |
| 3188 | } |
| 3189 | if err != nil { |
| 3190 | return "", xerrors.Errorf("get owner oidc link: %w", err) |
| 3191 | } |
| 3192 | |
| 3193 | if shouldRefresh, expiresAt := shouldRefreshOIDCToken(link); shouldRefresh { |
| 3194 | token, err := oidcConfig.TokenSource(ctx, &oauth2.Token{ |
| 3195 | AccessToken: link.OAuthAccessToken, |
| 3196 | RefreshToken: link.OAuthRefreshToken, |
| 3197 | // Use the expiresAt returned by shouldRefreshOIDCToken. |
| 3198 | // It will force a refresh with an expired time. |
| 3199 | Expiry: expiresAt, |
| 3200 | }).Token() |
| 3201 | if err != nil { |
| 3202 | // If OIDC fails to refresh, we return an empty string and don't fail. |
| 3203 | // There isn't a way to hard-opt in to OIDC from a template, so we don't |
| 3204 | // want to fail builds if users haven't authenticated for a while or something. |
| 3205 | return "", nil |
| 3206 | } |
| 3207 | link.OAuthAccessToken = token.AccessToken |
| 3208 | link.OAuthRefreshToken = token.RefreshToken |
| 3209 | link.OAuthExpiry = token.Expiry |
| 3210 | |
| 3211 | link, err = db.UpdateUserLink(ctx, database.UpdateUserLinkParams{ |
| 3212 | UserID: userID, |
| 3213 | LoginType: database.LoginTypeOIDC, |
| 3214 | OAuthAccessToken: link.OAuthAccessToken, |
| 3215 | OAuthAccessTokenKeyID: sql.NullString{}, // set by dbcrypt if required |
| 3216 | OAuthRefreshToken: link.OAuthRefreshToken, |
| 3217 | OAuthRefreshTokenKeyID: sql.NullString{}, // set by dbcrypt if required |
| 3218 | OAuthExpiry: link.OAuthExpiry, |
| 3219 | Claims: link.Claims, |
| 3220 | }) |
| 3221 | if err != nil { |
| 3222 | return "", xerrors.Errorf("update user link: %w", err) |
| 3223 | } |
| 3224 | logger.Info(ctx, "refreshed expired OIDC token for user during workspace build", slog.F("user_id", userID)) |
| 3225 | } |
| 3226 | |
| 3227 | return link.OAuthAccessToken, nil |
| 3228 | } |
| 3229 | |
| 3230 | func convertLogLevel(logLevel sdkproto.LogLevel) (database.LogLevel, error) { |
| 3231 | switch logLevel { |