diff --git a/packages/user/src/components/UsersTable/UsersTable.tsx b/packages/user/src/components/UsersTable/UsersTable.tsx index f05c4e14a..e932680cc 100644 --- a/packages/user/src/components/UsersTable/UsersTable.tsx +++ b/packages/user/src/components/UsersTable/UsersTable.tsx @@ -18,6 +18,7 @@ import type { InvitationRoleOption, InvitationExpiryDateField, UserType, + Role, } from "@/types"; type VisibleColumn = @@ -112,16 +113,16 @@ export const UsersTable = ({ id: "roles", header: t("table.defaultColumns.roles"), cell: ({ getValue, row: { original } }) => { - const roles = (original as unknown as { roles: string[] })?.roles; + const { roles } = original; if (Array.isArray(roles)) { return ( <> - {roles?.map((role: string, index: number) => ( + {roles?.map((role: Role) => ( ))} diff --git a/packages/user/src/components/__test__/DropdownUserMenu.snapshot.test.tsx b/packages/user/src/components/__test__/DropdownUserMenu.snapshot.test.tsx index 2cde967ef..3f5d884a5 100644 --- a/packages/user/src/components/__test__/DropdownUserMenu.snapshot.test.tsx +++ b/packages/user/src/components/__test__/DropdownUserMenu.snapshot.test.tsx @@ -19,6 +19,14 @@ function toJson(component: ReactTestRenderer) { return result as ReactTestRendererJSON; } +const roles = [ + { + id: 2, + role: "USER", + default: true, + }, +]; + test("Component matches snapshot", () => { const values = { setUser: vi.fn(), @@ -31,7 +39,7 @@ test("Component matches snapshot", () => { signedUpAt: 0, surname: "name", givenName: "test", - roles: ["USER"], + roles, }, loading: false, }; diff --git a/packages/user/src/components/__test__/DropdownUserMenu.test.tsx b/packages/user/src/components/__test__/DropdownUserMenu.test.tsx index 31e1576c5..9241b8852 100644 --- a/packages/user/src/components/__test__/DropdownUserMenu.test.tsx +++ b/packages/user/src/components/__test__/DropdownUserMenu.test.tsx @@ -8,6 +8,14 @@ import { userContext } from "../../context/UserProvider"; import DropdownUserMenu from "../DropdownUserMenu"; const setup = () => { + const roles = [ + { + id: 2, + role: "USER", + default: true, + }, + ]; + const values = { setUser: vi.fn(), user: { @@ -19,7 +27,7 @@ const setup = () => { signedUpAt: 0, surname: null, givenName: null, - roles: ["USER"], + roles, }, loading: false, }; diff --git a/packages/user/src/context/UserProvider.tsx b/packages/user/src/context/UserProvider.tsx index a8b8b492e..b0d1ef3ea 100644 --- a/packages/user/src/context/UserProvider.tsx +++ b/packages/user/src/context/UserProvider.tsx @@ -7,7 +7,7 @@ import { isUserVerified, verifySessionRoles, } from "../supertokens/helpers"; -import { UserContextType, UserType } from "../types"; +import { Role, UserContextType, UserType } from "../types"; interface Properties { children: React.ReactNode; @@ -49,7 +49,7 @@ const UserProvider = ({ children }: Properties) => { const userData = { ...user, - roles: roles, + roles, }; if (appConfig.user.features?.signUp?.emailVerification) { diff --git a/packages/user/src/index.ts b/packages/user/src/index.ts index c3e75a2ce..a3cae4b57 100644 --- a/packages/user/src/index.ts +++ b/packages/user/src/index.ts @@ -37,6 +37,8 @@ import login from "./supertokens/login"; import logout from "./supertokens/logout"; import resetPassword from "./supertokens/reset-password"; import signup from "./supertokens/signup"; +import UserPermissionClaim from "./supertokens/UserPermissionClaim"; +import UserRoleClaim from "./supertokens/UserRoleClaim"; import verifyEmail from "./supertokens/verify-email"; import { AcceptInvitation } from "./views/AcceptInvitation"; import { ChangePassword } from "./views/ChangePassword"; @@ -112,6 +114,8 @@ export { resetPassword, setUserData, signup, + UserPermissionClaim, + UserRoleClaim, superTokens, useEmailVerification, useFirstUserSignup, diff --git a/packages/user/src/supertokens/UserPermissionClaim.ts b/packages/user/src/supertokens/UserPermissionClaim.ts new file mode 100644 index 000000000..7471324f0 --- /dev/null +++ b/packages/user/src/supertokens/UserPermissionClaim.ts @@ -0,0 +1,10 @@ +import { PrimitiveArrayClaim } from "supertokens-web-js/recipe/session"; + +const UserPermissionClaim = new PrimitiveArrayClaim({ + id: "permission", + // eslint-disable-next-line @typescript-eslint/no-empty-function + refresh: async () => {}, + defaultMaxAgeInSeconds: Number.MAX_SAFE_INTEGER, +}); + +export default UserPermissionClaim; diff --git a/packages/user/src/supertokens/UserRoleClaim.ts b/packages/user/src/supertokens/UserRoleClaim.ts new file mode 100644 index 000000000..ee074cc88 --- /dev/null +++ b/packages/user/src/supertokens/UserRoleClaim.ts @@ -0,0 +1,10 @@ +import { PrimitiveArrayClaim } from "supertokens-web-js/recipe/session"; + +const UserRoleClaim = new PrimitiveArrayClaim({ + id: "role", + // eslint-disable-next-line @typescript-eslint/no-empty-function + refresh: async () => {}, + defaultMaxAgeInSeconds: Number.MAX_SAFE_INTEGER, +}); + +export default UserRoleClaim; diff --git a/packages/user/src/supertokens/helpers.ts b/packages/user/src/supertokens/helpers.ts index e3aa9e1ff..fc707cac6 100644 --- a/packages/user/src/supertokens/helpers.ts +++ b/packages/user/src/supertokens/helpers.ts @@ -1,18 +1,20 @@ import EmailVerification from "supertokens-web-js/recipe/emailverification"; import Session from "supertokens-web-js/recipe/session"; -import { UserRoleClaim } from "supertokens-web-js/recipe/userroles"; import logout from "./logout"; +import UserRoleClaim from "./UserRoleClaim"; import { removeUserData } from "../helpers"; +// eslint-disable-next-line import/no-unresolved +import { Role } from "../types"; /** * Get User roles */ -async function getUserRoles(): Promise { +async function getUserRoles(): Promise { if (await Session.doesSessionExist()) { const roles = await Session.getClaimValue({ claim: UserRoleClaim }); - return roles ? roles : []; + return (roles ? roles.map((role) => ({ role })) : []) as Role[]; } return []; @@ -59,7 +61,7 @@ async function verifySessionRoles(claims: string[]): Promise { return true; } else { // all user roles claim check failed - await removeUserData(); + removeUserData(); await logout(); } } diff --git a/packages/user/src/types/index.ts b/packages/user/src/types/index.ts index f1f4f20e2..b8d8c7227 100644 --- a/packages/user/src/types/index.ts +++ b/packages/user/src/types/index.ts @@ -16,6 +16,7 @@ import { ErrorResponse, ExtendedUser, LoginCredentials, + Role, SignInUpPromise, UpdateProfileInputType, UserContextType, @@ -39,6 +40,7 @@ export type { LoginCredentials, ResendInvitationResponse, RevokeInvitationResponse, + Role, SignInUpPromise, UpdateProfileInputType, UserContextType, diff --git a/packages/user/src/types/types.ts b/packages/user/src/types/types.ts index f884d8a7d..f0e16deea 100644 --- a/packages/user/src/types/types.ts +++ b/packages/user/src/types/types.ts @@ -1,12 +1,19 @@ import { EmailPasswordUserType } from "supertokens-web-js/recipe/thirdpartyemailpassword"; +export interface Role { + id: number; + role: string; + default: boolean; + permissions?: string; +} + export interface UserType extends EmailPasswordUserType { disabled?: boolean; givenName: string | null; isEmailVerified?: boolean; lastLoginAt: number; middleNames: string | null; - roles: string[]; + roles: Role[]; signedUpAt: number; surname: string | null; }