fix: refresh token rotation is broken in OAuth provider implementation (#9847)

* fix: Incorrect clientId used in getAccessToken response

* fix: getRefreshToken as well
This commit is contained in:
Tom Moor
2025-08-05 18:34:52 -04:00
committed by GitHub
parent 9bb802c35c
commit 84f45b7fb7
3 changed files with 89 additions and 2 deletions
+79
View File
@@ -1,6 +1,13 @@
import { v4 } from "uuid";
import { Scope } from "@shared/types";
import { OAuthAuthentication, OAuthClient, User } from "@server/models";
import { hash } from "@server/utils/crypto";
import { OAuthInterface } from "./OAuthInterface";
import {
buildOAuthAuthentication,
buildOAuthClient,
buildUser,
} from "@server/test/factories";
describe("OAuthInterface", () => {
const user = {
@@ -12,6 +19,78 @@ describe("OAuthInterface", () => {
redirectUris: ["https://example.com/callback"],
};
describe("#getAccessToken", () => {
it("should return correct token details", async () => {
const user = await buildUser();
const scope = [Scope.Read, Scope.Write];
const oAuthClient = await buildOAuthClient({ teamId: user.teamId });
const oAuthAuthentication = await buildOAuthAuthentication({
user,
oauthClientId: oAuthClient.id,
scope,
});
const result = await OAuthInterface.getAccessToken(
oAuthAuthentication.accessToken!
);
expect(result).toEqual({
accessToken: oAuthAuthentication.accessToken,
accessTokenExpiresAt: oAuthAuthentication.accessTokenExpiresAt,
scope,
client: {
id: oAuthClient.clientId,
grants: OAuthInterface.grants,
},
user: expect.objectContaining({
id: user.id,
email: user.email,
}),
});
});
it("should return false for invalid access token", async () => {
const result = await OAuthInterface.getAccessToken("invalid_token");
expect(result).toBe(false);
});
});
describe("#getRefreshToken", () => {
it("should return correct token details", async () => {
const user = await buildUser();
const scope = [Scope.Read, Scope.Write];
const oAuthClient = await buildOAuthClient({ teamId: user.teamId });
const oAuthAuthentication = await buildOAuthAuthentication({
user,
oauthClientId: oAuthClient.id,
scope,
});
const result = await OAuthInterface.getRefreshToken(
oAuthAuthentication.refreshToken!
);
expect(result).toEqual({
refreshToken: oAuthAuthentication.refreshToken,
refreshTokenExpiresAt: oAuthAuthentication.refreshTokenExpiresAt,
scope,
client: {
id: oAuthClient.clientId,
grants: OAuthInterface.grants,
},
user: expect.objectContaining({
id: user.id,
email: user.email,
}),
});
});
it("should return false for invalid refresh token", async () => {
const result = await OAuthInterface.getRefreshToken("invalid_token");
expect(result).toBe(false);
});
});
describe("#validateRedirectUri", () => {
it("should return true for valid redirect URI", async () => {
const redirectUri = "https://example.com/callback";
+2 -2
View File
@@ -70,7 +70,7 @@ export const OAuthInterface: RefreshTokenModel &
accessTokenExpiresAt: authentication.accessTokenExpiresAt,
scope: authentication.scope,
client: {
id: authentication.oauthClientId,
id: authentication.oauthClient.clientId,
grants: this.grants,
},
user: authentication.user,
@@ -89,7 +89,7 @@ export const OAuthInterface: RefreshTokenModel &
refreshTokenExpiresAt: authentication.refreshTokenExpiresAt,
scope: authentication.scope,
client: {
id: authentication.oauthClientId,
id: authentication.oauthClient.clientId,
grants: this.grants,
},
user: authentication.user,