mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
Update token management again
This commit is contained in:
35
src/smapi.ts
35
src/smapi.ts
@@ -248,7 +248,32 @@ class SonosSoap {
|
|||||||
getCredentialsForToken(token: string): SmapiToken | undefined {
|
getCredentialsForToken(token: string): SmapiToken | undefined {
|
||||||
logger.debug("getCredentialsForToken called with: " + token);
|
logger.debug("getCredentialsForToken called with: " + token);
|
||||||
logger.debug("Current tokens: " + JSON.stringify(this.tokenStore.getAll()));
|
logger.debug("Current tokens: " + JSON.stringify(this.tokenStore.getAll()));
|
||||||
return this.tokenStore.get(token);
|
|
||||||
|
// First try direct lookup
|
||||||
|
let smapiToken = this.tokenStore.get(token);
|
||||||
|
if (smapiToken) {
|
||||||
|
return smapiToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not found, try to find by service token (for cases where token was refreshed)
|
||||||
|
// This is a fallback mechanism to handle token refresh scenarios
|
||||||
|
const allTokens = this.tokenStore.getAll();
|
||||||
|
for (const [storedToken, storedSmapiToken] of Object.entries(allTokens)) {
|
||||||
|
try {
|
||||||
|
const verifyResult = this.smapiAuthTokens.verify(storedSmapiToken);
|
||||||
|
if (E.isRight(verifyResult)) {
|
||||||
|
// This token is valid, check if it matches our service token
|
||||||
|
const serviceToken = verifyResult.right;
|
||||||
|
// We can't easily extract the service token from the JWT without the key,
|
||||||
|
// so we'll rely on the direct lookup for now
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Token is invalid/expired, skip it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
associateCredentialsForToken(token: string, fullSmapiToken: SmapiToken, oldToken?:string) {
|
associateCredentialsForToken(token: string, fullSmapiToken: SmapiToken, oldToken?:string) {
|
||||||
logger.debug("Adding token: " + token + " " + JSON.stringify(fullSmapiToken));
|
logger.debug("Adding token: " + token + " " + JSON.stringify(fullSmapiToken));
|
||||||
@@ -540,8 +565,6 @@ function bindSmapiSoapServiceToExpress(
|
|||||||
throw SMAPI_FAULT_LOGIN_UNAUTHORIZED;
|
throw SMAPI_FAULT_LOGIN_UNAUTHORIZED;
|
||||||
});
|
});
|
||||||
} else if (isExpiredTokenError(authOrFail)) {
|
} else if (isExpiredTokenError(authOrFail)) {
|
||||||
// Don't pass old token here to avoid circular reference issues with Jest/SOAP
|
|
||||||
// Old expired tokens will be cleaned up by TTL or manual cleanup later
|
|
||||||
logger.info("Token expired, attempting refresh...");
|
logger.info("Token expired, attempting refresh...");
|
||||||
throw await pipe(
|
throw await pipe(
|
||||||
musicService.refreshToken(authOrFail.expiredToken),
|
musicService.refreshToken(authOrFail.expiredToken),
|
||||||
@@ -549,7 +572,7 @@ function bindSmapiSoapServiceToExpress(
|
|||||||
logger.info("Token refresh successful, issuing new SMAPI token");
|
logger.info("Token refresh successful, issuing new SMAPI token");
|
||||||
return smapiAuthTokens.issue(it.serviceToken);
|
return smapiAuthTokens.issue(it.serviceToken);
|
||||||
}),
|
}),
|
||||||
TE.tap(swapToken(undefined)),
|
TE.tap(swapToken(credentials?.loginToken?.token)),
|
||||||
TE.map((newToken) => ({
|
TE.map((newToken) => ({
|
||||||
Fault: {
|
Fault: {
|
||||||
faultcode: "Client.TokenRefreshRequired",
|
faultcode: "Client.TokenRefreshRequired",
|
||||||
@@ -615,12 +638,10 @@ function bindSmapiSoapServiceToExpress(
|
|||||||
throw fault.toSmapiFault();
|
throw fault.toSmapiFault();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
// Don't pass old token here to avoid circular reference issues with Jest/SOAP
|
|
||||||
// Old expired tokens will be cleaned up by TTL or manual cleanup later
|
|
||||||
return pipe(
|
return pipe(
|
||||||
musicService.refreshToken(serviceToken),
|
musicService.refreshToken(serviceToken),
|
||||||
TE.map((it) => smapiAuthTokens.issue(it.serviceToken)),
|
TE.map((it) => smapiAuthTokens.issue(it.serviceToken)),
|
||||||
TE.tap(swapToken(undefined)), // ignores the return value, like a tee or peek
|
TE.tap(swapToken(creds?.loginToken?.token)), // Pass the old token to be replaced
|
||||||
TE.map((it) => ({
|
TE.map((it) => ({
|
||||||
refreshAuthTokenResult: {
|
refreshAuthTokenResult: {
|
||||||
authToken: it.token,
|
authToken: it.token,
|
||||||
|
|||||||
@@ -41,13 +41,11 @@ export class InMemorySmapiTokenStore implements SmapiTokenStore {
|
|||||||
const smapiToken = this.tokens[tokenKey];
|
const smapiToken = this.tokens[tokenKey];
|
||||||
if (smapiToken) {
|
if (smapiToken) {
|
||||||
const verifyResult = smapiAuthTokens.verify(smapiToken);
|
const verifyResult = smapiAuthTokens.verify(smapiToken);
|
||||||
// Only delete if token verification fails with InvalidTokenError
|
|
||||||
// Do NOT delete ExpiredTokenError as those can still be refreshed
|
|
||||||
if (E.isLeft(verifyResult)) {
|
if (E.isLeft(verifyResult)) {
|
||||||
const error = verifyResult.left;
|
const error = verifyResult.left;
|
||||||
// Only delete invalid tokens, not expired ones (which can be refreshed)
|
// Delete both invalid and expired tokens to prevent accumulation
|
||||||
if (error._tag === 'InvalidTokenError') {
|
if (error._tag === 'InvalidTokenError' || error._tag === 'ExpiredTokenError') {
|
||||||
logger.debug(`Deleting invalid token from in-memory store`);
|
logger.debug(`Deleting ${error._tag} token from in-memory store`);
|
||||||
delete this.tokens[tokenKey];
|
delete this.tokens[tokenKey];
|
||||||
deletedCount++;
|
deletedCount++;
|
||||||
}
|
}
|
||||||
@@ -56,7 +54,7 @@ export class InMemorySmapiTokenStore implements SmapiTokenStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (deletedCount > 0) {
|
if (deletedCount > 0) {
|
||||||
logger.info(`Cleaned up ${deletedCount} invalid token(s) from in-memory store`);
|
logger.info(`Cleaned up ${deletedCount} token(s) from in-memory store`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return deletedCount;
|
return deletedCount;
|
||||||
@@ -142,13 +140,11 @@ export class FileSmapiTokenStore implements SmapiTokenStore {
|
|||||||
const smapiToken = this.tokens[tokenKey];
|
const smapiToken = this.tokens[tokenKey];
|
||||||
if (smapiToken) {
|
if (smapiToken) {
|
||||||
const verifyResult = smapiAuthTokens.verify(smapiToken);
|
const verifyResult = smapiAuthTokens.verify(smapiToken);
|
||||||
// Only delete if token verification fails with InvalidTokenError
|
|
||||||
// Do NOT delete ExpiredTokenError as those can still be refreshed
|
|
||||||
if (E.isLeft(verifyResult)) {
|
if (E.isLeft(verifyResult)) {
|
||||||
const error = verifyResult.left;
|
const error = verifyResult.left;
|
||||||
// Only delete invalid tokens, not expired ones (which can be refreshed)
|
// Delete both invalid and expired tokens to prevent accumulation
|
||||||
if (error._tag === 'InvalidTokenError') {
|
if (error._tag === 'InvalidTokenError' || error._tag === 'ExpiredTokenError') {
|
||||||
logger.debug(`Deleting invalid token from file store`);
|
logger.debug(`Deleting ${error._tag} token from file store`);
|
||||||
delete this.tokens[tokenKey];
|
delete this.tokens[tokenKey];
|
||||||
deletedCount++;
|
deletedCount++;
|
||||||
}
|
}
|
||||||
@@ -157,7 +153,7 @@ export class FileSmapiTokenStore implements SmapiTokenStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (deletedCount > 0) {
|
if (deletedCount > 0) {
|
||||||
logger.info(`Cleaned up ${deletedCount} invalid token(s) from file store`);
|
logger.info(`Cleaned up ${deletedCount} token(s) from file store`);
|
||||||
this.saveToFile();
|
this.saveToFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,13 +122,11 @@ export class SQLiteSmapiTokenStore implements SmapiTokenStore {
|
|||||||
const smapiToken = tokens[tokenKey];
|
const smapiToken = tokens[tokenKey];
|
||||||
if (smapiToken) {
|
if (smapiToken) {
|
||||||
const verifyResult = smapiAuthTokens.verify(smapiToken);
|
const verifyResult = smapiAuthTokens.verify(smapiToken);
|
||||||
// Only delete if token verification fails with InvalidTokenError
|
|
||||||
// Do NOT delete ExpiredTokenError as those can still be refreshed
|
|
||||||
if (E.isLeft(verifyResult)) {
|
if (E.isLeft(verifyResult)) {
|
||||||
const error = verifyResult.left;
|
const error = verifyResult.left;
|
||||||
// Only delete invalid tokens, not expired ones (which can be refreshed)
|
// Delete both invalid and expired tokens to prevent accumulation
|
||||||
if (error._tag === 'InvalidTokenError') {
|
if (error._tag === 'InvalidTokenError' || error._tag === 'ExpiredTokenError') {
|
||||||
logger.debug(`Deleting invalid token from SQLite store`);
|
logger.debug(`Deleting ${error._tag} token from SQLite store`);
|
||||||
this.delete(tokenKey);
|
this.delete(tokenKey);
|
||||||
deletedCount++;
|
deletedCount++;
|
||||||
}
|
}
|
||||||
@@ -137,7 +135,7 @@ export class SQLiteSmapiTokenStore implements SmapiTokenStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (deletedCount > 0) {
|
if (deletedCount > 0) {
|
||||||
logger.info(`Cleaned up ${deletedCount} invalid token(s) from SQLite store`);
|
logger.info(`Cleaned up ${deletedCount} token(s) from SQLite store`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return deletedCount;
|
return deletedCount;
|
||||||
|
|||||||
Reference in New Issue
Block a user