Skip to content

Commit

Permalink
Implement feature to verify role while logging in (#419)
Browse files Browse the repository at this point in the history
* feat: implement feature to verify role while log in

* feat: redirect to home page on basis of role
  • Loading branch information
KabinKhandThakuri authored Dec 9, 2024
1 parent 09ba86b commit 313e229
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 16 deletions.
7 changes: 6 additions & 1 deletion packages/vue-user/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const useUserStore = defineStore("user", () => {
const login = async (credentials: LoginCredentials) => {
const response = await doLogin(credentials);

setUser(response);
return response;
};

const logout = async () => {
Expand All @@ -51,6 +51,10 @@ const useUserStore = defineStore("user", () => {
"sFrontToken=; Max-Age=0; path=/; domain=" + location.hostname;
});

removeUser();
};

const removeUser = () => {
localStorage.removeItem(USER_KEY);
};

Expand Down Expand Up @@ -83,6 +87,7 @@ const useUserStore = defineStore("user", () => {
getUser,
login,
logout,
removeUser,
resetPassword,
requestPasswordReset,
setUser,
Expand Down
47 changes: 47 additions & 0 deletions packages/vue-user/src/supertokens/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Session from "supertokens-web-js/recipe/session";
import { UserRoleClaim } from "supertokens-web-js/recipe/userroles";

import useUserStore from "../store";

export async function verifySessionRoles(claims: string[]): Promise<boolean> {
if (await Session.doesSessionExist()) {
let errorCount = 0;

const validationErrors = await Session.validateClaims({
overrideGlobalClaimValidators: (globalValidators) => {
const validators = claims.map((claim) =>
UserRoleClaim.validators.includes(claim),
);

return [...globalValidators, ...validators];
},
});

if (validationErrors.length === 0) {
return true;
}

for (const err of validationErrors) {
if (err.validatorId === UserRoleClaim.id) {
// user roles claim check failed
errorCount += 1;
} else {
// some other claim check failed (from the global validators list)
}
}

if (errorCount < claims.length) {
// some user roles claim check passed
return true;
} else {
const userStore = useUserStore();

const { logout } = userStore;
// all user roles claim check failed
await logout();
}
}
// either a session does not exist, or one of the validators failed.
// so we do not allow access to this page.
return false;
}
2 changes: 2 additions & 0 deletions packages/vue-user/src/supertokens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Session from "supertokens-web-js/recipe/session";
import ThirdPartyEmailPassword from "supertokens-web-js/recipe/thirdpartyemailpassword";

import googleSignIn from "./google-signin";
import { verifySessionRoles } from "./helper";
import login from "./login";
import logout from "./logout";
import requestPasswordReset from "./request-password-reset";
Expand Down Expand Up @@ -66,4 +67,5 @@ export {
resetPassword,
signup,
verifyEmail,
verifySessionRoles,
};
1 change: 1 addition & 0 deletions packages/vue-user/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface DzangolabVueUserConfig {
};
routes?: RouteOverrides;
socialLogins?: string[];
supportedRoles?: string[];
}

declare module "@dzangolab/vue3-config" {
Expand Down
40 changes: 25 additions & 15 deletions packages/vue-user/src/views/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ export default {
import { useConfig } from "@dzangolab/vue3-config";
import { useI18n } from "@dzangolab/vue3-i18n";
import { Errors, Page } from "@dzangolab/vue3-ui";
import { storeToRefs } from "pinia";
import { ref } from "vue";
import { useRouter } from "vue-router";
import GoogleLogin from "../components/GoogleLogin.vue";
import LoginForm from "../components/LoginForm.vue";
import { useTranslations } from "../index";
import useUserStore from "../store";
import { verifySessionRoles } from "../supertokens";
import type { LoginCredentials } from "../types";
import type { AppConfig } from "@dzangolab/vue3-config";
Expand All @@ -59,8 +59,7 @@ const messages = useTranslations();
const { t } = useI18n({ messages });
const userStore = useUserStore();
const { login } = userStore;
const { user } = storeToRefs(userStore);
const { login, setUser } = userStore;
const router = useRouter();
Expand All @@ -71,18 +70,29 @@ const loading = ref(false);
const handleSubmit = async (credentials: LoginCredentials) => {
loading.value = true;
await login(credentials).catch((error) => {
errors.value = [
{
code: error.message,
message: t(`user.login.errors.${error.message}`),
},
];
});
if (user.value) {
router.push({ name: "home" });
}
await login(credentials)
.then(async (response) => {
if (response) {
const supportedRoles = config?.user?.supportedRoles;
if (
(supportedRoles && (await verifySessionRoles(supportedRoles))) ||
!supportedRoles?.length
) {
setUser(response);
router.push({ name: "home" });
}
}
})
.catch((error) => {
errors.value = [
{
code: error.message,
message: t(`user.login.errors.${error.message}`),
},
];
});
loading.value = false;
};
Expand Down

0 comments on commit 313e229

Please sign in to comment.