diff --git a/src/authz-module/libraries-manager/context.test.tsx b/src/authz-module/libraries-manager/context.test.tsx
new file mode 100644
index 0000000..6bfc719
--- /dev/null
+++ b/src/authz-module/libraries-manager/context.test.tsx
@@ -0,0 +1,125 @@
+import { screen } from '@testing-library/react';
+import { useParams } from 'react-router-dom';
+import { useValidateUserPermissions } from '@src/helpers/useValidateUserPermissions';
+import { renderWrapper } from '@src/setupTest';
+import { useLibrary } from './data/hooks';
+import { LibraryAuthZProvider, useLibraryAuthZ } from './context';
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: jest.fn(),
+}));
+
+jest.mock('@src/helpers/useValidateUserPermissions', () => ({
+ useValidateUserPermissions: jest.fn(),
+}));
+
+jest.mock('./data/hooks', () => ({
+ useLibrary: jest.fn(),
+}));
+
+const TestComponent = () => {
+ const context = useLibraryAuthZ();
+ return (
+
+
{context.username}
+
{context.libraryId}
+
{context.canManageTeam ? 'true' : 'false'}
+
{context.libraryName}
+
{context.libraryOrg}
+
+ );
+};
+
+describe('LibraryAuthZProvider', () => {
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ (useParams as jest.Mock).mockReturnValue({ libraryId: 'lib123' });
+
+ (useLibrary as jest.Mock).mockReturnValue({
+ data: {
+ title: 'Test Library',
+ org: 'Test Org',
+ },
+ });
+ });
+
+ it('provides the correct context values to consumers', () => {
+ (useValidateUserPermissions as jest.Mock).mockReturnValue({
+ data: [
+ { allowed: true }, // canViewTeam
+ { allowed: true }, // canManageTeam
+ ],
+ });
+
+ renderWrapper(
+
+
+
+ );
+
+ expect(screen.getByTestId('username')).toHaveTextContent('testuser');
+ expect(screen.getByTestId('libraryId')).toHaveTextContent('lib123');
+ expect(screen.getByTestId('canManageTeam')).toHaveTextContent('true');
+ expect(screen.getByTestId('libraryName')).toHaveTextContent('Test Library');
+ expect(screen.getByTestId('libraryOrg')).toHaveTextContent('Test Org');
+ });
+
+ it('throws error when user lacks both view and manage permissions', () => {
+ (useValidateUserPermissions as jest.Mock).mockReturnValue({
+ data: [
+ { allowed: false }, // canViewTeam
+ { allowed: false }, // canManageTeam
+ ],
+ });
+
+ expect(() => {
+ renderWrapper(
+
+
+
+ );
+ }).toThrow('NoAccess');
+ });
+
+ it('provides context when user can view but not manage team', () => {
+ (useValidateUserPermissions as jest.Mock).mockReturnValue({
+ data: [
+ { allowed: true }, // canViewTeam
+ { allowed: false }, // canManageTeam
+ ],
+ });
+
+ renderWrapper(
+
+
+
+ );
+
+ expect(screen.getByTestId('canManageTeam')).toHaveTextContent('false');
+ });
+
+ it('throws error when libraryId is missing', () => {
+ (useParams as jest.Mock).mockReturnValue({}); // No libraryId
+
+ expect(() => {
+ renderWrapper(
+
+
+
+ );;
+ }).toThrow('MissingLibrary');
+ });
+
+ it('throws error when useLibraryAuthZ is used outside provider', () => {
+ const BrokenComponent = () => {
+ useLibraryAuthZ();
+ return null;
+ };
+
+ expect(() => {
+ renderWrapper();
+ }).toThrow('useLibraryAuthZ must be used within an LibraryAuthZProvider');
+ });
+});
diff --git a/src/authz-module/libraries-manager/context.tsx b/src/authz-module/libraries-manager/context.tsx
new file mode 100644
index 0000000..9fada0d
--- /dev/null
+++ b/src/authz-module/libraries-manager/context.tsx
@@ -0,0 +1,76 @@
+import {
+ createContext, useContext, useMemo, ReactNode,
+} from 'react';
+import { useParams } from 'react-router-dom';
+import { AppContext } from '@edx/frontend-platform/react';
+import { useValidateUserPermissions } from '@src/helpers/useValidateUserPermissions';
+import { useLibrary } from './data/hooks';
+
+const LIBRARY_TEAM_PERMISSIONS = ['act:view_library_team', 'act:manage_library_team'];
+
+export type AppContextType = {
+ authenticatedUser: {
+ username: string;
+ email: string;
+ };
+};
+
+type LibraryAuthZContextType = {
+ canManageTeam: boolean;
+ username: string;
+ libraryId: string;
+ roles: string[];
+ permissions: string[];
+ libraryName: string;
+ libraryOrg: string;
+};
+
+const LibraryAuthZContext = createContext(undefined);
+
+type AuthZProviderProps = {
+ children: ReactNode;
+};
+
+export const LibraryAuthZProvider: React.FC = ({ children }) => {
+ const { libraryId } = useParams<{ libraryId: string }>();
+ const { authenticatedUser } = useContext(AppContext) as AppContextType;
+
+ // TODO: Implement a custom error view
+ if (!libraryId) {
+ throw new Error('MissingLibrary');
+ }
+ const permissions = LIBRARY_TEAM_PERMISSIONS.map(action => ({ action, object: libraryId }));
+
+ const { data: userPermissions } = useValidateUserPermissions(permissions);
+ const [{ allowed: canViewTeam }, { allowed: canManageTeam }] = userPermissions;
+
+ if (!canViewTeam && !canManageTeam) {
+ throw new Error('NoAccess');
+ }
+
+ const { data: libraryMetadata } = useLibrary(libraryId);
+
+ const value = useMemo((): LibraryAuthZContextType => ({
+ username: authenticatedUser.username,
+ libraryId,
+ libraryName: libraryMetadata.title,
+ libraryOrg: libraryMetadata.org,
+ roles: [],
+ permissions: [],
+ canManageTeam,
+ }), [libraryId, authenticatedUser.username, canManageTeam]);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useLibraryAuthZ = (): LibraryAuthZContextType => {
+ const context = useContext(LibraryAuthZContext);
+ if (context === undefined) {
+ throw new Error('useLibraryAuthZ must be used within an LibraryAuthZProvider');
+ }
+ return context;
+};
diff --git a/src/authz-module/libraries-manager/data/api.ts b/src/authz-module/libraries-manager/data/api.ts
new file mode 100644
index 0000000..b0fd811
--- /dev/null
+++ b/src/authz-module/libraries-manager/data/api.ts
@@ -0,0 +1,26 @@
+import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
+import { LibraryMetadata, TeamMember } from '@src/authz-module/constants';
+import { getApiUrl, getStudioApiUrl } from '@src/helpers/utils';
+
+export interface GetTeamMembersResponse {
+ members: TeamMember[];
+ totalCount: number;
+}
+
+// TODO: replece api path once is created
+export const getTeamMembers = async (libraryId: string): Promise => {
+ const { data } = await getAuthenticatedHttpClient().get(getApiUrl(`/api/authz/v1/roles/users?scope=${libraryId}`));
+ return data.results;
+};
+
+
+// TODO: this should be replaced in the future with Console API
+export const getLibrary = async (libraryId: string): Promise => {
+ const { data } = await getAuthenticatedHttpClient().get(getStudioApiUrl(`/api/libraries/v2/${libraryId}/`));
+ return {
+ id: data.id,
+ org: data.org,
+ title: data.title,
+ slug: data.slug,
+ };
+};
diff --git a/src/authz-module/libraries-manager/data/hooks.test.tsx b/src/authz-module/libraries-manager/data/hooks.test.tsx
new file mode 100644
index 0000000..a5b7734
--- /dev/null
+++ b/src/authz-module/libraries-manager/data/hooks.test.tsx
@@ -0,0 +1,115 @@
+import { ReactNode } from 'react';
+import { renderHook, waitFor } from '@testing-library/react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { useLibrary, useTeamMembers } from './hooks';
+import * as api from './api';
+
+const mockMembers = [
+ {
+ displayName: 'Alice',
+ username: 'user1',
+ email: 'alice@example.com',
+ roles: ['admin', 'author'],
+ },
+ {
+ displayName: 'Bob',
+ username: 'user2',
+ email: 'bob@example.com',
+ roles: ['collaborator'],
+ },
+];
+
+const mockLibrary = {
+ id: 'lib:123',
+ org: 'demo-org',
+ title: 'Test Library',
+ slug: 'test-library',
+};
+
+const createWrapper = () => {
+ const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: ReactNode }) => (
+ {children}
+
+ );
+
+ return wrapper;
+};
+
+describe('useTeamMembers', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('returns data when API call succeeds', async () => {
+ jest.spyOn(api, 'getTeamMembers').mockResolvedValue(mockMembers);
+
+ const { result } = renderHook(() => useTeamMembers('lib:123'), {
+ wrapper: createWrapper(),
+ });
+
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
+
+ expect(api.getTeamMembers).toHaveBeenCalledWith('lib:123');
+ expect(result.current.data).toEqual(mockMembers);
+ });
+
+ it('handles error when API call fails', async () => {
+ jest
+ .spyOn(api, 'getTeamMembers')
+ .mockRejectedValue(new Error('API failure'));
+
+ const { result } = renderHook(() => useTeamMembers('lib:123'), {
+ wrapper: createWrapper(),
+ });
+
+ await waitFor(() => expect(result.current.isError).toBe(true));
+
+ expect(api.getTeamMembers).toHaveBeenCalledWith('lib:123');
+ expect(result.current.error).toBeDefined();
+ expect(result.current.data).toBeUndefined();
+ });
+});
+
+describe('useLibrary', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('returns metadata on success', async () => {
+ jest.spyOn(api, 'getLibrary').mockResolvedValue(mockLibrary);
+
+ const { result } = renderHook(
+ () => useLibrary('lib123'),
+ { wrapper: createWrapper() },
+ );
+ await waitFor(() => {
+ expect(result.current.data).toEqual(mockLibrary);
+ expect(api.getLibrary).toHaveBeenCalledWith('lib123');
+ });
+ });
+
+ it('throws on error', () => {
+ jest
+ .spyOn(api, 'getLibrary')
+ .mockRejectedValue(new Error('Not found'));
+
+ const wrapper = createWrapper();
+ try {
+ renderHook(() => useLibrary('lib123'), { wrapper });
+ } catch (e) {
+ expect(e).toEqual(new Error('Not found'));
+ }
+
+ expect(api.getLibrary).toHaveBeenCalledWith('lib123');
+ });
+});
diff --git a/src/authz-module/libraries-manager/data/hooks.ts b/src/authz-module/libraries-manager/data/hooks.ts
new file mode 100644
index 0000000..9289b63
--- /dev/null
+++ b/src/authz-module/libraries-manager/data/hooks.ts
@@ -0,0 +1,37 @@
+import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
+import { LibraryMetadata, TeamMember } from '@src/authz-module/constants';
+import { getLibrary, getTeamMembers } from './api';
+
+/**
+ * React Query hook to fetch all team members for a specific library.
+ * It retrieves the full list of members who have access to the given library.
+ *
+ * @param libraryId - The unique identifier of the library
+ *
+ * @example
+ * ```tsx
+ * const { data: teamMembers, isLoading, isError } = useTeamMembers('lib:123');
+ * ```
+ */
+export const useTeamMembers = (libraryId: string) => useQuery({
+ queryKey: ['team-members', libraryId],
+ queryFn: () => getTeamMembers(libraryId),
+ staleTime: 1000 * 60 * 30, // refetch after 30 minutes
+});
+
+/**
+ * React Query hook to retrive the inforation of the current library.
+ *
+ * @param libraryId - The unique ID of the library.
+ *
+ * @example
+ * const { data, isLoading, isError } = useLibrary('lib:123',);
+ *
+ */
+export function useLibrary(libraryId: string) {
+ return useSuspenseQuery({
+ queryKey: ['library-metadata', libraryId],
+ queryFn: () => getLibrary(libraryId),
+ retry: false,
+ });
+}