Skip to content

Commit

Permalink
Serialize maintainToken to ensure we only renew once
Browse files Browse the repository at this point in the history
  • Loading branch information
fredli74 committed Oct 1, 2024
1 parent 8edd781 commit 36383aa
Showing 1 changed file with 43 additions and 26 deletions.
69 changes: 43 additions & 26 deletions providers/tesla/tesla-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,42 +30,59 @@ export async function authorize(
}
}

// Maintain a map to track ongoing token renewals
const tokenLocks: Map<string, Promise<TeslaToken>> = new Map();

// Check token and refresh through direct database update
export async function maintainToken(
db: DBInterface,
token: Partial<Omit<TeslaToken, "refresh_token">> &
Pick<TeslaToken, "refresh_token">
): Promise<TeslaToken> {
log(LogLevel.Trace, `maintainToken ${JSON.stringify(token)}`);
try {
if (
token.access_token !== undefined &&
token.expires_at !== undefined &&
!TeslaAPI.tokenExpired(token as TeslaToken)
) {
log(LogLevel.Trace, `Token ${token.access_token} is still valid`);
return token as TeslaToken;
}
if (
token.access_token !== undefined &&
token.expires_at !== undefined &&
!TeslaAPI.tokenExpired(token as TeslaToken)
) {
log(LogLevel.Trace, `Token ${token.access_token} is still valid`);
return token as TeslaToken;
}

assert(token.refresh_token !== undefined);
if (token.access_token) {
log(LogLevel.Trace, `Token ${token.access_token} is expired, calling renewToken`);
} else {
log(LogLevel.Trace, `Client pre-emptively requrested token refresh on ${token.refresh_token}`);
}
assert(token.refresh_token !== undefined);

const newToken = await teslaAPI.renewToken(token.refresh_token);
validToken(db, token.refresh_token, newToken);
return newToken;
} catch (err:any) {
if (err && err.message === "login_required") {
log(LogLevel.Warning, `Token ${token.refresh_token} is invalid (login_required)`);
invalidToken(db, token);
} else {
log(LogLevel.Error, `Unexpected error raised when renewing token ${JSON.stringify(err)}`);
}
throw new ApolloError("Invalid token", "INVALID_TOKEN");
if (token.access_token) {
log(LogLevel.Trace, `Token ${token.access_token} is expired, calling renewToken`);
} else {
log(LogLevel.Trace, `Client pre-emptively requested token refresh on ${token.refresh_token}`);
}

let renewal = tokenLocks.get(token.refresh_token);
if (!renewal) {
renewal = (async () => {
try {
const newToken = await teslaAPI.renewToken(token.refresh_token);
validToken(db, token.refresh_token, newToken);
return newToken;
} catch(err:any) {
if (err && err.message === "login_required") {
log(LogLevel.Warning, `Token ${token.refresh_token} is invalid (login_required)`);
invalidToken(db, token);
} else {
log(LogLevel.Error, `Unexpected error raised when renewing token ${JSON.stringify(err)}`);
}
throw new ApolloError("Invalid token", "INVALID_TOKEN");
} finally {
tokenLocks.delete(token.refresh_token);
}
})();
tokenLocks.set(token.refresh_token, renewal);
} else {
log(LogLevel.Trace, `Token ${token.refresh_token} is already being refreshed, waiting for it to complete`);
}

// Wait for the renewal process to complete (all callers will get the same result or error)
return await renewal;
}

async function validToken(
Expand Down

0 comments on commit 36383aa

Please sign in to comment.