refreshMCPTokenIfNeeded delegates to mcpclient.RefreshOAuth2Token and persists the result to the database when a refresh occurs. The logger should carry chat-scoped fields so log lines can be correlated with specific chat requests.
( ctx context.Context, logger slog.Logger, cfg database.MCPServerConfig, tok database.MCPServerUserToken, )
| 9941 | // The logger should carry chat-scoped fields so log lines can be |
| 9942 | // correlated with specific chat requests. |
| 9943 | func (p *Server) refreshMCPTokenIfNeeded( |
| 9944 | ctx context.Context, |
| 9945 | logger slog.Logger, |
| 9946 | cfg database.MCPServerConfig, |
| 9947 | tok database.MCPServerUserToken, |
| 9948 | ) (database.MCPServerUserToken, error) { |
| 9949 | result, err := mcpclient.RefreshOAuth2Token(ctx, cfg, tok) |
| 9950 | if err != nil { |
| 9951 | return tok, err |
| 9952 | } |
| 9953 | |
| 9954 | if !result.Refreshed { |
| 9955 | return tok, nil |
| 9956 | } |
| 9957 | |
| 9958 | logger.Info(ctx, "refreshed MCP oauth2 token", |
| 9959 | slog.F("server_slug", cfg.Slug), |
| 9960 | slog.F("user_id", tok.UserID), |
| 9961 | ) |
| 9962 | |
| 9963 | var expiry sql.NullTime |
| 9964 | if !result.Expiry.IsZero() { |
| 9965 | expiry = sql.NullTime{Time: result.Expiry, Valid: true} |
| 9966 | } |
| 9967 | |
| 9968 | //nolint:gocritic // Chatd needs system-level write access to |
| 9969 | // persist the refreshed OAuth2 token for the user. |
| 9970 | updated, err := p.db.UpsertMCPServerUserToken( |
| 9971 | dbauthz.AsSystemRestricted(ctx), |
| 9972 | database.UpsertMCPServerUserTokenParams{ |
| 9973 | MCPServerConfigID: tok.MCPServerConfigID, |
| 9974 | UserID: tok.UserID, |
| 9975 | AccessToken: result.AccessToken, |
| 9976 | AccessTokenKeyID: sql.NullString{}, |
| 9977 | RefreshToken: result.RefreshToken, |
| 9978 | RefreshTokenKeyID: sql.NullString{}, |
| 9979 | TokenType: result.TokenType, |
| 9980 | Expiry: expiry, |
| 9981 | }, |
| 9982 | ) |
| 9983 | if err != nil { |
| 9984 | // The provider may have rotated the refresh token, |
| 9985 | // invalidating the old one. Use the new token |
| 9986 | // in-memory so at least this connection succeeds. |
| 9987 | logger.Warn(ctx, "failed to persist refreshed MCP oauth2 token, using in-memory", |
| 9988 | slog.F("server_slug", cfg.Slug), |
| 9989 | slog.Error(err), |
| 9990 | ) |
| 9991 | tok.AccessToken = result.AccessToken |
| 9992 | tok.RefreshToken = result.RefreshToken |
| 9993 | tok.TokenType = result.TokenType |
| 9994 | tok.Expiry = expiry |
| 9995 | return tok, nil |
| 9996 | } |
| 9997 | |
| 9998 | return updated, nil |
| 9999 | } |
no test coverage detected