Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Fix: Pre-emptively trigger login after google token expired #212

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

that-one-arab
Copy link
Contributor

Fixes issue

Comment on lines +84 to +93
} catch (error) {
if (error instanceof BaseError && error.result === "No access token") {
res.promise({ valid: false, error: "Invalid Google Token" });
} else {
res.promise({
valid: null, // We don't know if the session is valid or not, so we return null
error,
});
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would've liked to handle this error more consistently with how other google related errors are handled but I couldn't establish a pattern in functions like isInvalidGoogleToken

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why couldn't that pattern be established?


import compassAuthService from "./compass.auth.service";

const logger = Logger("app:google.auth.service");

export const getGcalClient = async (userId: string) => {
export const getGAuthClientForUser = async (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted as its own function for reusability.
Gave the option of providing a mongodb user object or just the userId Providing the user object directly will save a db call for better performance

@that-one-arab that-one-arab marked this pull request as ready for review January 3, 2025 05:16
@tyler-dane
Copy link
Contributor

tyler-dane commented Jan 3, 2025

No big concerns with this at first glance. I want to QA this one more thoroughly to make sure, though.

I'm going to hold off on doing that while I wrap up #203 and #196.

In the meantime, please find an issue from the 'Ready' column of the board to work on. Alternatively, you could find an issue in the Backlog to refine. By "refine", I mean thinking it through more, adding details, and updating the issue with a high-level description of your suggested implementation. After doing that I'll read through it and give it the 👍 if it looks good to start working on.

Since you're more familiar with the codebase and have been producing good work, this could be a good time to pick up a more difficult or larger issue if you're up for it.

@that-one-arab
Copy link
Contributor Author

@tyler-dane Thank you! I think I'll opt to work on issues based on the priorities you assigned them since we have a lot of them as ready. Once we start running out of ready issues I'll work on refining existing backlog items

that-one-arab and others added 3 commits January 8, 2025 07:25
Implement a new api route to validate a user's google oauth session
also renames controller method from verifyGAuthSession to verifyGToken to more accurately represent what's happening. Compass doesn't maintain a persistent session with Google like it does with it's user's session (via Supertokens). Instead, it just passes the access token in requests
@tyler-dane tyler-dane force-pushed the bug_trigger_login_after_gauth_expire branch from 4876592 to 5c77f5e Compare January 8, 2025 14:14
making it a class doesn't add much value, as we're not taking advantage of any class benefits (inheritance, class methods, encapsulation)
const dueToGAuth = !!isSessionValid && !isGAuthSessionValid;

if (dueToGAuth) {
navigate(`${ROOT_ROUTES.LOGIN}?reason=gauth-session-expired`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a constant for the reason instead of just using the string here. This'll make it easier to test, access across other files, and rename it when things change

Comment on lines +84 to +93
} catch (error) {
if (error instanceof BaseError && error.result === "No access token") {
res.promise({ valid: false, error: "Invalid Google Token" });
} else {
res.promise({
valid: null, // We don't know if the session is valid or not, so we return null
error,
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why couldn't that pattern be established?


if (!res.ok) return false;

const body = (await res.json()) as { valid: boolean };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the shared response type when casting here

clearSession();
});
}
}, [searchParams]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not need to await clearSession?

Status.BAD_REQUEST,
false
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than throwing a new BaseError, create an error in error.constants for this scenario.

Then throw it using the error() function like so:

throw error(AuthError.YourNewAuthError, "Access token not retrieved");

The description property in the error provides more info about the error.
The second arg in the error() is meant to explain the result of the error not happening, which the client can use to determine next steps.

Try not to include too many implementation details in the result string, like "Probably needs a new refresh token to obtain a new access token." We don't want to send unnecessary info to the client. Instead, you can include that info in a debug log.

This'll make it easier to test, typecheck, and prevent bugs


if (reason === "gauth-session-expired") {
toast.error("Google session expired, please login again");
clearSession();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Will this toast even render for long, if we're immediately going to clear their session and cause a redirect? Do we need to wait for it to render before running clearSession? Is that better UX than just redirecting them immediately?

  2. If you want to keep the toast, don't mention "Google session expired." Users don't care what a session is or that it's the Google connection that's requiring us to make them reauthenticate. Instead, say something like "It's been a while, so we're redirecting you back to the login page. This helps us keep your account safe"

error,
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. change valid to isValid for consistency with other booleans in the app.

  2. Don't return null. Keep isValid as a boolean that always either returns true or false

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pre-emptively trigger login after google token expired
2 participants