diff --git a/frontend/README.md b/frontend/README.md
index 64e343e1846df7c27fe49505a338146e8aeb189f..176c1cdc37b1efdfd84180e916730ecc69e6348c 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,6 +1,30 @@
 This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
 
-## Available Scripts
+## Setup
+
+First, run `yarn install` to install the node modules used for the frontend. 
+
+In order for the API links to work, the following repository branches need to be cloned and run separately:
+
+### 1. materials@scientia-integration
+```shell
+# Clone the repository and checkout to the relevant branch
+git clone https://gitlab.doc.ic.ac.uk/edtech/materials.git
+git checkout scientia-integration
+
+# Setup and run the development server on port 5000
+python3 -m venv venv
+source venv/bin/activate
+pip install --upgrade pip && pip install -r requirements.txt 
+export FLASK_ENV=development
+export FLASK_APP=materials
+flask create_all
+flask populate -r materials/mocks/resources.json
+flask run
+```
+
+
+## To run
 
 In the project directory, you can run:
 
diff --git a/frontend/src/components/pages/ModuleList/index.tsx b/frontend/src/components/pages/ModuleList/index.tsx
index 0a4fde6f4cb0393fb11c739a64577fe5f81b4bd8..93ea4322e4756202e9122a22b98635ed553351e7 100644
--- a/frontend/src/components/pages/ModuleList/index.tsx
+++ b/frontend/src/components/pages/ModuleList/index.tsx
@@ -29,7 +29,7 @@ const ModuleList: React.FC = () => {
     },
     {
       title: "Discrete Mathematics",
-      code: "CO142",
+      code: "142",
       image: discreteIllustration,
       terms: [Term.AUTUMN],
       progressStatus: ProgressStatus.IN_PROGRESS,
diff --git a/frontend/src/components/pages/ModuleResources/index.tsx b/frontend/src/components/pages/ModuleResources/index.tsx
index a1a93203c895bf861fa752d4d35b5895ff3110c4..0c075b4b04d42f846b68b72765bde755ce7177fb 100644
--- a/frontend/src/components/pages/ModuleResources/index.tsx
+++ b/frontend/src/components/pages/ModuleResources/index.tsx
@@ -1,5 +1,9 @@
-import React from "react";
+import React, { useEffect, useState } from "react";
 import styles from "./style.module.scss";
+
+import classNames from "classnames";
+import { request } from "../../../utils/api"
+import { api } from "../../../constants/routes"
 import MyBreadcrumbs from "components/atoms/MyBreadcrumbs";
 
 import InputGroup from "react-bootstrap/InputGroup";
diff --git a/frontend/src/components/pages/ModuleResources/style.module.scss b/frontend/src/components/pages/ModuleResources/style.module.scss
index 1ece388a13e426c13b6f64524dd498766d796667..c0e45a84077da719e17ebd16b93bb433ae0e964b 100644
--- a/frontend/src/components/pages/ModuleResources/style.module.scss
+++ b/frontend/src/components/pages/ModuleResources/style.module.scss
@@ -51,4 +51,4 @@
   font-weight: 500 !important;
   color: $white !important;
   border-color: $black;
-}
\ No newline at end of file
+}
diff --git a/frontend/src/components/pages/StandardView/index.tsx b/frontend/src/components/pages/StandardView/index.tsx
index 4f40d2ca508fc437dce7f6727f213ed04dfb58e6..64a3e72bfb6851271fb0fc8d50561b8c8a7d236b 100644
--- a/frontend/src/components/pages/StandardView/index.tsx
+++ b/frontend/src/components/pages/StandardView/index.tsx
@@ -70,9 +70,7 @@ const StandardView: React.FC<StandardViewProps> = ({
             <ModuleResources />
           </Route>
 
-          <Route path="/modules/:id/feedback">
-            <ModuleFeedback />
-          </Route>
+          <Route path="/modules/:id/feedback" component={ModuleFeedback} />
 
           <Route path="/timeline">
             <ExamplePage name="Timeline" />
diff --git a/frontend/src/constants/auth.tsx b/frontend/src/constants/auth.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e0ef8c55ea1e05b05fbf3e708831fab3f7ed4f33
--- /dev/null
+++ b/frontend/src/constants/auth.tsx
@@ -0,0 +1,8 @@
+const authConstants = {
+  ACCESS_TOKEN_HEADER: () => `Bearer ${sessionStorage.getItem("currentUser")}`,
+  ACCESS_TOKEN: "currentUser",
+  USER_INFO: "userInfo"
+};
+  
+export default authConstants;
+  
\ No newline at end of file
diff --git a/frontend/src/constants/routes.tsx b/frontend/src/constants/routes.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..dd37f6e245d5e0516af3a876b3a631900a99b536
--- /dev/null
+++ b/frontend/src/constants/routes.tsx
@@ -0,0 +1,15 @@
+const dev = {
+  MATERIALS_URL: "http://localhost:5000"
+}
+
+const prod = {
+  MATERIALS_URL: "https://materials.doc.ic.ac.uk",
+}
+
+const config = process.env.NODE_ENV === "development" ? dev : prod;
+
+export const api = {
+  MATERIALS_LOGIN: config.MATERIALS_URL + "/auth/login",
+  MATERIALS_COURSES: config.MATERIALS_URL + "/courses/1819",
+  MATERIALS_RESOURCES: config.MATERIALS_URL + "/resources"
+}
\ No newline at end of file
diff --git a/frontend/src/utils/api.tsx b/frontend/src/utils/api.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b21d81177ec0c48f5daa7c51ac1f72f2f25990f3
--- /dev/null
+++ b/frontend/src/utils/api.tsx
@@ -0,0 +1,41 @@
+import authConstants from "../constants/auth";
+import authenticationService from "../utils/auth";
+import { api } from "../constants/routes"
+
+interface RequestOptions {
+    [key: string]: any
+}
+
+// API calling interface. onSuccess and onError are functions that take in data
+// and error parameters respectively. Body is process as query parameters if
+// method is GET
+// Note: will trigger CORS OPTIONS preflight due to the Authorization header
+export async function request(url: string, method: string, onSuccess: any, onError: any, body?: any) {
+  if (!authenticationService.userIsLoggedIn()) {
+    // TODO: Credentials should be handled elsewhere
+    // TODO: Specific endpoint login route should be passed in
+    await authenticationService.login("abc123", "a", api.MATERIALS_LOGIN);
+  }
+  
+  var options: RequestOptions = {
+    method: method,
+    mode: "cors",
+    headers: { 
+      "Authorization": authConstants.ACCESS_TOKEN_HEADER(),
+      "Access-Control-Allow-Origin": "*",
+    },
+  };
+
+  if (method === "GET") {
+    url = url + "?" + new URLSearchParams(body);
+  } else {
+    options.body = JSON.stringify(body);
+  }
+
+  return fetch(url, options)
+    .then((result) => {
+      onSuccess(result);
+    }, (error) => {
+      onError(error);
+    })
+}
diff --git a/frontend/src/utils/auth.tsx b/frontend/src/utils/auth.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b0e19a20f1444aeff6cdf42faef8f747ad6a78ca
--- /dev/null
+++ b/frontend/src/utils/auth.tsx
@@ -0,0 +1,52 @@
+import authConstants from "../constants/auth";
+
+function storeDataInStorage(data: { access_token: string; user_info: any; }) {
+  sessionStorage.setItem(authConstants.ACCESS_TOKEN, data.access_token);
+  sessionStorage.setItem(
+    authConstants.USER_INFO,
+    JSON.stringify(data.user_info)
+  );
+}
+
+function removeDataFromStorage() {
+  sessionStorage.removeItem(authConstants.ACCESS_TOKEN);
+  sessionStorage.removeItem(authConstants.USER_INFO);
+}
+
+function getUserInfo() {
+  const info = sessionStorage.getItem(authConstants.USER_INFO)
+  if (info) {
+    return JSON.parse(info)
+  }
+  return {}
+}
+
+async function login(username: string, password: string, login_url: string) {
+  const response = await fetch(login_url, {
+    method: "POST",
+    mode: "cors",
+    headers: {
+      "Content-Type": "application/json",
+      "Access-Control-Allow-Origin": "*",
+    },
+    body: JSON.stringify({ username, password })
+  });
+  if (response.ok) {
+    const data = await response.json();
+    storeDataInStorage(data);
+    return true;
+  }
+  return false;
+}
+
+const logout = () => removeDataFromStorage();
+const userIsLoggedIn = () => {
+  return sessionStorage.getItem(authConstants.ACCESS_TOKEN) !== null;
+};
+
+export default {
+  login,
+  logout,
+  userIsLoggedIn,
+  getUserInfo
+};