MCPcopy Index your code
hub / github.com/coder/coder / OIDCAccessToken

Method OIDCAccessToken

coderd/mcp.go:65–126  ·  view source on GitHub ↗

OIDCAccessToken implements mcpclient.UserOIDCTokenSource. It refreshes expired tokens and persists the refreshed token back to user_links. The chatd dbauthz subject does not grant ResourceSystem.Read or ResourceUser.UpdatePersonal, so DB calls elevate to AsSystemRestricted; the per-user authorizatio

(ctx context.Context, userID uuid.UUID)

Source from the content-addressed store, hash-verified

63// elevate to AsSystemRestricted; the per-user authorization is
64// already enforced by the API handler that owns ctx.
65func (s *oidcMCPTokenSource) OIDCAccessToken(ctx context.Context, userID uuid.UUID) (string, error) {
66 //nolint:gocritic // user_links read needs system access; the
67 // caller's user identity is supplied via the userID parameter.
68 dbCtx := dbauthz.AsSystemRestricted(ctx)
69 link, err := s.db.GetUserLinkByUserIDLoginType(dbCtx, database.GetUserLinkByUserIDLoginTypeParams{
70 UserID: userID,
71 LoginType: database.LoginTypeOIDC,
72 })
73 if errors.Is(err, sql.ErrNoRows) {
74 return "", nil
75 }
76 if err != nil {
77 return "", xerrors.Errorf("get oidc user link: %w", err)
78 }
79
80 if shouldRefresh, expiresAt := shouldRefreshOIDCToken(link); shouldRefresh {
81 token, err := s.config.TokenSource(ctx, &oauth2.Token{
82 AccessToken: link.OAuthAccessToken,
83 RefreshToken: link.OAuthRefreshToken,
84 // Use the expiresAt returned by shouldRefreshOIDCToken.
85 // It will force a refresh with an expired time.
86 Expiry: expiresAt,
87 }).Token()
88 if err != nil {
89 // Don't fail the request; the upstream MCP server will see no
90 // Authorization header and can return a 401 if it requires one.
91 s.logger.Warn(ctx, "failed to refresh OIDC token for MCP request",
92 slog.F("user_id", userID),
93 slog.Error(err),
94 )
95 return "", nil
96 }
97 link.OAuthAccessToken = token.AccessToken
98 link.OAuthRefreshToken = token.RefreshToken
99 link.OAuthExpiry = token.Expiry
100
101 // Persist on a detached context so a canceled chat request
102 // cannot drop a refresh-token rotation, see PR #24332.
103 persistCtx, persistCancel := context.WithTimeout(
104 context.WithoutCancel(dbCtx), 10*time.Second,
105 )
106 link, err = s.db.UpdateUserLink(persistCtx, database.UpdateUserLinkParams{
107 UserID: userID,
108 LoginType: database.LoginTypeOIDC,
109 OAuthAccessToken: link.OAuthAccessToken,
110 OAuthAccessTokenKeyID: sql.NullString{}, // set by dbcrypt if required
111 OAuthRefreshToken: link.OAuthRefreshToken,
112 OAuthRefreshTokenKeyID: sql.NullString{}, // set by dbcrypt if required
113 OAuthExpiry: link.OAuthExpiry,
114 Claims: link.Claims,
115 })
116 persistCancel()
117 if err != nil {
118 return "", xerrors.Errorf("update user link after oidc refresh: %w", err)
119 }
120 s.logger.Info(ctx, "refreshed expired OIDC token for MCP request",
121 slog.F("user_id", userID),
122 )

Callers

nothing calls this directly

Calls 10

AsSystemRestrictedFunction · 0.92
shouldRefreshOIDCTokenFunction · 0.70
TokenMethod · 0.65
TokenSourceMethod · 0.65
UpdateUserLinkMethod · 0.65
IsMethod · 0.45
ErrorfMethod · 0.45
ErrorMethod · 0.45
InfoMethod · 0.45

Tested by

no test coverage detected