Replace password storage with token-based authentication for server switching

Co-authored-by: retardgerman <78982850+retardgerman@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-09-05 20:03:33 +00:00
parent 85929c2854
commit a377317710
3 changed files with 60 additions and 30 deletions

View File

@@ -11,10 +11,7 @@ interface Server {
serverName?: string;
serverId?: string;
lastUsername?: string;
savedCredentials?: {
username: string;
password: string;
};
savedToken?: string;
}
interface PreviousServersListProps {
@@ -58,7 +55,7 @@ export const PreviousServersList: React.FC<PreviousServersListProps> = ({
onPress={() => onServerSelect(s)}
title={getServerDisplayName(s)}
subtitle={getServerSubtitle(s)}
icon={s.savedCredentials ? "key" : "server"}
icon={s.savedToken ? "key" : "server"}
showArrow
/>
))}

View File

@@ -13,10 +13,7 @@ interface Server {
serverName?: string;
serverId?: string;
lastUsername?: string;
savedCredentials?: {
username: string;
password: string;
};
savedToken?: string;
}
interface Props extends ViewProps {}
@@ -54,8 +51,8 @@ export const ServerSwitcher: React.FC<Props> = ({ ...props }) => {
const getServerSubtitle = (server: Server) => {
if (server.lastUsername) {
const hasCredentials = !!server.savedCredentials;
return hasCredentials
const hasToken = !!server.savedToken;
return hasToken
? `${server.lastUsername} • Auto-login available`
: `Last user: ${server.lastUsername}`;
}
@@ -81,7 +78,7 @@ export const ServerSwitcher: React.FC<Props> = ({ ...props }) => {
onPress={() => handleServerSwitch(server)}
title={getServerDisplayName(server)}
subtitle={getServerSubtitle(server)}
icon={server.savedCredentials ? "key" : "server"}
icon={server.savedToken ? "key" : "server"}
showArrow
disabled={switchingServer === server.address}
/>

View File

@@ -33,10 +33,7 @@ interface Server {
serverName?: string;
serverId?: string;
lastUsername?: string;
savedCredentials?: {
username: string;
password: string;
};
savedToken?: string;
}
export const apiAtom = atom<Api | null>(null);
@@ -256,7 +253,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
setApi(jellyfin.createApi(api?.basePath, auth.data?.AccessToken));
storage.set("token", auth.data?.AccessToken);
// Save credentials to the current server if requested
// Save token to the current server if requested
if (saveCredentials && api.basePath) {
const previousServers = JSON.parse(
storage.getString("previousServers") || "[]",
@@ -267,7 +264,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
return {
...server,
lastUsername: username,
savedCredentials: { username, password }
savedToken: auth.data.AccessToken
};
}
return server;
@@ -348,20 +345,42 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
// Get current server info for comparison
const currentServerId = await getCurrentServerId();
// If switching to same server (different URL), try auto-login with saved credentials
if (server.serverId && server.serverId === currentServerId && server.savedCredentials) {
// If switching to same server (different URL), try auto-login with saved token
if (server.serverId && server.serverId === currentServerId && server.savedToken) {
try {
// Just set the new server without logging out
await setServerMutation.mutateAsync(server);
// Auto-login with saved credentials
await loginMutation.mutateAsync({
username: server.savedCredentials.username,
password: server.savedCredentials.password,
saveCredentials: false, // Don't save again
});
return;
// Create API instance with saved token
const apiInstance = jellyfin?.createApi(server.address, server.savedToken);
if (!apiInstance) throw new Error("Failed to create API instance");
// Validate the token by making an authenticated request
const userApi = getUserApi(apiInstance);
const userResponse = await userApi.getCurrentUser();
if (userResponse.data) {
// Token is valid, update the API and user
setApi(apiInstance);
setUser(userResponse.data);
storage.set("serverUrl", server.address);
storage.set("token", server.savedToken);
storage.set("user", JSON.stringify(userResponse.data));
return;
}
} catch (error) {
console.warn("Auto-login failed, falling back to manual login:", error);
console.warn("Saved token is invalid, falling back to manual login:", error);
// Remove invalid token from server
const previousServers = JSON.parse(
storage.getString("previousServers") || "[]",
) as Server[];
const updatedServers = previousServers.map((s) => {
if (s.address === server.address) {
const { savedToken, ...serverWithoutToken } = s;
return serverWithoutToken;
}
return s;
});
storage.set("previousServers", JSON.stringify(updatedServers));
}
}
@@ -441,6 +460,23 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
if (!jellyfin) return;
try {
// Migrate any existing savedCredentials to remove them
const previousServers = JSON.parse(
storage.getString("previousServers") || "[]",
) as any[];
if (previousServers.length > 0) {
const migratedServers = previousServers.map((server) => {
if (server.savedCredentials) {
// Remove savedCredentials field for security
const { savedCredentials, ...serverWithoutCredentials } = server;
return serverWithoutCredentials;
}
return server;
});
storage.set("previousServers", JSON.stringify(migratedServers));
}
const token = getTokenFromStorage();
const serverUrl = getServerUrlFromStorage();
const storedUser = getUserFromStorage();