DEV Community

Cover image for Protecting Student Privacy: Building a Secure Grades Viewer with React and Permit
Fadahunsi Seyi
Fadahunsi Seyi

Posted on • Originally published at Medium

Protecting Student Privacy: Building a Secure Grades Viewer with React and Permit

Should student grade be accessible to all?? Preventing unnecessary access to information without requiring any identity or revealing data should happen in an application. In this article, we will see how it can be done safely by developing an application that lets students ask to see their grades.

On the frontend, we are going to use React and on the backend we will use Node.js, and Permit for access permission control. Following these instructions, you will make an application a very smooth, safe, and user-friendly experience.

Prerequisite

  • Basic understanding of JavaScript and React : A prior knowledge about the basic javascript concepts along with familiarity in using react framework is a must on the frontend part for validation.
  • Basic Node.js and Express knowledge: Learn the basics of Node. The backend server needs to setup and handle with js, for instance Express.
  • Permit Account: Register for a Permit.

Setting Up Permit

Using Permit Elements, you can "share your app" with end users and know that they will have access control using a set of wide covering pre-built embeddable UI components which are fully functional.

Major Features of Permit Elements are;

  • Advanced Security: Follows industry-standard protocols for secure, modern user identification.
  • Automatically adjusts to different screen sizes, maintaining the same user experience regardless of platform. This is what you would call responsive design.
  • Use custom stylistic: Using these you can make your website look and feel consistent.
  • Basic Embedder: Pass and play for your Setup.

Implementing Access Request Element

A frontend component called Access Request Element enables users to request access to restricted or specific permission-required resources. If a request is made, it goes to the workspace owner who can choose either approve or deny.

It is an embedded, dedicated UI in your application for end-users to request dynamic updates on what they should be allowed access no matter if they are not being given it at the moment.

When a user sends in a request it goes to an "admin" role from the definition that you made on your User Management Element tagged under: This can be beneficial because it allows you to open access, by default restricted components.

Including the Access Request Element in your frontend app is really simple, users can click on this button "Request access" to request their permissions. These requests can then be managed and responded to within the "User Management" Element by admins.

Annotation 2024-07-19 031443

The image below, will explain what would be seen when the Access Request element has been clicked;

Annotation 2024-07-19 032036

What is the Connected User Management Element? This is a list of dropdowns of the created User Management element. The user Management element will be integrated with the Access-Request element. The Access Request Element will be triggered when a student wants to request access, the User Management will allow the student to be approved or denied.

Implementing User Management Element

With the help of the embeddable User Management Element, you can take command of your entire team and assign different team members varying degrees of access based on how they will be managing other team members.

Annotation 2024-07-19 032921

The image above shows the User management element. Below is an image, which shows the properties in the User Management element;

Annotation 2024-07-19 092631

The User Management element will integrate with the Access-Request element. This will be shown in the image below;

Annotation 2024-07-19 092711

Connecting the Elements into your application

To connect these elements, you'd need to implement the iFrame for each element which will be shown below;

Annotation 2024-07-20 114432

Copy the iFrame code generated above, and connect it to your frontend application. The generated iFrame code will look like the below;

<iframe
    title="Permit Element Name"
    src="https://embed.permit.io/<ELEMENT_NAME>?envId=<SOME_UNIQUE_ID>&darkMode=false&resourceInstanceKey=<RESOURCE_INSTANCE_KEY>&tenantKey=<TENANT_KEY>"
    width="100%"
    height="100%"
/>
Enter fullscreen mode Exit fullscreen mode

In the coming sections, I will explain how to connect it with your front-end application.

Implementing the Backend Logic

In this one, we will be discussing at what type of works Permit is targeted to use. io for user management tasks including access control. A server offers an endpoint to grant access (and uses Permit) -ibm-dx-connect: io can authenticate all users with tenant ID and Api-key.

Server: It sets the middleware used for CORS (Cross Origin Resource Sharing), cookies and parsing JSON to be able to have a safe communication process. The main framework is contained in a route that takes the access request, authenticates it if valid and routes to associated controller area. Example of this setup is implemented as below;

npm install express @permitio/permit-js cookie-parser cors dotenv

const express = require("express");
const { Permit } = require("permitio");
const cookieParser = require("cookie-parser");
const cors = require("cors");
require('dotenv').config();

const TENANT = process.env.TENANT_ID
const apikey = process.env.PERMIT_API_KEY

const permit = new Permit({
  pdp: "https://cloudpdp.api.permit.io",
  token: apikey,
});

const app = express();
const port = 5000;

app.use(cors({ origin: "http://localhost:3000", credentials: true }));
app.use(cookieParser());
app.use(express.json());

app.get("/request_access", async (req, res) => {
  try {
    const {email}  = req.body;
      const result = await permit.elements.loginAs({
        userId: email,
        tenantId: TENANT,
      });
      if(result.token !== null){
       return res.status(302).redirect(result.redirect_url);
      }
     return res.status(403).send({ message: result.error });
  } catch (error) {
  return  res.status(500).json({ error: "Failed to request access" });
  }
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

This code snippet above controls user access permissions that connects with Permit.io. The dotenv uses environment variables, CORS for Cross-Origin Resource Sharing support, cookieParser with what helps work with cookies in Js and Permit. The following steps will set the tenantID and apikey on the server so that these can be accessed using environment variables with apikey and Permit. When creating an instance of Permit from a PDP URL, Then we start the application and let it listen to port 5000.

This line defines a route that handles GET requests to /request_access. Upon a request to this endpoint, the server parses out the email from that request body and logs into requesting access using loginAs method with another Payload instance referring again to Permit. Server logs in the redirect URL and redirects client to it with status 302 (Source)

If no token is sent back, it will reply a 403 status with an error message. All errors you face will return a 500 status response with error message.

Implementing the Frontend Logic

In this section, we will connect our Permit with our React application, including the iFrame component.

npx create-react-app secure-grades-viewer

The command above will initialize the React application.

npm install @permitio/permit-js

The command above, will install the Permit SDK into your react application.

// Login.js
import React, { useEffect } from 'react';
import permit from '@permitio/permit-js';
import { LoginMethod } from '@permitio/permit-js/dist/types';

const backendURL = 'http://localhost:5000';

const loginUser = async () => {
  try {
    await permit.elements.login({
      loginUrl: `${backendURL}/request_access`,
      loginMethod: LoginMethod.cookie,
      tenant: "default"
    });
  } catch (error) {
    console.log(error, 'here is the error');
  }
};

const logoutUser = async () => {
  await permit.elements.logout();
};

const Login = () => {
  useEffect(() => {
    loginUser();
  }, []);

  return null;
};

export default Login;
Enter fullscreen mode Exit fullscreen mode

The code above manages logging in the user interface by means of Permit.io. The code snippet above ensures that as soon as this component is installed, the login attempt will be made. The loginUser function in this component will trigger the permit.elements.login method, the backend URL and the login method(cookie), tenant name. Wrapping loginUser inside of useEffect with an empty dependency array ensures that the login only happens once when this component is first rendered.

// CheckGrade.js
import React from 'react';
import { useNavigate } from 'react-router-dom';

const CheckGrade = () => {
  const navigate = useNavigate();

  const handleCheckGrade = () => {
    const permitSession = document.cookie.split('; ').find(row => row.startsWith('permit_session='));

    if (permitSession) {
      navigate('/viewgrade');
    } else {
      navigate('/users-embed');
    }
  };

  return (
    <div>
      <button onClick={handleCheckGrade} style={{ margin: '10px' }}>
        Check your grade
      </button>
    </div>
  );
};

export default CheckGrade;
Enter fullscreen mode Exit fullscreen mode

The CheckGrade component - checking for the presence of a cookie (permit_session) and redirecting using history. It uses the useNavigate hook from react-router-dom to navigate through code. This function gets called whenever the user presses "Check your grade" and when that happens we are going to send a request in handleCheckGrade. It checks for the existence of a permit_session cookie from within document cookies in this function.

If it exists, the user is using to be sent through /viewgrade; if not they are redirected to the login form at /users-embed. It can do a good job of centralizing the checking session status logic inside our application.

// UserEmbed.js

import React from 'react'
import { useNavigate } from 'react-router-dom'

export default function UsersEmbed() {
    const nav = useNavigate()
  return (
    <div style={{height: '75vh'}}>
        <button onClick={() => nav('/')} style={{margin: '10px'}}>Back</button>
<iframe
title="Permit Element request-student-access"
    src="https://embed.permit.io/<ELEMENT_NAME>?envId=<SOME_UNIQUE_ID>&darkMode=false&resourceInstanceKey=<RESOURCE_INSTANCE_KEY>&tenantKey=<TENANT_KEY>"
width="100%"
height="100%"/>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Embedded Permit using UsersEmbed Component ui interface for controlling student authorization requests in app. This is a button that does the navigation and an iFrame loading the Permit.io element. This makes it possible for you to work with external access management functionalities and keep navigation standards in your application.

// App.js
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import UsersEmbed from './UsersEmbed';
import CheckGrade from './CheckGrade';
import Login from './Login';

function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Login />
        <Routes>
          <Route path="/" element={<CheckGrade />} />
          <Route path="/users-embed" element={<UsersEmbed />} />
          <Route path="/viewgrade" element={<ViewGrade />} />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The App component is where our app starts it specifies some of the routing uses a Login that lets you log in. Routing is handled by react-router-dom, which we use BrowserRouter to implement it The available routes in the application are defined inside the router via Routes component.

These routes are the root path ( / ), which makes use of CheckGrade component, the users-embed /users-embed, that renders UsersEmbed Component and lastly a viewgrade /viewgrade,that currently shows a placeholder for user grade details. This way the App component stays abstracted enough to handle routes and use login logic within it.

To start the backend server, first run the following to make sure you are in line with all steps below:

npm run dev

To start your frontend application, you will run the command below;

npm start

So once a student wants to request for to view their result, they will see the image below;

Annotation 2024-07-20 192832

So once the student has requested for access, the teacher who must be an admin, will see the following;

Annotation 2024-07-20 192754

So once the teacher who is an admin, accepts the student's request, the student will be redirected to the /viewgrade where the result shows.

Conclusion

In this article, we learned how user authentication and also handling controls through the use of Permit.io. Breaking it down into different steps (login, session validation and embedded access request) has taught us how to have in a modular approach that makes modifying code easy.

Top comments (0)