import { getServerDao } from '../dao/index.js';
import { ServerConfig } from '../types/index.js';
type OAuthConfig = NonNullable<ServerConfig['oauth']>;
export type ServerConfigWithOAuth = ServerConfig & { oauth: OAuthConfig };
export interface OAuthSettingsContext {
serverConfig: ServerConfig;
oauth: OAuthConfig;
}
/**
* Load the latest server configuration from DAO.
*/
export const loadServerConfig = async (serverName: string): Promise<ServerConfig | undefined> => {
const serverDao = getServerDao();
const server = await serverDao.findById(serverName);
if (!server) {
return undefined;
}
const { name: _, ...config } = server;
return config;
};
/**
* Mutate OAuth configuration for a server and persist the updated settings.
* The mutator receives the server config to allow related updates when needed.
*/
export const mutateOAuthSettings = async (
serverName: string,
mutator: (context: OAuthSettingsContext) => void,
): Promise<ServerConfigWithOAuth | undefined> => {
const serverDao = getServerDao();
const server = await serverDao.findById(serverName);
if (!server) {
console.warn(`Server ${serverName} not found while updating OAuth settings`);
return undefined;
}
const { name: _, ...serverConfig } = server;
if (!serverConfig.oauth) {
serverConfig.oauth = {};
}
const context: OAuthSettingsContext = {
serverConfig,
oauth: serverConfig.oauth,
};
mutator(context);
const updated = await serverDao.update(serverName, { oauth: serverConfig.oauth });
if (!updated) {
throw new Error(`Failed to persist OAuth settings for server ${serverName}`);
}
return context.serverConfig as ServerConfigWithOAuth;
};
export const persistClientCredentials = async (
serverName: string,
credentials: {
clientId: string;
clientSecret?: string;
scopes?: string[];
authorizationEndpoint?: string;
tokenEndpoint?: string;
},
): Promise<ServerConfigWithOAuth | undefined> => {
const updated = await mutateOAuthSettings(serverName, ({ oauth }) => {
oauth.clientId = credentials.clientId;
oauth.clientSecret = credentials.clientSecret;
if (credentials.scopes && credentials.scopes.length > 0) {
oauth.scopes = credentials.scopes;
}
if (credentials.authorizationEndpoint) {
oauth.authorizationEndpoint = credentials.authorizationEndpoint;
}
if (credentials.tokenEndpoint) {
oauth.tokenEndpoint = credentials.tokenEndpoint;
}
});
console.log(`Persisted OAuth client credentials for server: ${serverName}`);
if (credentials.scopes && credentials.scopes.length > 0) {
console.log(`Stored OAuth scopes for ${serverName}: ${credentials.scopes.join(', ')}`);
}
return updated;
};
/**
* Persist OAuth tokens and optionally replace the stored refresh token.
*/
export const persistTokens = async (
serverName: string,
tokens: {
accessToken: string;
refreshToken?: string | null;
clearPendingAuthorization?: boolean;
},
): Promise<ServerConfigWithOAuth | undefined> => {
return mutateOAuthSettings(serverName, ({ oauth }) => {
oauth.accessToken = tokens.accessToken;
if (tokens.refreshToken !== undefined) {
if (tokens.refreshToken) {
oauth.refreshToken = tokens.refreshToken;
} else {
delete oauth.refreshToken;
}
}
if (tokens.clearPendingAuthorization && oauth.pendingAuthorization) {
delete oauth.pendingAuthorization;
}
});
};
/**
* Update or create a pending authorization record.
*/
export const updatePendingAuthorization = async (
serverName: string,
pending: Partial<NonNullable<OAuthConfig['pendingAuthorization']>>,
): Promise<ServerConfigWithOAuth | undefined> => {
return mutateOAuthSettings(serverName, ({ oauth }) => {
oauth.pendingAuthorization = {
...(oauth.pendingAuthorization || {}),
...pending,
createdAt: pending.createdAt ?? Date.now(),
};
});
};
/**
* Clear cached OAuth data using shared helpers.
*/
export const clearOAuthData = async (
serverName: string,
scope: 'all' | 'client' | 'tokens' | 'verifier',
): Promise<ServerConfigWithOAuth | undefined> => {
return mutateOAuthSettings(serverName, ({ oauth }) => {
if (scope === 'tokens' || scope === 'all') {
delete oauth.accessToken;
delete oauth.refreshToken;
}
if (scope === 'client' || scope === 'all') {
delete oauth.clientId;
delete oauth.clientSecret;
}
if (scope === 'verifier' || scope === 'all') {
if (oauth.pendingAuthorization) {
delete oauth.pendingAuthorization;
}
}
});
};