Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/admin/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@
"resetPasswordTokenMissing": "This reset link is missing its token. Request a new one.",
"resetPasswordTokenDead": "This reset link is invalid or has expired. Request a new one.",
"requestNewResetLink": "Request a new reset link",
"newPassword": "New password"
"newPassword": "New password",
"signInWithSso": "Sign in with SSO"
Comment thread
alex-budanov marked this conversation as resolved.
},
"dashboard": {
"title": "Dashboard",
Expand Down
3 changes: 2 additions & 1 deletion apps/admin/src/i18n/locales/kk.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@
"resetPasswordTokenMissing": "Сілтемеде токен жоқ. Жаңасын сұраңыз.",
"resetPasswordTokenDead": "Бұл сілтеме жарамсыз немесе мерзімі өтіп кетті. Жаңасын сұраңыз.",
"requestNewResetLink": "Жаңа сілтеме сұрау",
"newPassword": "Жаңа құпиясөз"
"newPassword": "Жаңа құпиясөз",
"signInWithSso": "SSO арқылы кіру"
},
"dashboard": {
"title": "Басқару тақтасы",
Expand Down
3 changes: 2 additions & 1 deletion apps/admin/src/i18n/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@
"resetPasswordTokenMissing": "В ссылке для сброса отсутствует токен. Запросите новую.",
"resetPasswordTokenDead": "Ссылка для сброса недействительна или истекла. Запросите новую.",
"requestNewResetLink": "Запросить новую ссылку",
"newPassword": "Новый пароль"
"newPassword": "Новый пароль",
"signInWithSso": "Войти через SSO"
},
"dashboard": {
"title": "Панель управления",
Expand Down
1 change: 1 addition & 0 deletions apps/admin/src/lib/api-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const API_ENDPOINTS = {
magicLogin: () => `${API_VERSION}/auth/magic-login`,
register: () => `${API_VERSION}/auth/register`,
registrationStatus: () => `${API_VERSION}/auth/registration-status`,
ssoStatus: () => `${API_VERSION}/auth/sso-status`,
me: () => `${API_VERSION}/auth/me`,
verifyEmail: () => `${API_VERSION}/auth/verify-email`,
resendVerification: () => `${API_VERSION}/auth/resend-verification`,
Expand Down
11 changes: 11 additions & 0 deletions apps/admin/src/services/auth-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ export const authService = {
return response.data.data;
},

/**
* Resolve whether the current tenant (by host/subdomain) has made SSO
* mandatory. Unauthenticated, same pre-auth shape as getRegistrationStatus().
*/
getSsoStatus: async (): Promise<{ enforceSso: boolean }> => {
const response = await api.get<{ success: boolean; data: { enforceSso: boolean } }>(
API_ENDPOINTS.auth.ssoStatus()
);
Comment thread
alex-budanov marked this conversation as resolved.
return response.data.data;
},

logout: async (): Promise<void> => {
await api.post(API_ENDPOINTS.auth.logout());
},
Expand Down
29 changes: 29 additions & 0 deletions apps/admin/src/tests/services/auth-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,35 @@ describe('Auth Service', () => {
});
});

describe('getSsoStatus', () => {
it('should return enforceSso: true when SSO is mandatory for the tenant', async () => {
vi.mocked(api.get).mockResolvedValueOnce({
data: { success: true, data: { enforceSso: true } },
} as AxiosResponse);

const result = await authService.getSsoStatus();

expect(result).toEqual({ enforceSso: true });
expect(api.get).toHaveBeenCalledWith('/api/v1/auth/sso-status');
});

it('should return enforceSso: false when SSO is not mandatory for the tenant', async () => {
vi.mocked(api.get).mockResolvedValueOnce({
data: { success: true, data: { enforceSso: false } },
} as AxiosResponse);

const result = await authService.getSsoStatus();

expect(result).toEqual({ enforceSso: false });
});

it('should propagate API errors', async () => {
vi.mocked(api.get).mockRejectedValueOnce(new Error('Network error'));

await expect(authService.getSsoStatus()).rejects.toThrow('Network error');
});
});

describe('login', () => {
it('should login with email and password', async () => {
const mockResponse = {
Expand Down
Loading