diff --git a/backend/WebUI/api_webui.go b/backend/WebUI/api_webui.go
index ee8c8581d6d3bcba19cf641aaa9b3f38b3a24992..83a8847600df5e72ad68eb1d94fa6c4ecefe689b 100644
--- a/backend/WebUI/api_webui.go
+++ b/backend/WebUI/api_webui.go
@@ -5,10 +5,16 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
+	"os"
+	"reflect"
+	"time"
 	"strings"
 
+	"github.com/dgrijalva/jwt-go"
 	"github.com/gin-gonic/gin"
+	"github.com/google/uuid"
 	"go.mongodb.org/mongo-driver/bson"
+	"golang.org/x/crypto/bcrypt"
 
 	"github.com/free5gc/MongoDBLibrary"
 	"github.com/free5gc/openapi/models"
@@ -24,6 +30,8 @@ const (
 	amPolicyDataColl = "policyData.ues.amData"
 	smPolicyDataColl = "policyData.ues.smData"
 	flowRuleDataColl = "policyData.ues.flowRule"
+	userDataColl     = "userData"
+	tenantDataColl   = "tenantData"
 )
 
 var httpsClient *http.Client
@@ -79,6 +87,47 @@ func sendResponseToClient(c *gin.Context, response *http.Response) {
 	c.JSON(response.StatusCode, jsonData)
 }
 
+func sendResponseToClientFilterTenant(c *gin.Context, response *http.Response, tenantId string) {
+	// Subscription data.
+	filterTenantIdOnly := bson.M{"tenantId": tenantId}
+	amDataList := MongoDBLibrary.RestfulAPIGetMany(amDataColl, filterTenantIdOnly)
+
+	tenantCheck := func(supi string) bool {
+		for _, amData := range amDataList {
+			if supi == amData["ueId"] && tenantId == amData["tenantId"] {
+				return true
+			}
+		}
+		return false
+	}
+
+	// Response data.
+	var jsonData interface{}
+	json.NewDecoder(response.Body).Decode(&jsonData)
+
+	s := reflect.ValueOf(jsonData)
+	if s.Kind() != reflect.Slice {
+		c.JSON(response.StatusCode, jsonData)
+		return
+	}
+
+	var sliceData []interface{}
+	for i := 0; i < s.Len(); i++ {
+		mapData := s.Index(i).Interface()
+		m := reflect.ValueOf(mapData)
+		for _, key := range m.MapKeys() {
+			if key.String() == "Supi" {
+				strct := m.MapIndex(key)
+				if tenantCheck(strct.Interface().(string)) {
+					sliceData = append(sliceData, mapData)
+				}
+			}
+		}
+	}
+
+	c.JSON(response.StatusCode, sliceData)
+}
+
 func GetSampleJSON(c *gin.Context) {
 	setCorsHeader(c)
 
@@ -270,24 +319,449 @@ func GetSampleJSON(c *gin.Context) {
 	c.JSON(http.StatusOK, subsData)
 }
 
+type OAuth struct {
+	AccessToken  string `json:"access_token"`
+	RefreshToken string `json:"refresh_token"`
+	TokenType    string `json:"token_type"`
+	ExpiresIn    int    `json:"expires_in"`
+}
+
+type LoginRequest struct {
+	Username string `json:"username"`
+	Password string `json:"password"`
+}
+
+func JWT(email, userId, tenantId string) string {
+	token := jwt.New(jwt.SigningMethodHS256)
+
+	claims := token.Claims.(jwt.MapClaims)
+	claims["sub"] = userId
+	claims["iat"] = time.Now()
+	claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
+	claims["email"] = email
+	claims["tenantId"] = tenantId
+
+	tokenString, _ := token.SignedString([]byte(os.Getenv("SIGNINGKEY")))
+
+	return tokenString
+}
+
+func generateHash(password string) {
+	hash, _ := bcrypt.GenerateFromPassword([]byte(password), 12)
+	logger.WebUILog.Warnln("Password hash:", hash)
+}
+
+func Login(c *gin.Context) {
+	setCorsHeader(c)
+
+	login := LoginRequest{}
+	err := json.NewDecoder(c.Request.Body).Decode(&login)
+	if err != nil {
+		logger.WebUILog.Warnln("JSON decode error", err)
+		c.JSON(http.StatusInternalServerError, gin.H{})
+		return
+	}
+
+	generateHash(login.Password)
+
+	filterEmail := bson.M{"email": login.Username}
+	userData := MongoDBLibrary.RestfulAPIGetOne(userDataColl, filterEmail)
+
+	if len(userData) == 0 {
+		logger.WebUILog.Warnln("Can't find user email", login.Username)
+		c.JSON(http.StatusForbidden, gin.H{})
+		return
+	}
+
+	hash := userData["encryptedPassword"].(string)
+
+	err = bcrypt.CompareHashAndPassword([]byte(hash), []byte(login.Password))
+	if err != nil {
+		logger.WebUILog.Warnln("Password incorrect", login.Username)
+		c.JSON(http.StatusForbidden, gin.H{})
+		return
+	}
+
+	userId := userData["userId"].(string)
+	tenantId := userData["tenantId"].(string)
+
+	logger.WebUILog.Warnln("Login success", login.Username)
+	logger.WebUILog.Warnln("userid", userId)
+	logger.WebUILog.Warnln("tenantid", tenantId)
+
+	token := JWT(login.Username, userId, tenantId)
+	logger.WebUILog.Warnln("token", token)
+
+	oauth := OAuth{}
+	oauth.AccessToken = token
+	c.JSON(http.StatusOK, oauth)
+}
+
+// Placeholder to handle logout.
+func Logout(c *gin.Context) {
+	setCorsHeader(c)
+	// Needs to invalidate access_token.
+	c.JSON(http.StatusOK, gin.H{})
+}
+
+type AuthSub struct {
+	models.AuthenticationSubscription
+	TenantId string `json:"tenantId" bson:"tenantId"`
+}
+
+// Parse JWT
+func ParseJWT(tokenStr string) jwt.MapClaims {
+	token, _ := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
+		return []byte(os.Getenv("SIGNINGKEY")), nil
+	})
+
+	claims, _ := token.Claims.(jwt.MapClaims)
+
+	return claims
+}
+
+// Check of admin user. This should be done with proper JWT token.
+func CheckAuth(c *gin.Context) bool {
+	tokenStr := c.GetHeader("Token")
+	if tokenStr == "admin" {
+		return true
+	} else {
+		return false
+	}
+}
+
+// Tenat ID
+func GetTenantId(c *gin.Context) string {
+	tokenStr := c.GetHeader("Token")
+	if tokenStr == "admin" {
+		return ""
+	}
+	claims := ParseJWT(tokenStr)
+	return claims["tenantId"].(string)
+}
+
+// Tenant
+func GetTenants(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	tenantDataInterface := MongoDBLibrary.RestfulAPIGetMany(tenantDataColl, bson.M{})
+	var tenantData []Tenant
+	json.Unmarshal(sliceToByte(tenantDataInterface), &tenantData)
+
+	c.JSON(http.StatusOK, tenantData)
+}
+
+func GetTenantByID(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	tenantId := c.Param("tenantId")
+
+	filterTenantIdOnly := bson.M{"tenantId": tenantId}
+	tenantDataInterface := MongoDBLibrary.RestfulAPIGetOne(tenantDataColl, filterTenantIdOnly)
+	if len(tenantDataInterface) == 0 {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	var tenantData Tenant
+	json.Unmarshal(mapToByte(tenantDataInterface), &tenantData)
+
+	c.JSON(http.StatusOK, tenantData)
+}
+
+func PostTenant(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	var tenantData Tenant
+	if err := c.ShouldBindJSON(&tenantData); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{})
+		return
+	}
+
+	if tenantData.TenantId == "" {
+		tenantData.TenantId = uuid.Must(uuid.NewRandom()).String()
+	}
+
+	tenantBsonM := toBsonM(tenantData)
+	filterTenantIdOnly := bson.M{"tenantId": tenantData.TenantId}
+	MongoDBLibrary.RestfulAPIPost(tenantDataColl, filterTenantIdOnly, tenantBsonM)
+
+	c.JSON(http.StatusOK, tenantData)
+}
+
+func PutTenantByID(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	tenantId := c.Param("tenantId")
+
+	filterTenantIdOnly := bson.M{"tenantId": tenantId}
+	tenantDataInterface := MongoDBLibrary.RestfulAPIGetOne(tenantDataColl, filterTenantIdOnly)
+	if len(tenantDataInterface) == 0 {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	var tenantData Tenant
+	if err := c.ShouldBindJSON(&tenantData); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{})
+		return
+	}
+	tenantData.TenantId = tenantId
+
+	tenantBsonM := toBsonM(tenantData)
+	filterTenantIdOnly = bson.M{"tenantId": tenantId}
+	MongoDBLibrary.RestfulAPIPost(tenantDataColl, filterTenantIdOnly, tenantBsonM)
+
+	c.JSON(http.StatusOK, gin.H{})
+}
+
+func DeleteTenantByID(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	tenantId := c.Param("tenantId")
+	filterTenantIdOnly := bson.M{"tenantId": tenantId}
+
+	MongoDBLibrary.RestfulAPIDeleteMany(amDataColl, filterTenantIdOnly)
+	MongoDBLibrary.RestfulAPIDeleteMany(userDataColl, filterTenantIdOnly)
+	MongoDBLibrary.RestfulAPIDeleteOne(tenantDataColl, filterTenantIdOnly)
+
+	c.JSON(http.StatusOK, gin.H{})
+}
+
+// Utility function.
+func GetTenantById(tenantId string) map[string]interface{} {
+	filterTenantIdOnly := bson.M{"tenantId": tenantId}
+	return MongoDBLibrary.RestfulAPIGetOne(tenantDataColl, filterTenantIdOnly)
+}
+
+// Users
+func GetUsers(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	tenantId := c.Param("tenantId")
+	if len(GetTenantById(tenantId)) == 0 {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	filterTenantIdOnly := bson.M{"tenantId": tenantId}
+	userDataInterface := MongoDBLibrary.RestfulAPIGetMany(userDataColl, filterTenantIdOnly)
+
+	var userData []User
+	json.Unmarshal(sliceToByte(userDataInterface), &userData)
+	for pos, _ := range userData {
+		userData[pos].EncryptedPassword = ""
+	}
+
+	c.JSON(http.StatusOK, userData)
+}
+
+func GetUserByID(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	tenantId := c.Param("tenantId")
+	if len(GetTenantById(tenantId)) == 0 {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+	userId := c.Param("userId")
+
+	filterUserIdOnly := bson.M{"tenantId": tenantId, "userId": userId}
+	userDataInterface := MongoDBLibrary.RestfulAPIGetOne(userDataColl, filterUserIdOnly)
+	if len(userDataInterface) == 0 {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	var userData User
+	json.Unmarshal(mapToByte(userDataInterface), &userData)
+	userData.EncryptedPassword = ""
+
+	c.JSON(http.StatusOK, userData)
+}
+
+func PostUserByID(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	tenantId := c.Param("tenantId")
+	if len(GetTenantById(tenantId)) == 0 {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	var userData User
+	if err := c.ShouldBindJSON(&userData); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{})
+		return
+	}
+
+	filterEmail := bson.M{"email": userData.Email}
+	userWithEmailData := MongoDBLibrary.RestfulAPIGetOne(userDataColl, filterEmail)
+	if len(userWithEmailData) != 0 {
+		logger.WebUILog.Warnln("Email already exists", userData.Email)
+		c.JSON(http.StatusForbidden, gin.H{})
+		return
+	}
+
+	userData.TenantId = tenantId
+	userData.UserId = uuid.Must(uuid.NewRandom()).String()
+	hash, _ := bcrypt.GenerateFromPassword([]byte(userData.EncryptedPassword), 12)
+	userData.EncryptedPassword = string(hash)
+
+	userBsonM := toBsonM(userData)
+	filterUserIdOnly := bson.M{"tenantId": userData.TenantId, "userId": userData.UserId}
+	MongoDBLibrary.RestfulAPIPost(userDataColl, filterUserIdOnly, userBsonM)
+
+	c.JSON(http.StatusOK, userData)
+}
+
+func PutUserByID(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	tenantId := c.Param("tenantId")
+	if len(GetTenantById(tenantId)) == 0 {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+	userId := c.Param("userId")
+
+	var newUserData User
+	if err := c.ShouldBindJSON(&newUserData); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{})
+		return
+	}
+
+	filterUserIdOnly := bson.M{"tenantId": tenantId, "userId": userId}
+	userDataInterface := MongoDBLibrary.RestfulAPIGetOne(userDataColl, filterUserIdOnly)
+	if len(userDataInterface) == 0 {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	var userData User
+	json.Unmarshal(mapToByte(userDataInterface), &userData)
+
+	if newUserData.Email != "" && newUserData.Email != userData.Email {
+		filterEmail := bson.M{"email": newUserData.Email}
+		sameEmailInterface := MongoDBLibrary.RestfulAPIGetOne(userDataColl, filterEmail)
+		if len(sameEmailInterface) != 0 {
+			c.JSON(http.StatusBadRequest, bson.M{})
+			return
+		}
+		userData.Email = newUserData.Email
+	}
+
+	if newUserData.EncryptedPassword != "" {
+		hash, _ := bcrypt.GenerateFromPassword([]byte(newUserData.EncryptedPassword), 12)
+		userData.EncryptedPassword = string(hash)
+	}
+
+	userBsonM := toBsonM(userData)
+	MongoDBLibrary.RestfulAPIPost(userDataColl, filterUserIdOnly, userBsonM)
+
+	c.JSON(http.StatusOK, userData)
+}
+
+func DeleteUserByID(c *gin.Context) {
+	setCorsHeader(c)
+
+	if !CheckAuth(c) {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+
+	tenantId := c.Param("tenantId")
+	if len(GetTenantById(tenantId)) == 0 {
+		c.JSON(http.StatusNotFound, bson.M{})
+		return
+	}
+	userId := c.Param("userId")
+
+	filterUserIdOnly := bson.M{"tenantId": tenantId, "userId": userId}
+	MongoDBLibrary.RestfulAPIDeleteOne(userDataColl, filterUserIdOnly)
+
+	c.JSON(http.StatusOK, gin.H{})
+}
+
 // Get all subscribers list
 func GetSubscribers(c *gin.Context) {
 	setCorsHeader(c)
 
 	logger.WebUILog.Infoln("Get All Subscribers List")
 
+	tokenStr := c.GetHeader("Token")
+
+	var claims jwt.MapClaims = nil
+	if tokenStr != "admin" {
+		claims = ParseJWT(tokenStr)
+	}
+
 	var subsList []SubsListIE = make([]SubsListIE, 0)
 	amDataList := MongoDBLibrary.RestfulAPIGetMany(amDataColl, bson.M{})
 	for _, amData := range amDataList {
 		ueId := amData["ueId"]
 		servingPlmnId := amData["servingPlmnId"]
-		tmp := SubsListIE{
-			PlmnID: servingPlmnId.(string),
-			UeId:   ueId.(string),
+		tenantId := amData["tenantId"]
+
+		filterUeIdOnly := bson.M{"ueId": ueId}
+		authSubsDataInterface := MongoDBLibrary.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly)
+
+		var authSubsData AuthSub
+		json.Unmarshal(mapToByte(authSubsDataInterface), &authSubsData)
+
+		if tokenStr == "admin" || tenantId == claims["tenantId"].(string) {
+			tmp := SubsListIE{
+				PlmnID: servingPlmnId.(string),
+				UeId:   ueId.(string),
+			}
+			subsList = append(subsList, tmp)
 		}
-		subsList = append(subsList, tmp)
 	}
-
 	c.JSON(http.StatusOK, subsList)
 }
 
@@ -358,6 +832,12 @@ func PostSubscriberByID(c *gin.Context) {
 	setCorsHeader(c)
 	logger.WebUILog.Infoln("Post One Subscriber Data")
 
+	var claims jwt.MapClaims = nil
+	tokenStr := c.GetHeader("Token")
+	if tokenStr != "admin" {
+		claims = ParseJWT(tokenStr)
+	}
+
 	var subsData SubsData
 	if err := c.ShouldBindJSON(&subsData); err != nil {
 		logger.WebUILog.Panic(err.Error())
@@ -369,11 +849,28 @@ func PostSubscriberByID(c *gin.Context) {
 	filterUeIdOnly := bson.M{"ueId": ueId}
 	filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId}
 
+	// Lookup same UE ID of other tenant's subscription.
+	if claims != nil {
+		authSubsDataInterface := MongoDBLibrary.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly)
+		if len(authSubsDataInterface) > 0 {
+			if authSubsDataInterface["tenantId"].(string) != claims["tenantId"].(string) {
+				c.JSON(http.StatusUnprocessableEntity, gin.H{})
+				return
+			}
+		}
+	}
+
 	authSubsBsonM := toBsonM(subsData.AuthenticationSubscription)
 	authSubsBsonM["ueId"] = ueId
+	if claims != nil {
+		authSubsBsonM["tenantId"] = claims["tenantId"].(string)
+	}
 	amDataBsonM := toBsonM(subsData.AccessAndMobilitySubscriptionData)
 	amDataBsonM["ueId"] = ueId
 	amDataBsonM["servingPlmnId"] = servingPlmnId
+	if claims != nil {
+		amDataBsonM["tenantId"] = claims["tenantId"].(string)
+	}
 
 	smDatasBsonA := make([]interface{}, 0, len(subsData.SessionManagementSubscriptionData))
 	for _, smSubsData := range subsData.SessionManagementSubscriptionData {
@@ -597,7 +1094,14 @@ func GetRegisteredUEContext(c *gin.Context) {
 			c.JSON(http.StatusInternalServerError, gin.H{})
 			return
 		}
-		sendResponseToClient(c, resp)
+
+		// Filter by tenant.
+		tenantId := GetTenantId(c)
+		if tenantId == "" {
+			sendResponseToClient(c, resp)
+		} else {
+			sendResponseToClientFilterTenant(c, resp, tenantId)
+		}
 	} else {
 		c.JSON(http.StatusInternalServerError, gin.H{
 			"cause": "No AMF Found",
diff --git a/backend/WebUI/model_tenant_data.go b/backend/WebUI/model_tenant_data.go
new file mode 100644
index 0000000000000000000000000000000000000000..28d44b3ddc993b74b286de9c0dc3779dc5f4df5f
--- /dev/null
+++ b/backend/WebUI/model_tenant_data.go
@@ -0,0 +1,6 @@
+package WebUI
+
+type Tenant struct {
+	TenantId   string `json:"tenantId"`
+	TenantName string `json:"tenantName"`
+}
diff --git a/backend/WebUI/model_user_data.go b/backend/WebUI/model_user_data.go
new file mode 100644
index 0000000000000000000000000000000000000000..34560eafe8c45d7d99cb23ae75670de7c1343c64
--- /dev/null
+++ b/backend/WebUI/model_user_data.go
@@ -0,0 +1,8 @@
+package WebUI
+
+type User struct {
+	UserId            string `json:"userId"`
+	TenantId          string `json:"tenantId"`
+	Email             string `json:"email"`
+	EncryptedPassword string `json:"encryptedPassword"`
+}
diff --git a/backend/WebUI/routers.go b/backend/WebUI/routers.go
index e3fc57874774dcef3332427e7f997bf9529bcc74..1bed2554781287d9f1392d1e36f5161c5c367316 100644
--- a/backend/WebUI/routers.go
+++ b/backend/WebUI/routers.go
@@ -60,6 +60,90 @@ var routes = Routes{
 		GetSampleJSON,
 	},
 
+	{
+		"Login",
+		http.MethodPost,
+		"/login",
+		Login,
+	},
+
+	{
+		"Logout",
+		http.MethodPost,
+		"/logout",
+		Logout,
+	},
+
+	{
+		"GetTenants",
+		http.MethodGet,
+		"/tenant",
+		GetTenants,
+	},
+
+	{
+		"GetTenantByID",
+		http.MethodGet,
+		"/tenant/:tenantId",
+		GetTenantByID,
+	},
+
+	{
+		"PostTenant",
+		http.MethodPost,
+		"/tenant",
+		PostTenant,
+	},
+
+	{
+		"PutTenantByID",
+		http.MethodPut,
+		"/tenant/:tenantId",
+		PutTenantByID,
+	},
+
+	{
+		"DeleteTenantByID",
+		http.MethodDelete,
+		"/tenant/:tenantId",
+		DeleteTenantByID,
+	},
+
+	{
+		"GetUsers",
+		http.MethodGet,
+		"/tenant/:tenantId/user",
+		GetUsers,
+	},
+
+	{
+		"GetUserByID",
+		http.MethodGet,
+		"/tenant/:tenantId/user/:userId",
+		GetUserByID,
+	},
+
+	{
+		"PostUserByID",
+		http.MethodPost,
+		"/tenant/:tenantId/user",
+		PostUserByID,
+	},
+
+	{
+		"PutUserByID",
+		http.MethodPut,
+		"/tenant/:tenantId/user/:userId",
+		PutUserByID,
+	},
+
+	{
+		"DeleteUserByID",
+		http.MethodDelete,
+		"/tenant/:tenantId/user/:userId",
+		DeleteUserByID,
+	},
+
 	{
 		"GetSubscribers",
 		http.MethodGet,
diff --git a/frontend/src/components/SideBar/Nav.js b/frontend/src/components/SideBar/Nav.js
index 61d53ec5451da320890391980c6bc8c08e704532..1effbbe562eaa5eb0573323e95be8f7fa8ae29dc 100644
--- a/frontend/src/components/SideBar/Nav.js
+++ b/frontend/src/components/SideBar/Nav.js
@@ -1,11 +1,25 @@
 import React, {Component} from 'react';
 import {Link, withRouter} from 'react-router-dom';
+import LocalStorageHelper from "../../util/LocalStorageHelper";
 
 class Nav extends Component {
   state = {};
 
   render() {
     let {location} = this.props;
+    let user = LocalStorageHelper.getUserInfo();
+    let childView = "";
+    if (user.accessToken === "admin") {
+      childView = (
+          <li className={this.isPathActive('/tenants') ? 'active' : null}>
+          <Link to="/tenants">
+          <i className="pe-7s-users"/>
+          <p>Tenant and User</p>
+          </Link>
+          </li>
+      );
+    }
+
     /* Icons:
      *  - https://fontawesome.com/icons
      *  - http://themes-pixeden.com/font-demos/7-stroke/
@@ -33,6 +47,8 @@ class Nav extends Component {
           </Link>
         </li>
 
+      {childView}
+
       </ul>
     );
   }
diff --git a/frontend/src/models/Tenant.js b/frontend/src/models/Tenant.js
new file mode 100644
index 0000000000000000000000000000000000000000..c860579226fb1697d003c3f972eb7ecbce7025ab
--- /dev/null
+++ b/frontend/src/models/Tenant.js
@@ -0,0 +1,12 @@
+import Serializable from "./Serializable";
+
+export default class Tenant extends Serializable{
+  id = '';
+  name = "";
+
+  constructor(id, name) {
+    super();
+    this.id = id;
+    this.name = name;
+  }
+}
diff --git a/frontend/src/models/User.js b/frontend/src/models/User.js
index b780188e405a1482e52a8e8170d9b415cfb403ef..a582a8eaac91abf63869e029bc8998ce436b3c62 100644
--- a/frontend/src/models/User.js
+++ b/frontend/src/models/User.js
@@ -4,11 +4,17 @@ export default class User extends Serializable{
   username = "";
   name = "";
   imageUrl = "";
+  accessToken = "";
+  id = '';
+  email = "";
 
-  constructor(username, name) {
+  constructor(username, name, accessToken, id, email) {
     super();
     this.username = username;
     this.name = name;
     this.imageUrl = 'https://cdn1.iconfinder.com/data/icons/evil-icons-user-interface/64/avatar-256.png';
+    this.accessToken = accessToken;
+    this.id = id;
+    this.email = email;
   }
 }
diff --git a/frontend/src/pages/Main/index.js b/frontend/src/pages/Main/index.js
index af485509d5b17dc5a8cba2a5cefa91ab58d74fe3..c0478fdb2de1e68fc5fe685257a74760193ff66b 100644
--- a/frontend/src/pages/Main/index.js
+++ b/frontend/src/pages/Main/index.js
@@ -13,7 +13,9 @@ import SideBar from '../../components/SideBar';
 import Subscribers from '../Subscribers';
 import Tasks from '../Tasks';
 import UEInfo from '../Dashboard';
-import UEInfoDetail from '../UEInfoDetail'
+import UEInfoDetail from '../UEInfoDetail';
+import Tenants from '../Tenants';
+import Users from '../Users';
 
 const Main = ({
                 mobileNavVisibility,
@@ -47,6 +49,8 @@ const Main = ({
           <Route exact path="/tasks" component={Tasks}/>
           <Route exact path="/ueinfo" component={UEInfo}/>
           <Route exact path="/ueinfo/:id" component={UEInfoDetail}/>
+          <Route exact path="/tenants" component={Tenants}/>
+          <Route exact path="/users/:id" component={Users}/>
 
           <Footer/>
         </div>
diff --git a/frontend/src/pages/Tenants/TenantOverview.js b/frontend/src/pages/Tenants/TenantOverview.js
new file mode 100644
index 0000000000000000000000000000000000000000..29a7b640d42d26746ca3a52b92dbc6dc1e631000
--- /dev/null
+++ b/frontend/src/pages/Tenants/TenantOverview.js
@@ -0,0 +1,132 @@
+import React, { Component } from 'react';
+import { Link, withRouter } from "react-router-dom";
+import { connect } from "react-redux";
+import { Button, Table } from "react-bootstrap";
+import TenantModal from "./components/TenantModal";
+import ApiHelper from "../../util/ApiHelper";
+
+class TenantOverview extends Component {
+  state = {
+    tenantModalOpen: false,
+    tenantModalData: null,
+  };
+
+  componentDidMount() {
+    ApiHelper.fetchTenants().then();
+  }
+
+  openAddTenant() {
+    this.setState({
+      tenantModalOpen: true,
+      tenantModalData: null,
+    });
+  }
+
+  /**
+   * @param tenantId  {string}
+   */
+  async openEditTenant(tenantId) {
+    const tenant = await ApiHelper.fetchTenantById(tenantId);
+
+    this.setState({
+      tenantModalOpen: true,
+      tenantModalData: tenant,
+    });
+  }
+
+  async addTenant(tenantData) {
+    this.setState({ tenantModalOpen: false });
+
+    if (!await ApiHelper.createTenant(tenantData)) {
+      alert("Error creating new tenant");
+    }
+    ApiHelper.fetchTenants().then();
+  }
+
+  /**
+   * @param tenantData
+   */
+  async updateTenant(tenantData) {
+    this.setState({ tenantModalOpen: false });
+
+    const result = await ApiHelper.updateTenant(tenantData);
+
+    if (!result) {
+      alert("Error updating tenant: " + tenantData["ueId"]);
+    }
+    ApiHelper.fetchTenants().then();
+  }
+
+  /**
+  * @param tenant  {Tenant}
+   */
+  async deleteTenant(tenant) {
+    if (!window.confirm(`Delete tenant ${tenant.id}?`))
+      return;
+
+    const result = await ApiHelper.deleteTenant(tenant.id);
+    ApiHelper.fetchTenants().then();
+    if (!result) {
+      alert("Error deleting tenant: " + tenant.id);
+    }
+  }
+
+  render() {
+    return (
+      <div className="container-fluid">
+        <div className="row">
+          <div className="col-md-12">
+            <div className="card">
+              <div className="header subscribers__header">
+                <h4>Tenants</h4>
+                <Button bsStyle={"primary"} className="subscribers__button"
+                  onClick={this.openAddTenant.bind(this)}>
+                  New Tenant
+                </Button>
+              </div>
+              <div className="content subscribers__content">
+                <Table className="subscribers__table" striped bordered condensed hover>
+                  <thead>
+                    <tr>
+                      <th style={{ width: 400 }}>Tenant ID</th>
+                      <th colSpan={2}>Tenant Name</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    {this.props.tenants.map(tenant => (
+                      <tr key={tenant.id}>
+                        <td>{tenant.id}</td>
+                        <td><font color="blue"><u><Link to={"/users/"+tenant.id}>{tenant.name}</Link></u></font></td>
+                        <td style={{ textAlign: 'center' }}>
+                          <Button variant="danger" onClick={this.deleteTenant.bind(this, tenant)}>Delete</Button>
+                         &nbsp;&nbsp;&nbsp;&nbsp;
+                        <Button variant="info" onClick={this.openEditTenant.bind(this, tenant.id)}>Modify</Button>
+                        </td>
+                      </tr>
+                    ))}
+                  </tbody>
+                </Table>
+
+                <p>&nbsp;</p><p>&nbsp;</p>
+                <p>&nbsp;</p><p>&nbsp;</p>
+                <p>&nbsp;</p><p>&nbsp;</p>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <TenantModal open={this.state.tenantModalOpen}
+          setOpen={val => this.setState({ tenantModalOpen: val })}
+          tenant={this.state.tenantModalData}
+          onModify={this.updateTenant.bind(this)}
+          onSubmit={this.addTenant.bind(this)} />
+      </div>
+    );
+  }
+}
+
+const mapStateToProps = state => ({
+  tenants: state.tenant.tenants,
+});
+
+export default withRouter(connect(mapStateToProps)(TenantOverview));
diff --git a/frontend/src/pages/Tenants/components/TenantModal.js b/frontend/src/pages/Tenants/components/TenantModal.js
new file mode 100644
index 0000000000000000000000000000000000000000..6a3dd63af927622e2107ea226dca558f19769dfd
--- /dev/null
+++ b/frontend/src/pages/Tenants/components/TenantModal.js
@@ -0,0 +1,133 @@
+import React, { Component } from 'react';
+import { Modal } from "react-bootstrap";
+import Form from "react-jsonschema-form";
+import PropTypes from 'prop-types';
+
+class TenantModal extends Component {
+  static propTypes = {
+    open: PropTypes.bool.isRequired,
+    setOpen: PropTypes.func.isRequired,
+    tenant: PropTypes.object,
+    onModify: PropTypes.func.isRequired,
+    onSubmit: PropTypes.func.isRequired,
+  };
+
+  state = {
+    editMode: false,
+    formData: undefined,
+    // for force re-rendering json form
+    rerenderCounter: 0,
+  };
+
+  state = {
+    formData: undefined,
+    editMode: false,
+    // for force re-rendering json form
+    rerenderCounter: 0,
+  };
+
+  schema = {
+    // title: "A registration form",
+    // "description": "A simple form example.",
+    type: "object",
+    required: [
+      "tenantName",
+    ],
+    properties: {
+      tenantId: {
+        type: "string",
+        title: "Tenant ID",
+        pattern: "^[0-9a-zA-Z-]*$",
+        default: "",
+        readOnly: true,
+      },
+      tenantName: {
+        type: "string",
+        title: "Tenant Name",
+        default: "",
+      },
+    },
+  };
+
+  componentDidUpdate(prevProps, prevState, snapshot) {
+    if (prevProps !== this.props) {
+      this.setState({ editMode: !!this.props.tenant });
+
+      if (this.props.tenant) {
+        const tenant = this.props.tenant;
+
+        let formData = {
+          tenantId: tenant['tenantId'],
+          tenantName: tenant['tenantName'],
+        };
+
+        this.updateFormData(formData).then();
+      } else {
+        let formData = {
+          tenantId: "",
+          tenantName: "",
+        };
+        this.updateFormData(formData).then();
+      }
+    }
+  }
+
+  async onChange(data) {
+    const lastData = this.state.formData;
+
+    if (lastData && lastData.tenantId === undefined)
+      lastData.tenantId = "";
+  }
+
+  async updateFormData(newData) {
+    // Workaround for bug: https://github.com/rjsf-team/react-jsonschema-form/issues/758
+    await this.setState({ rerenderCounter: this.state.rerenderCounter + 1 });
+    await this.setState({
+      rerenderCounter: this.state.rerenderCounter + 1,
+      formData: newData,
+    });
+  }
+
+  onSubmitClick(result) {
+    const formData = result.formData;
+
+    let tenantData = {
+      "tenantId": formData["tenantId"],
+      "tenantName": formData["tenantName"]
+    };
+
+    if(this.state.editMode) {
+      this.props.onModify(tenantData);
+    } else {
+      this.props.onSubmit(tenantData);
+    }
+  }
+
+  render() {
+    return (
+      <Modal
+        show={this.props.open}
+        className={"fields__edit-modal theme-light"}
+        backdrop={"static"}
+        onHide={this.props.setOpen.bind(this, false)}>
+        <Modal.Header closeButton>
+          <Modal.Title id="example-modal-sizes-title-lg">
+            {this.state.editMode ? "Edit Tenant" : "New Tenant"}
+          </Modal.Title>
+        </Modal.Header>
+
+        <Modal.Body>
+          {this.state.rerenderCounter % 2 === 0 &&
+            <Form schema={this.schema}
+              formData={this.state.formData}
+              onChange={this.onChange.bind(this)}
+              onSubmit={this.onSubmitClick.bind(this)} />
+          }
+        </Modal.Body>
+      </Modal>
+    );
+
+  }
+}
+
+export default TenantModal;
diff --git a/frontend/src/pages/Tenants/index.js b/frontend/src/pages/Tenants/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..9511e0dae9648184e339c24b81b840f43e8562f8
--- /dev/null
+++ b/frontend/src/pages/Tenants/index.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import {Route} from 'react-router-dom';
+import TenantOverview from "./TenantOverview";
+
+const Tenants = ({match}) => (
+  <div className="content">
+    <Route exact path={`${match.url}/`} component={TenantOverview} />
+  </div>
+);
+
+export default Tenants;
diff --git a/frontend/src/pages/Users/UserOverview.js b/frontend/src/pages/Users/UserOverview.js
new file mode 100644
index 0000000000000000000000000000000000000000..6422d992e8bd03af33a063421dadec0177fe0f1a
--- /dev/null
+++ b/frontend/src/pages/Users/UserOverview.js
@@ -0,0 +1,140 @@
+import React, { Component } from 'react';
+import { withRouter } from "react-router-dom";
+import { connect } from "react-redux";
+import { Button, Table } from "react-bootstrap";
+import UserModal from "./components/UserModal";
+import ApiHelper from "../../util/ApiHelper";
+
+class UserOverview extends Component {
+  state = {
+    userModalOpen: false,
+    userModalData: null,
+  };
+
+  async componentDidMount() {
+    const tenantId = this.props.match.url.replace(/^.*[\\\/]/, '');
+
+    ApiHelper.fetchUsers(tenantId).then();
+
+    const tenant = await ApiHelper.fetchTenantById(tenantId);
+    this.setState({
+      tenantId: tenantId,
+      tenantName: tenant.tenantName,
+    });
+  }
+
+  openAddUser() {
+    this.setState({
+      userModalOpen: true,
+      userModalData: null,
+    });
+  }
+
+  /**
+   * @param userId  {string}
+   */
+  async openEditUser(userId) {
+    const user = await ApiHelper.fetchUserById(this.state.tenantId, userId);
+
+    this.setState({
+      userModalOpen: true,
+      userModalData: user,
+    });
+  }
+
+  async addUser(userData) {
+    this.setState({ userModalOpen: false });
+
+    if (!await ApiHelper.createUser(this.state.tenantId, userData)) {
+      alert("Error creating new user");
+    }
+    ApiHelper.fetchUsers(this.state.tenantId).then();
+  }
+
+  /**
+   * @param userData
+   */
+  async updateUser(userData) {
+    this.setState({ userModalOpen: false });
+
+    const result = await ApiHelper.updateUser(this.state.tenantId, userData.userId, userData);
+
+    if (!result) {
+      alert("Error updating user: " + userData["userId"]);
+    }
+    ApiHelper.fetchUsers(this.state.tenantId).then();
+  }
+
+  /**
+  * @param user  {User}
+   */
+  async deleteUser(user) {
+    if (!window.confirm(`Delete user ${user.id}?`))
+      return;
+
+    const result = await ApiHelper.deleteUser(this.state.tenantId, user.id);
+    ApiHelper.fetchUsers(this.state.tenantId).then();
+    if (!result) {
+      alert("Error deleting user: " + user.id);
+    }
+  }
+
+  render() {
+    return (
+      <div className="container-fluid">
+        <div className="row">
+          <div className="col-md-12">
+            <div className="card">
+              <div className="header subscribers__header">
+                <h4>Users ({this.state.tenantName})</h4>
+                <Button bsStyle={"primary"} className="subscribers__button"
+                  onClick={this.openAddUser.bind(this)}>
+                  New User
+                </Button>
+              </div>
+              <div className="content subscribers__content">
+                <Table className="subscribers__table" striped bordered condensed hover>
+                  <thead>
+                    <tr>
+                      <th style={{ width: 400 }}>User ID</th>
+                      <th colSpan={2}>User Email</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    {this.props.users.map(user => (
+                      <tr key={user.id}>
+                        <td>{user.id}</td>
+                        <td>{user.email}</td>
+                        <td style={{ textAlign: 'center' }}>
+                          <Button variant="danger" onClick={this.deleteUser.bind(this, user)}>Delete</Button>
+                         &nbsp;&nbsp;&nbsp;&nbsp;
+                        <Button variant="info" onClick={this.openEditUser.bind(this, user.id)}>Modify</Button>
+                        </td>
+                      </tr>
+                    ))}
+                  </tbody>
+                </Table>
+
+                <p>&nbsp;</p><p>&nbsp;</p>
+                <p>&nbsp;</p><p>&nbsp;</p>
+                <p>&nbsp;</p><p>&nbsp;</p>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <UserModal open={this.state.userModalOpen}
+          setOpen={val => this.setState({ userModalOpen: val })}
+          user={this.state.userModalData}
+          onModify={this.updateUser.bind(this)}
+          onSubmit={this.addUser.bind(this)} />
+      </div>
+    );
+  }
+}
+
+const mapStateToProps = state => ({
+   users: state.user.users,
+});
+
+export default withRouter(connect(mapStateToProps)(UserOverview));
diff --git a/frontend/src/pages/Users/components/UserModal.js b/frontend/src/pages/Users/components/UserModal.js
new file mode 100644
index 0000000000000000000000000000000000000000..1d42d7f548ea7a13eb355a7f1fcd2815ce47e121
--- /dev/null
+++ b/frontend/src/pages/Users/components/UserModal.js
@@ -0,0 +1,141 @@
+import React, { Component } from 'react';
+import { Modal } from "react-bootstrap";
+import Form from "react-jsonschema-form";
+import PropTypes from 'prop-types';
+
+class UserModal extends Component {
+  static propTypes = {
+    open: PropTypes.bool.isRequired,
+    setOpen: PropTypes.func.isRequired,
+    user: PropTypes.object,
+    onModify: PropTypes.func.isRequired,
+    onSubmit: PropTypes.func.isRequired,
+  };
+
+  state = {
+    editMode: false,
+    formData: undefined,
+    // for force re-rendering json form
+    rerenderCounter: 0,
+  };
+
+  state = {
+    formData: undefined,
+    editMode: false,
+    // for force re-rendering json form
+    rerenderCounter: 0,
+  };
+
+  schema = {
+    // title: "A registration form",
+    // "description": "A simple form example.",
+    type: "object",
+    required: [
+      "email",
+    ],
+    properties: {
+      userId: {
+        type: "string",
+        title: "User ID",
+        pattern: "^[0-9a-zA-Z-]*$",
+        default: "",
+        readOnly: true,
+      },
+      email: {
+        type: "string",
+        title: "User Email",
+        default: "",
+      },
+      password: {
+        type: "string",
+        title: "Password",
+        default: "",
+      },
+    },
+  };
+
+  componentDidUpdate(prevProps, prevState, snapshot) {
+    if (prevProps !== this.props) {
+      this.setState({ editMode: !!this.props.user });
+
+      if (this.props.user) {
+        const user = this.props.user;
+
+        let formData = {
+          userId: user['userId'],
+          email: user['email'],
+          password: user['password'],
+        };
+
+        this.updateFormData(formData).then();
+      } else {
+        let formData = {
+          userId: "",
+          email: "",
+          password: "",
+        };
+        this.updateFormData(formData).then();
+      }
+    }
+  }
+
+  async onChange(data) {
+    const lastData = this.state.formData;
+
+    if (lastData && lastData.userId === undefined)
+      lastData.userId = "";
+  }
+
+  async updateFormData(newData) {
+    // Workaround for bug: https://github.com/rjsf-team/react-jsonschema-form/issues/758
+    await this.setState({ rerenderCounter: this.state.rerenderCounter + 1 });
+    await this.setState({
+      rerenderCounter: this.state.rerenderCounter + 1,
+      formData: newData,
+    });
+  }
+
+  onSubmitClick(result) {
+    const formData = result.formData;
+
+    let userData = {
+      "userId": formData["userId"],
+      "email": formData["email"],
+      "password": formData["password"]
+    };
+
+    if(this.state.editMode) {
+      this.props.onModify(userData);
+    } else {
+      this.props.onSubmit(userData);
+    }
+  }
+
+  render() {
+    return (
+      <Modal
+        show={this.props.open}
+        className={"fields__edit-modal theme-light"}
+        backdrop={"static"}
+        onHide={this.props.setOpen.bind(this, false)}>
+        <Modal.Header closeButton>
+          <Modal.Title id="example-modal-sizes-title-lg">
+            {this.state.editMode ? "Edit User" : "New User"}
+          </Modal.Title>
+        </Modal.Header>
+
+        <Modal.Body>
+          {this.state.rerenderCounter % 2 === 0 &&
+            <Form schema={this.schema}
+              formData={this.state.formData}
+              onChange={this.onChange.bind(this)}
+              onSubmit={this.onSubmitClick.bind(this)} />
+          }
+        </Modal.Body>
+      </Modal>
+    );
+
+  }
+}
+
+export default UserModal;
diff --git a/frontend/src/pages/Users/index.js b/frontend/src/pages/Users/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..6308c8f46a84ead20c8d70856c242dc0e9d09c16
--- /dev/null
+++ b/frontend/src/pages/Users/index.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import {Route} from 'react-router-dom';
+import UserOverview from "./UserOverview";
+
+const Users = ({match}) => (
+  <div className="content">
+    <Route exact path={`${match.url}/`} component={UserOverview} />
+  </div>
+);
+
+export default Users;
diff --git a/frontend/src/redux/actions/tenantActions.js b/frontend/src/redux/actions/tenantActions.js
new file mode 100644
index 0000000000000000000000000000000000000000..3ad6348594ee1b59ff859c376fea90ca8f966ca4
--- /dev/null
+++ b/frontend/src/redux/actions/tenantActions.js
@@ -0,0 +1,11 @@
+
+export default class tenantActions {
+  static SET_TENANTS = 'TENANT/SET_TENANTS';
+
+  static setTenants(tenants) {
+    return {
+      type: this.SET_TENANTS,
+      tenants: tenants,
+    };
+  }
+}
diff --git a/frontend/src/redux/actions/userActions.js b/frontend/src/redux/actions/userActions.js
new file mode 100644
index 0000000000000000000000000000000000000000..6e8ea4c21fc56d9ff23ad9ae02e7d00823b80b70
--- /dev/null
+++ b/frontend/src/redux/actions/userActions.js
@@ -0,0 +1,11 @@
+
+export default class userActions {
+  static SET_USERS = 'USER/SET_USERS';
+
+  static setUsers(users) {
+    return {
+      type: this.SET_USERS,
+      users: users,
+    };
+  }
+}
diff --git a/frontend/src/redux/reducers/index.js b/frontend/src/redux/reducers/index.js
index bef8ba6278fc1d9ba36cebb7b9ee2676528b98c5..b7e91bff53fb9243698550ff9d33bbae84a1ad76 100644
--- a/frontend/src/redux/reducers/index.js
+++ b/frontend/src/redux/reducers/index.js
@@ -3,11 +3,15 @@ import auth from './auth';
 import layout from './layout';
 import subscriber from "./subscriber";
 import ueinfo from "./ueinfo";
+import tenant from "./tenant";
+import user from "./user";
 
 export default {
   auth,
   layout,
   subscriber,
   ueinfo,
+  tenant,
+  user,
   form: formReducer,
 };
diff --git a/frontend/src/redux/reducers/tenant.js b/frontend/src/redux/reducers/tenant.js
new file mode 100644
index 0000000000000000000000000000000000000000..c5e9be401cbd7144cfed5ac728647eab5f13107c
--- /dev/null
+++ b/frontend/src/redux/reducers/tenant.js
@@ -0,0 +1,26 @@
+import actions from '../actions/tenantActions';
+
+const initialState = {
+  tenants: [],
+  tenantsMap: {}
+};
+
+export default function reducer(state = initialState, action) {
+  let nextState = {...state};
+
+  switch (action.type) {
+    case actions.SET_TENANTS:
+      nextState.tenants = action.tenants;
+      nextState.tenantsMap = createTenantsMap(action.tenants);
+      return nextState;
+
+    default:
+      return state;
+  }
+}
+
+function createTenantsMap(tenants) {
+  let tenantsMap = {};
+  tenants.forEach(tenants => tenantsMap[tenants['id']] = tenants);
+  return tenantsMap;
+}
diff --git a/frontend/src/redux/reducers/user.js b/frontend/src/redux/reducers/user.js
new file mode 100644
index 0000000000000000000000000000000000000000..ad22f8ec2233c94cbc34ea6268b0fa29d454e940
--- /dev/null
+++ b/frontend/src/redux/reducers/user.js
@@ -0,0 +1,26 @@
+import actions from '../actions/userActions';
+
+const initialState = {
+  users: [],
+  usersMap: {}
+};
+
+export default function reducer(state = initialState, action) {
+  let nextState = {...state};
+
+  switch (action.type) {
+    case actions.SET_USERS:
+      nextState.users = action.users;
+      nextState.usersMap = createUsersMap(action.users);
+      return nextState;
+
+    default:
+      return state;
+  }
+}
+
+function createUsersMap(users) {
+  let usersMap = {};
+  users.forEach(users => usersMap[users['id']] = users);
+  return usersMap;
+}
diff --git a/frontend/src/util/ApiHelper.js b/frontend/src/util/ApiHelper.js
index 5dc35bae53b714d3ca84f15cc8f108cdc5d66364..b1a0a4b43f57ef1c7f2f1a3793ff4cd006502582 100644
--- a/frontend/src/util/ApiHelper.js
+++ b/frontend/src/util/ApiHelper.js
@@ -2,11 +2,19 @@ import Http from './Http';
 import {store} from '../index';
 import subscriberActions from "../redux/actions/subscriberActions";
 import Subscriber from "../models/Subscriber";
+import tenantActions from "../redux/actions/tenantActions";
+import Tenant from "../models/Tenant";
+import userActions from "../redux/actions/userActions";
+import User from "../models/User";
+import axios from 'axios';
+import LocalStorageHelper from "./LocalStorageHelper";
 
 class ApiHelper {
 
   static async fetchSubscribers() {
     try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
       let response = await Http.get('subscriber');
       if (response.status === 200 && response.data) {
         const subscribers = response.data.map(val => new Subscriber(val['ueId'], val['plmnID']));
@@ -33,6 +41,8 @@ class ApiHelper {
 
   static async createSubscriber(subscriberData) {
     try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
       let response = await Http.post(
         `subscriber/${subscriberData["ueId"]}/${subscriberData["plmnID"]}`, subscriberData);
       if (response.status === 201)
@@ -67,7 +77,171 @@ class ApiHelper {
     }
 
     return false;
-  } 
+  }
+
+  static async login(loginRequest) {
+    console.log("login");
+    try {
+      let response = await Http.post(`login`, loginRequest);
+      return response;
+    } catch (error) {
+      console.error(error);
+    }
+    return false;
+  }
+
+  static async fetchTenants() {
+    try {
+      store.dispatch(tenantActions.setTenants([]));
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      let response = await Http.get('tenant');
+      if (response.status === 200 && response.data) {
+        const tenants = response.data.map(val => new Tenant(val['tenantId'], val['tenantName']));
+        store.dispatch(tenantActions.setTenants(tenants));
+        return true;
+      }
+    } catch (error) {
+    }
+
+    return false;
+  }
+
+  static async fetchTenantById(id) {
+    try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      let response = await Http.get(`tenant/${id}`);
+      if (response.status === 200 && response.data) {
+        return response.data;
+      }
+    } catch (error) {
+    }
+
+    return false;
+  }
+
+  static async createTenant(tenantData) {
+    try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      let response = await Http.post(
+        'tenant', tenantData);
+      if (response.status === 200)
+        return true;
+    } catch (error) {
+      console.error(error);
+    }
+
+    return false;
+  }
+
+  static async updateTenant(tenantData) {
+    try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      let response = await Http.put(
+        `tenant/${tenantData["tenantId"]}`, tenantData);
+      if (response.status === 200)
+        return true;
+    } catch (error) {
+      console.error(error);
+    }
+
+    return false;
+  }
+
+  static async deleteTenant(id) {
+    try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      let response = await Http.delete(`tenant/${id}`);
+      if (response.status === 200)
+        return true;
+    } catch (error) {
+      console.error(error);
+    }
+
+    return false;
+  }
+
+  static async fetchUsers(tenantId) {
+    try {
+      store.dispatch(userActions.setUsers([]));
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      let response = await Http.get(`tenant/${tenantId}/user`);
+      if (response.status === 200 && response.data) {
+        const users = response.data.map(val => new User('', '', '', val['userId'], val['email']));
+        store.dispatch(userActions.setUsers(users));
+        return true;
+      }
+    } catch (error) {
+    }
+
+    return false;
+  }
+
+  static async fetchUserById(tenantId, id) {
+    try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      let response = await Http.get(`tenant/${tenantId}/user/${id}`);
+      if (response.status === 200 && response.data) {
+        return response.data;
+      }
+    } catch (error) {
+    }
+
+    return false;
+  }
+
+  static async createUser(tenantId, userData) {
+    try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      userData['encryptedPassword'] = userData['password'];
+      let response = await Http.post(
+        `tenant/${tenantId}/user`, userData);
+      if (response.status === 200)
+        return true;
+    } catch (error) {
+      console.error(error);
+    }
+
+    return false;
+  }
+
+  static async updateUser(tenantId, userId, userData) {
+    try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      userData['encryptedPassword'] = userData['password'];
+      let response = await Http.put(
+        `tenant/${tenantId}/user/${userId}`, userData);
+      if (response.status === 200)
+        return true;
+    } catch (error) {
+      console.error(error);
+    }
+
+    return false;
+  }
+
+  static async deleteUser(tenantId, id) {
+    try {
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
+      let response = await Http.delete(`tenant/${tenantId}/user/${id}`);
+      if (response.status === 200)
+        return true;
+    } catch (error) {
+      console.error(error);
+    }
+
+    return false;
+  }
+
 }
 
 export default ApiHelper;
diff --git a/frontend/src/util/AuthHelper.js b/frontend/src/util/AuthHelper.js
index ade18d87cb1866409510049339ae610c483de016..4f902d896083a0607777c74e95f788d578511f52 100644
--- a/frontend/src/util/AuthHelper.js
+++ b/frontend/src/util/AuthHelper.js
@@ -2,6 +2,8 @@ import {store} from '../index';
 import authActions from '../redux/actions/authActions';
 import config from '../config/config';
 import User from "../models/User";
+import ApiHelper from "./ApiHelper";
+import LocalStorageHelper from "./LocalStorageHelper";
 
 export default class AuthHelper {
 
@@ -13,11 +15,23 @@ export default class AuthHelper {
    */
   static async login(username, password) {
     if (username === config.USERNAME && password === config.PASSWORD) {
-      let user = new User(username, "System Administrator");
+      let user = new User(username, "System Administrator", "admin");
+      LocalStorageHelper.setUserInfo(user);
       store.dispatch(authActions.setUser(user));
       return true;
     } else {
-      return false;
+      let response = await ApiHelper.login({username: username, password: password});
+      if (response === undefined) {
+        return false;
+      }
+      if (response.status === 200) {
+        let user = new User(username, "User", response.data.access_token);
+        LocalStorageHelper.setUserInfo(user);
+        store.dispatch(authActions.setUser(user));
+        return true;
+      } else {
+        return false;
+      }
     }
   }
 
diff --git a/frontend/src/util/LocalStorageHelper.js b/frontend/src/util/LocalStorageHelper.js
index 4b0b77ab5ae8d20305a690a743763723ca8bf4ad..27988eff937abe8c2ee4a9d9fc5c7344cfc931df 100644
--- a/frontend/src/util/LocalStorageHelper.js
+++ b/frontend/src/util/LocalStorageHelper.js
@@ -14,6 +14,7 @@ export default class LocalStorageHelper {
    */
   static getUserInfo() {
     let json = localStorage.getItem('user_info');
-    return json === null ? null : ApiTokens.deserialize(json);
+    // return json === null ? null : ApiTokens.deserialize(json);
+    return json === null ? null : User.deserialize(json);
   }
 }
diff --git a/frontend/src/util/UEInfoApiHelper.js b/frontend/src/util/UEInfoApiHelper.js
index c6b01f04de13c430196473e0f8dadd5e38ba7b4e..67b06c050ade99324e098b4e1e7e6db19e6d5f8b 100644
--- a/frontend/src/util/UEInfoApiHelper.js
+++ b/frontend/src/util/UEInfoApiHelper.js
@@ -2,6 +2,8 @@ import Http from './Http';
 import {store} from '../index';
 import ueinfoActions from "../redux/actions/ueinfoActions";
 import UEInfo from "../models/UEInfo";
+import axios from 'axios';
+import LocalStorageHelper from "./LocalStorageHelper";
 
 class UeInfoApiHelper {
 
@@ -11,6 +13,8 @@ class UeInfoApiHelper {
     try {
       let url =  "registered-ue-context"
       // console.log("Making request to ", url, " ....")
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
       let response = await Http.get(url);
       if (response.status === 200) {
         let registered_users = [];
@@ -20,7 +24,7 @@ class UeInfoApiHelper {
           );
           store.dispatch(ueinfoActions.setRegisteredUE(registered_users));
         } else {
-          store.dispatch(ueinfoActions.unsetRegisteredUEError());
+          store.dispatch(ueinfoActions.setRegisteredUE(registered_users));
         }
         return true;
       } else {
@@ -55,6 +59,8 @@ class UeInfoApiHelper {
       let url = `registered-ue-context/${supi}`
       // console.log("Making request to ", url, " ....")
 
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
       let response = await Http.get(url);
       if (response.status === 200 && response.data) {
         //To do: implement set rgistered ue action
@@ -84,6 +90,8 @@ class UeInfoApiHelper {
       let  url = `ue-pdu-session-info/${smContextRef}`
       // console.log("Making request to ", url, " ....")
 
+      let user = LocalStorageHelper.getUserInfo();
+      axios.defaults.headers.common['Token'] = user.accessToken;
       let response = await Http.get(url);
       if (response.status === 200 && response.data) {
         //To do: implement set rgistered ue action
diff --git a/go.mod b/go.mod
index e91bdbb9537d4d68225dc87cb3dc92e25a4d0f2d..232f667c1cbe1b8f7efe0ad110bf52084927c3ab 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.14
 require (
 	github.com/antonfisher/nested-logrus-formatter v1.3.0
 	github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
+	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/free5gc/MongoDBLibrary v1.0.0
 	github.com/free5gc/logger_conf v1.0.0
 	github.com/free5gc/logger_util v1.0.0
@@ -13,10 +14,12 @@ require (
 	github.com/free5gc/version v1.0.0
 	github.com/gin-contrib/cors v1.3.1
 	github.com/gin-gonic/gin v1.6.3
+	github.com/google/uuid v1.3.0
 	github.com/mitchellh/mapstructure v1.4.0
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sirupsen/logrus v1.7.0
 	github.com/urfave/cli v1.22.5
 	go.mongodb.org/mongo-driver v1.4.4
+	golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
 	gopkg.in/yaml.v2 v2.4.0
 )
diff --git a/go.sum b/go.sum
index 140ab64508a4010f7212bdf01b2412247fdf3666..a76e669fb3d8d0a14384b88323db5b487a73df7c 100644
--- a/go.sum
+++ b/go.sum
@@ -171,6 +171,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=