mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-23 19:04:10 +01:00
145 lines
4.1 KiB
JavaScript
145 lines
4.1 KiB
JavaScript
|
|
jest.mock('@librechat/data-schemas', () => ({
|
||
|
|
logger: { error: jest.fn(), debug: jest.fn(), warn: jest.fn() },
|
||
|
|
}));
|
||
|
|
jest.mock('~/server/services/GraphTokenService', () => ({
|
||
|
|
getGraphApiToken: jest.fn(),
|
||
|
|
}));
|
||
|
|
jest.mock('~/server/services/AuthService', () => ({
|
||
|
|
requestPasswordReset: jest.fn(),
|
||
|
|
setOpenIDAuthTokens: jest.fn(),
|
||
|
|
resetPassword: jest.fn(),
|
||
|
|
setAuthTokens: jest.fn(),
|
||
|
|
registerUser: jest.fn(),
|
||
|
|
}));
|
||
|
|
jest.mock('~/strategies', () => ({ getOpenIdConfig: jest.fn() }));
|
||
|
|
jest.mock('~/models', () => ({
|
||
|
|
deleteAllUserSessions: jest.fn(),
|
||
|
|
getUserById: jest.fn(),
|
||
|
|
findSession: jest.fn(),
|
||
|
|
updateUser: jest.fn(),
|
||
|
|
findUser: jest.fn(),
|
||
|
|
}));
|
||
|
|
jest.mock('@librechat/api', () => ({
|
||
|
|
isEnabled: jest.fn(),
|
||
|
|
findOpenIDUser: jest.fn(),
|
||
|
|
}));
|
||
|
|
|
||
|
|
const { isEnabled } = require('@librechat/api');
|
||
|
|
const { getGraphApiToken } = require('~/server/services/GraphTokenService');
|
||
|
|
const { graphTokenController } = require('./AuthController');
|
||
|
|
|
||
|
|
describe('graphTokenController', () => {
|
||
|
|
let req, res;
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
jest.clearAllMocks();
|
||
|
|
isEnabled.mockReturnValue(true);
|
||
|
|
|
||
|
|
req = {
|
||
|
|
user: {
|
||
|
|
openidId: 'oid-123',
|
||
|
|
provider: 'openid',
|
||
|
|
federatedTokens: {
|
||
|
|
access_token: 'federated-access-token',
|
||
|
|
id_token: 'federated-id-token',
|
||
|
|
},
|
||
|
|
},
|
||
|
|
headers: { authorization: 'Bearer app-jwt-which-is-id-token' },
|
||
|
|
query: { scopes: 'https://graph.microsoft.com/.default' },
|
||
|
|
};
|
||
|
|
|
||
|
|
res = {
|
||
|
|
status: jest.fn().mockReturnThis(),
|
||
|
|
json: jest.fn(),
|
||
|
|
};
|
||
|
|
|
||
|
|
getGraphApiToken.mockResolvedValue({
|
||
|
|
access_token: 'graph-access-token',
|
||
|
|
token_type: 'Bearer',
|
||
|
|
expires_in: 3600,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should pass federatedTokens.access_token as OBO assertion, not the auth header bearer token', async () => {
|
||
|
|
await graphTokenController(req, res);
|
||
|
|
|
||
|
|
expect(getGraphApiToken).toHaveBeenCalledWith(
|
||
|
|
req.user,
|
||
|
|
'federated-access-token',
|
||
|
|
'https://graph.microsoft.com/.default',
|
||
|
|
);
|
||
|
|
expect(getGraphApiToken).not.toHaveBeenCalledWith(
|
||
|
|
expect.anything(),
|
||
|
|
'app-jwt-which-is-id-token',
|
||
|
|
expect.anything(),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return the graph token response on success', async () => {
|
||
|
|
await graphTokenController(req, res);
|
||
|
|
|
||
|
|
expect(res.json).toHaveBeenCalledWith({
|
||
|
|
access_token: 'graph-access-token',
|
||
|
|
token_type: 'Bearer',
|
||
|
|
expires_in: 3600,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return 403 when user is not authenticated via Entra ID', async () => {
|
||
|
|
req.user.provider = 'google';
|
||
|
|
req.user.openidId = undefined;
|
||
|
|
|
||
|
|
await graphTokenController(req, res);
|
||
|
|
|
||
|
|
expect(res.status).toHaveBeenCalledWith(403);
|
||
|
|
expect(getGraphApiToken).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return 403 when OPENID_REUSE_TOKENS is not enabled', async () => {
|
||
|
|
isEnabled.mockReturnValue(false);
|
||
|
|
|
||
|
|
await graphTokenController(req, res);
|
||
|
|
|
||
|
|
expect(res.status).toHaveBeenCalledWith(403);
|
||
|
|
expect(getGraphApiToken).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return 400 when scopes query param is missing', async () => {
|
||
|
|
req.query.scopes = undefined;
|
||
|
|
|
||
|
|
await graphTokenController(req, res);
|
||
|
|
|
||
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
||
|
|
expect(getGraphApiToken).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return 401 when federatedTokens.access_token is missing', async () => {
|
||
|
|
req.user.federatedTokens = {};
|
||
|
|
|
||
|
|
await graphTokenController(req, res);
|
||
|
|
|
||
|
|
expect(res.status).toHaveBeenCalledWith(401);
|
||
|
|
expect(getGraphApiToken).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return 401 when federatedTokens is absent entirely', async () => {
|
||
|
|
req.user.federatedTokens = undefined;
|
||
|
|
|
||
|
|
await graphTokenController(req, res);
|
||
|
|
|
||
|
|
expect(res.status).toHaveBeenCalledWith(401);
|
||
|
|
expect(getGraphApiToken).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return 500 when getGraphApiToken throws', async () => {
|
||
|
|
getGraphApiToken.mockRejectedValue(new Error('OBO exchange failed'));
|
||
|
|
|
||
|
|
await graphTokenController(req, res);
|
||
|
|
|
||
|
|
expect(res.status).toHaveBeenCalledWith(500);
|
||
|
|
expect(res.json).toHaveBeenCalledWith({
|
||
|
|
message: 'Failed to obtain Microsoft Graph token',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|