Skip to content
New issue

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

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

Already on GitHub? # to your account

fix(connection): getConnectionCredentials should return credentials even on error #2870

Merged
merged 4 commits into from
Oct 22, 2024

Conversation

bodinsamuel
Copy link
Collaborator

Describe your changes

Fixes https://linear.app/nango/issue/NAN-1933/fix-ui-crash-when-loading-a-connection-with-token-refresh-error

  • Split getConnectionCredentials and initial connection fetch
    For the /connections/:id endpoint we still want to return the connection even if the refresh failed but if there was an error we stripped the credentials to avoid leaking stuff. Initially, I had just reverted this and stripped the credentials outside of the function at multiple places but the code was even more crappier.
    So I split the function, this allows us to not repeat call to fetch the integration on the API and correctly output the errors. It's a bit less straightforward but more granular

@bodinsamuel bodinsamuel self-assigned this Oct 22, 2024
Copy link

linear bot commented Oct 22, 2024

@@ -56,21 +56,22 @@ export async function exec(): Promise<void> {
const limit = 1000;
// eslint-disable-next-line no-constant-condition
while (true) {
const staleConnections = await connectionService.getStaleConnections({ days: 0, limit, cursor });
const staleConnections = await connectionService.getStaleConnections({ days: 1, limit, cursor });
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think it was an error, let me know @TBonnin

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes, that's a test leftover. Thank you for fixing


if (credentialResponse.isErr()) {
const connectionRes = await connectionService.getConnection(upsertedConnection.connection_id, upsertedConnection.provider_config_key, environment.id);
if (connectionRes.error || !connectionRes.response) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

On post connection I don't think we need to test again the credentials since we just fetched them, but maybe I forgot an edge case? @khaliqgant

Copy link
Member

Choose a reason for hiding this comment

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

No, should be fine to just use getConnection here

@@ -36,7 +36,7 @@ export function refreshConnectionsCron(): void {
}

export async function exec(): Promise<void> {
return await tracer.trace<Promise<void>>('nango.server.cron.refreshConnections', async (span) => {
await tracer.trace<Promise<void>>('nango.server.cron.refreshConnections', async (span) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

exec is itself awaited above.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes, it's just eslint best practice rules when you return void https://typescript-eslint.io/rules/no-confusing-void-expression/

if (!provider) {
const error = new NangoError('unknown_provider_config');
return Err(error);
}

const copy = { ...connection };
Copy link
Collaborator

Choose a reason for hiding this comment

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

why a copy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it's modifying the credentials by reference since we don't fetch anymore inside, it's best practice to copy (even though it's a shallow copy)

}
}
const errorLog = await errorNotificationService.auth.get(connectionRes.response.id as number);
res.status(200).send({ errorLog, provider: integration.provider, connection: connectionRes.response });
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we return something else than 200 in case of error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The thing is in the UI it's still used to display the page, before that we returned an error by did not respected that and still displayed the page. My take on this: it should be a totally different endpoint.

Copy link
Collaborator

Choose a reason for hiding this comment

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

ok. can you add a comment?

@bodinsamuel bodinsamuel merged commit d6d0c12 into master Oct 22, 2024
21 checks passed
@bodinsamuel bodinsamuel deleted the sam/fix/get-connection-error branch October 22, 2024 13:47
bodinsamuel added a commit that referenced this pull request Feb 6, 2025
…on (#3472)

## Changes 

Noticed something weird while improving the type for Connection in #3470

- Automatic refresh token was not passing decrypted connection


When doing this refactor #2870 I
split the refresh and the getConnection because it was inefficient. I
forgot to decrypt the connection, and due to bad typing, there was no
difference between Connection and (decrypted)Connection. The really
permissive code completely allowed this to pass, and still mark
connection has refreshed while not being refreshed.

NB: this was not impacting the regular refresh on fetch
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants