Feature: Multi-user: Changes to initial admin login processing / implement JWT (#140)

* Add simple JWT parsing function

* Implement user action for global store
This commit is contained in:
Brandon Lee Kitajchuk
2022-10-26 09:21:25 -07:00
committed by GitHub
parent 50d93b0089
commit 3093f40e00
6 changed files with 55 additions and 12 deletions
+4 -2
View File
@@ -1,5 +1,7 @@
{
"token": "327a2d17-ce9b-45a7-b0ff-a556536d27fb",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3NpZCI6IjFjNTc4MWQyLTY5MGItNDIwYy1iZDUzLTVkN2Y1NjMwMDVjOCIsInNjb3BlIjoiYWRtaW4iLCJmb3JjZV9jaGFuZ2UiOnRydWUsInBlcm1pc3Npb25zIjpbIlBST1ZJU0lPTl9VU0VSUyIsIlBST1ZJU0lPTl9TRVJWSUNFUyIsIlZJRVdfT05MWSJdLCJpYXQiOjE2NjY3OTgzMTEsImV4cCI6MTY2NjgwMTkxMX0.ZV3KnRit8WGpipfiiMAZ2AVLQ25csWje1-K6hdqxktE",
"scope": "admin",
"user_sid": "78131ad5-f041-4d5d-821c-47b2d8c6d015",
"force_change": false
"force_change": false,
"permissions": ["VIEW_ONLY", "PROVISION_SERVICES", "PROVISION_USERS"]
}
+19 -2
View File
@@ -10,6 +10,22 @@ export type IpType = "ip" | "fqdn" | "fqdn-top-level" | "invalid";
export type LimitCategories = "api_rate" | "voice_call_session" | "device";
/** User roles / permissions */
export type UserScopes = "admin" | "service_provider" | "account";
export type UserPermissions =
| "VIEW_ONLY"
| "PROVISION_SERVICES"
| "PROVISION_USERS";
// We'll want something like this for actual permissions implementation...
// export enum UserPermissions {
// VIEW_ONLY = 0,
// PROVISION_SERVICES = 1,
// PROVISION_USERS = 2,
// }
/** Status codes */
export enum StatusCodes {
@@ -90,12 +106,13 @@ export interface PasswordSettings {
/** API responses/payloads */
export interface User {
scope: UserScopes;
user_sid: string;
permissions: UserPermissions[];
}
export interface UserLogin {
export interface UserLogin extends User {
token: string;
user_sid: string;
force_change: boolean;
}
+5 -4
View File
@@ -108,6 +108,7 @@ export const Navi = ({
/** Fetch service providers */
useEffect(() => {
dispatch({ type: "user" });
dispatch({ type: "serviceProviders" });
}, []);
@@ -166,12 +167,12 @@ export const Navi = ({
<Icons.PlusCircle />
</button>
</div>
{/* Until we have the APIs this initial UI is not accessible */}
{user && (
{/* Intentionally hiding this as we will need to work this out as we go... */}
{/* ACL component will need to be updated for new user scope/permissions handling */}
{false && user && (
<div className="navi__user">
{/* Seed should be user sid when we have the APIs for it... */}
<Blockies
seed="jambonz"
seed={user!.user_sid}
size={6}
scale={6}
color={getCssVar("--jambonz")}
+20
View File
@@ -60,6 +60,26 @@ export const setToken = (token: string) => {
localStorage.setItem(storageKey, token);
};
/**
* Decode data from a JWT
* https://stackoverflow.com/questions/38552003/how-to-decode-jwt-token-in-javascript-without-using-a-library
*/
export const parseJwt = (token: string) => {
const base64Url = token.split(".")[1];
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
const jsonPayload = decodeURIComponent(
window
.atob(base64)
.split("")
.map((c) => {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
})
.join("")
);
return JSON.parse(jsonPayload);
};
/**
* Provider hook that creates auth object and handles state
*/
+5 -3
View File
@@ -1,5 +1,6 @@
import { getUser, getServiceProviders } from "src/api";
import { getServiceProviders } from "src/api";
import { sortLocaleName } from "src/utils";
import { getToken, parseJwt } from "src/router/auth";
import type { State, Action } from "./types";
import type { ServiceProvider, User } from "src/api/types";
@@ -61,8 +62,9 @@ export const currentServiceProviderAction = (
};
export const userAsyncAction = async (): Promise<User> => {
const response = await getUser("user_sid");
return response.json;
const token = getToken();
const { user_sid, permissions, scope } = parseJwt(token);
return { user_sid, permissions, scope };
};
export const serviceProvidersAsyncAction = async (): Promise<
+2 -1
View File
@@ -6,6 +6,7 @@ import { AuthContext } from "src/router/auth";
import { MSG_SOMETHING_WRONG } from "src/constants";
import type { AuthStateContext } from "src/router/auth";
import type { UserLogin } from "src/api/types";
import userLogin from "../../cypress/fixtures/userLogin.json";
@@ -19,7 +20,7 @@ type LayoutProviderProps = TestProviderProps & {
};
export const signinError = () => Promise.reject(MSG_SOMETHING_WRONG);
export const signinSuccess = () => Promise.resolve(userLogin);
export const signinSuccess = () => Promise.resolve(userLogin as UserLogin);
export const signout = () => undefined;
export const authProps: AuthStateContext = {
token: "",