Skip to content
Snippets Groups Projects
Commit 34506174 authored by Behrisch, M. (Michael)'s avatar Behrisch, M. (Michael)
Browse files

feat: :zap: adds keycloak for JWT and auth mgmt

Replaces the custom made auth-backend (auth-gateway)
with its token logic and complexity
with established open-source
FIPS 140-2 compliant authentification solution

does not use auth-gateway anymore for authentification. close: #28
parent c980f62f
No related branches found
No related tags found
1 merge request!28feat: :zap: adds keycloak for JWT and auth mgmt
Pipeline #124123 failed
VITE_BACKEND_URL=api.graphpolaris.com VITE_BACKEND_URL=api.graphpolaris.com
VITE_STAGING=local VITE_STAGING=local
\ No newline at end of file
...@@ -2,5 +2,8 @@ interface ImportMeta { ...@@ -2,5 +2,8 @@ interface ImportMeta {
env: { env: {
VITE_BACKEND_URL: string; VITE_BACKEND_URL: string;
VITE_STAGING: string; VITE_STAGING: string;
VITE_KEYCLOAK_URL: string;
VITE_KEYCLOAK_REALM: string;
VITE_KEYCLOAK_CLIENT: string;
} }
} }
\ No newline at end of file
import { useEffect, useRef, useState } from 'react';
import GridLayout from 'react-grid-layout';
import Panel from '../components/panels/panel';
import { RawJSONVis } from '@graphpolaris/shared/lib/vis/rawjsonvis/rawjsonvis';
import SemanticSubstrates from '@graphpolaris/shared/lib/vis/semanticsubstrates/semanticsubstrates';
import { Schema } from '@graphpolaris/shared/lib/schema/panel';
import {
Query2BackendQuery,
QueryBuilder,
} from '@graphpolaris/shared/lib/querybuilder';
import {
assignNewGraphQueryResult,
useAppDispatch,
} from '@graphpolaris/shared/lib/data-access/store';
import { Navbar } from '../components/navbar/navbar';
import { VisualizationPanel } from './panels/Visualization';
import { import {
readInSchemaFromBackend, readInSchemaFromBackend,
useAuthorization, useAuth,
useAuthorizationCache, useAuthorizationCache,
useDatabaseAPI, useDatabaseAPI,
useQueryAPI, useQueryAPI,
useQuerybuilderGraph, useQuerybuilderGraph,
useQuerybuilderGraphology,
useQuerybuilderHash, useQuerybuilderHash,
useSchemaAPI, useSchemaAPI,
useSessionCache, useSessionCache
} from '@graphpolaris/shared/lib/data-access'; } from '@graphpolaris/shared/lib/data-access';
import LoginScreen from '../components/login/loginScreen';
import { WebSocketHandler } from '@graphpolaris/shared/lib/data-access/socket'; import { WebSocketHandler } from '@graphpolaris/shared/lib/data-access/socket';
import Broker from '@graphpolaris/shared/lib/data-access/socket/broker'; import Broker from '@graphpolaris/shared/lib/data-access/socket/broker';
import {
assignNewGraphQueryResult,
useAppDispatch,
} from '@graphpolaris/shared/lib/data-access/store';
import { GraphQueryResultFromBackend, resetGraphQueryResults } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
import { SchemaFromBackend } from '@graphpolaris/shared/lib/model/backend'; import { SchemaFromBackend } from '@graphpolaris/shared/lib/model/backend';
import {
Query2BackendQuery,
QueryBuilder,
} from '@graphpolaris/shared/lib/querybuilder';
import { Schema } from '@graphpolaris/shared/lib/schema/panel';
import { useEffect, useRef } from 'react';
import LoginScreen from '../components/login/loginScreen';
import { Navbar } from '../components/navbar/navbar';
import Panel from '../components/panels/panel';
import { domain } from '../environments/variables'; import { domain } from '../environments/variables';
import { QueryMultiGraphExport } from '@graphpolaris/shared/lib/querybuilder/graph/graphology/utils'; import { VisualizationPanel } from './panels/Visualization';
import { GraphQueryResultFromBackend, resetGraphQueryResults } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
import { clearQB } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
export function App() { export function App() {
const { AuthorizeFromCache, auth } = useAuthorization(domain); const isLogin = useAuth();
const auth = useAuthorizationCache()
const api = useDatabaseAPI(domain); const api = useDatabaseAPI(domain);
const api_schema = useSchemaAPI(domain); const api_schema = useSchemaAPI(domain);
const api_query = useQueryAPI(domain); const api_query = useQueryAPI(domain);
...@@ -46,9 +43,13 @@ export function App() { ...@@ -46,9 +43,13 @@ export function App() {
const queryHash = useQuerybuilderHash(); const queryHash = useQuerybuilderHash();
const ws = useRef(new WebSocketHandler(domain)); const ws = useRef(new WebSocketHandler(domain));
// for testing purposes
// useEffect(() => {
// console.info('Authentification changed', auth)
// }, [auth]);
useEffect(() => { useEffect(() => {
// Default // Default
AuthorizeFromCache();
Broker.instance().subscribe( Broker.instance().subscribe(
(data: SchemaFromBackend) => dispatch(readInSchemaFromBackend(data)), (data: SchemaFromBackend) => dispatch(readInSchemaFromBackend(data)),
'schema_result' 'schema_result'
...@@ -77,11 +78,13 @@ export function App() { ...@@ -77,11 +78,13 @@ export function App() {
useEffect(() => { useEffect(() => {
// Newly (un)authorized // Newly (un)authorized
console.log(auth.authorized);
if (auth.authorized) { if (auth.authorized) {
console.info("App is authorized; Getting Datatabases", isLogin);
api.GetAllDatabases({ updateSessionCache: true }); api.GetAllDatabases({ updateSessionCache: true });
} else {
// TODO clear all data from redux store; Issue id: #31
} }
}, [auth.authorized]); }, [isLogin]);
useEffect(() => { useEffect(() => {
// New query // New query
......
VITE_KEYCLOAK_URL=https://login.graphpolaris.com/
VITE_KEYCLOAK_REALM=graphpolaris
VITE_KEYCLOAK_CLIENT=web
\ No newline at end of file
...@@ -69,7 +69,7 @@ export const useDatabaseAPI = (domain: string) => { ...@@ -69,7 +69,7 @@ export const useDatabaseAPI = (domain: string) => {
async function GetAllDatabases(options: GetDatabasesOptions = {}): Promise<Array<string>> { async function GetAllDatabases(options: GetDatabasesOptions = {}): Promise<Array<string>> {
const { updateSessionCache: updateDatabaseCache = true } = options; const { updateSessionCache: updateDatabaseCache = true } = options;
console.log(accessToken); // console.log(accessToken);
const response = await fetch(`https://${domain}/user/database`, { const response = await fetch(`https://${domain}/user/database`, {
method: 'GET', method: 'GET',
credentials: 'same-origin', credentials: 'same-origin',
......
import { useEffect, useState } from 'react';
import { useAppDispatch, useAuthorizationCache } from '../store'; import { useAppDispatch, useAuthorizationCache } from '../store';
import { dispatch } from 'd3';
import { authorized, unauthorized, updateAccessToken } from '../store/authSlice'; import { authorized, unauthorized, updateAccessToken } from '../store/authSlice';
/** /**
...@@ -155,6 +153,9 @@ export function useAuthorization(domain: string) { ...@@ -155,6 +153,9 @@ export function useAuthorization(domain: string) {
* @returns true is authorization was successful, else returns false * @returns true is authorization was successful, else returns false
*/ */
async function AuthorizeFromCache(): Promise<boolean> { async function AuthorizeFromCache(): Promise<boolean> {
// Attempt to log in with a refresh-token // Attempt to log in with a refresh-token
const authResponse = await getNewAccessToken(domain, auth.accessToken); const authResponse = await getNewAccessToken(domain, auth.accessToken);
......
export * from './authorizationHook'; export * from './authorizationHook';
\ No newline at end of file export * from './useAuth';
import Keycloak from "keycloak-js";
import { useEffect, useRef, useState } from "react";
import { useAppDispatch } from '../store';
import { authorized } from '../store/authSlice';
export const useAuth = () => {
const keycloak = new Keycloak({
// url: import.meta.env.VITE_KEYCLOAK_URL,
// realm: import.meta.env.VITE_KEYCLOAK_REALM,
// clientId: import.meta.env.VITE_KEYCLOAK_CLIENT,
url: 'https://login.graphpolaris.com',
realm: 'graphpolaris' ,
clientId: 'web',
});
const isRun = useRef(false);
const [isLogin, setLogin] = useState(false);
const dispatch = useAppDispatch();
useEffect(() => {
if (isRun.current) return;
isRun.current = true;
keycloak
.init({
onLoad: 'login-required',
enableLogging : false,
}).then(async (isAuthenticated) => {
// console.log("useAuth useEffect", isAuthenticated, keycloak.idTokenParsed);
setLogin(isAuthenticated);
// just for example here:
// const profile = await getUserProfile();
// console.log("useAuth useEffect profile", profile);
await dispatch(
authorized({
// Info from https://www.keycloak.org/docs/latest/securing_apps/
userID: keycloak.idTokenParsed.preferred_username ?? '',
sessionID: keycloak.idTokenParsed.sid ?? '',
accessToken: keycloak.token ?? '',
authorized: true,
})
);
});
}, []);
const getUserProfile = async () => {
return keycloak.loadUserProfile()
.then(function(profile) {
return profile;
// console.info("user info", JSON.stringify(profile, null, " "))
}).catch(function() {
alert('Failed to load user profile');
});
}
return isLogin;
};
// export useAuth;
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
"graphology-layout-noverlap": "^0.4.2", "graphology-layout-noverlap": "^0.4.2",
"graphology-types": "^0.24.7", "graphology-types": "^0.24.7",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"keycloak-js": "^21.1.1",
"pixi.js": "^7.1.4", "pixi.js": "^7.1.4",
"react-cookie": "^4.1.1", "react-cookie": "^4.1.1",
"react-grid-layout": "^1.3.4", "react-grid-layout": "^1.3.4",
......
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment