Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get OIDC access tokens once the authentication redirect is successful #3250

Merged
13 changes: 0 additions & 13 deletions dashboard/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,6 @@ import { showToast } from "actions/toastActions";
import { useDispatch, useSelector } from "react-redux";
import { ReactKeycloakProvider } from "@react-keycloak/web";

const eventLogger = (event, error) => {
// Placeholder to perform logging action upon certain Keycloak events.
// Keycloak events are described under
// https://www.keycloak.org/docs/latest/securing_apps/#javascript-adapter-reference
};

const tokenLogger = (tokens) => {
// Placeholder for to perform action when new token is generated
// console.log('onKeycloakTokens', tokens["refreshToken"]);
};

const ProtectedRoute = ({ redirectPath = APP_ROUTES.AUTH, children }) => {
const loggedIn = Cookies.get("isLoggedIn");
const dispatch = useDispatch();
Expand Down Expand Up @@ -70,8 +59,6 @@ const App = () => {
{keycloak && (
<ReactKeycloakProvider
authClient={keycloak}
onEvent={eventLogger}
onTokens={tokenLogger}
initOptions={{
onLoad: "check-sso",
checkLoginIframe: true,
Expand Down
32 changes: 17 additions & 15 deletions dashboard/src/actions/authActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import Cookies from "js-cookie";
import { SUCCESS } from "assets/constants/overviewConstants";
import { showToast } from "actions/toastActions";

const maxWait = 50000; // Milliseconds
webbnh marked this conversation as resolved.
Show resolved Hide resolved
/**
* Timer to check if the endpoints are loaded.
* Wait for the Pbench Server endpoints to be loaded.
* @param {getState} getState object.
* @return {promise} promise object
*/
export function waitForKeycloak(getState) {
export function waitForEndpoints(getState) {
const waitStart = Date.now();
const maxWait = 50000; // Milliseconds
/**
* Loop to check if the endpoints are loaded.
* Settle the wait-for-endpoints promise.
* @param {resolve} resolve object.
* @param {reject} reject object
*/
Expand All @@ -28,16 +28,17 @@ export function waitForKeycloak(getState) {
setTimeout(check, 250, resolve, reject);
}
}
return new Promise((resolve, reject) => {
check(resolve, reject);
});
return new Promise((resolve, reject) => check(resolve, reject));
}

// Perform some house keeping when the user logs in
export const authCookies = () => async (dispatch, getState) => {
await waitForKeycloak(getState);
await waitForEndpoints(getState);
const keycloak = getState().apiEndpoint.keycloak;
if (keycloak.authenticated) {
// Set the isLoggedIn cookie with an expiry of OIDC refresh token.
// We have to convert the UNIX epoch seconds returned by the refresh token
// expiry to milliseconds before we can use it for creating a Date object.
Cookies.set("isLoggedIn", true, {
expires: new Date(keycloak.refreshTokenParsed.exp * 1000),
});
Expand All @@ -54,16 +55,17 @@ export const movePage = (toPage, navigate) => async (dispatch) => {
navigate(toPage);
};

export const logout = () => async (dispatch, getState) => {
export const clearCachedSession = () => async (dispatch, getState) => {
dispatch({ type: TYPES.LOADING });
const keycloak = getState().apiEndpoint.keycloak;
const keys = ["username", "isLoggedIn"];
for (const key of keys) {
Cookies.remove(key);
}
keycloak.logout();
Cookies.remove("isLoggedIn");
dispatch({ type: TYPES.COMPLETED });
setTimeout(() => {
window.location.href = APP_ROUTES.AUTH;
}, CONSTANTS.LOGOUT_DELAY_MS);
};

export const sessionLogout = () => async (dispatch, getState) => {
const keycloak = getState().apiEndpoint.keycloak;
keycloak.logout();
dispatch(clearCachedSession());
webbnh marked this conversation as resolved.
Show resolved Hide resolved
};
6 changes: 3 additions & 3 deletions dashboard/src/actions/endpointAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export const fetchEndpoints = async (dispatch) => {
payload: data,
});
const keycloak = new Keycloak({
url: data?.openid?.server,
realm: data?.openid?.realm,
clientId: data?.openid?.client,
url: data.openid.server,
realm: data.openid.realm,
clientId: data.openid.client,
MVarshini marked this conversation as resolved.
Show resolved Hide resolved
});
dispatch({
type: TYPES.SET_KEYCLOAK,
Expand Down
4 changes: 2 additions & 2 deletions dashboard/src/actions/toastActions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as TYPES from "./types";
import * as APP_ROUTES from "utils/routeConstants";

import { uid } from "utils/helper";
import { clearCachedSession } from "./authActions";

export const showSessionExpired = () => async (dispatch) => {
const toast = {
Expand All @@ -10,7 +10,7 @@ export const showSessionExpired = () => async (dispatch) => {
message: "Please login to continue",
};
dispatch(showToast(toast.variant, toast.title, toast.message));
window.location.href = APP_ROUTES.AUTH;
dispatch(clearCachedSession());
};

export const showFailureToast = () => async (dispatch) => {
Expand Down
1 change: 0 additions & 1 deletion dashboard/src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export const NAVBAR_CLOSE = "NAVBAR_CLOSE";
export const GET_PUBLIC_DATASETS = "GET_PUBLIC_DATASETS";
export const FAVORITED_DATASETS = "GET_FAVORITE_DATASETS";
export const UPDATE_PUBLIC_DATASETS = "UPDATE_PUBLIC_DATASETS";
export const SET_LOGIN_DETAILS = "SET_LOGIN_DETAILS";

/* DASHBOARD OVERVIEW */
export const USER_RUNS = "USER_RUNS";
Expand Down
4 changes: 2 additions & 2 deletions dashboard/src/modules/components/HeaderComponent/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";

import { logout } from "actions/authActions";
import { sessionLogout } from "actions/authActions";
import pbenchLogo from "assets/logo/pbench_logo.svg";
import { useKeycloak } from "@react-keycloak/web";
import { movePage } from "actions/authActions";
Expand All @@ -50,7 +50,7 @@ const HeaderToolbar = () => {
const type = event.target.name;
const menuOptions = {
profile: () => navigate(APP_ROUTES.USER_PROFILE),
logout: () => dispatch(logout()),
logout: () => dispatch(sessionLogout()),
};
const action = menuOptions[type];
if (action) {
Expand Down
4 changes: 2 additions & 2 deletions dashboard/src/utils/axiosInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ const { dispatch, getState } = store;

const axiosInstance = axios.create({ responseType: "json" });

axiosInstance.interceptors.request.use(async (req) => {
axiosInstance.interceptors.request.use((req) => {
const keycloak = getState().apiEndpoint.keycloak;
if (keycloak?.authenticated) {
req.headers.Authorization = `Bearer ${keycloak?.token}`;
req.headers.Authorization = `Bearer ${keycloak.token}`;
}
return req;
});
Expand Down
31 changes: 0 additions & 31 deletions dashboard/src/utils/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,3 @@ export const uid = () => {

return head + tail;
};

/**
* Determine whether the specified password meets the requirements for a valid password
* Requirements include length, presence of upper-case letters, digits, and special characters
* @function
* @param {string} password - Entered password
* @param {number} passwordLength - Minimum required length of password
* @return {boolean} - true if the password is valid, false otherwise
*/
export const validatePassword = (password, passwordLength) => {
return {
passwordLength: password.length >= passwordLength ? "success" : "error",
passwordSpecialChars: /[`!@#$%^&*()_+\-=\]{};':"\\|,.<>?~]/.test(password)
? "success"
: "error",
passwordContainsNumber: /\d/.test(password) ? "success" : "error",
passwordBlockLetter: /[A-Z]/.test(password) ? "success" : "error",
};
};

/**
* Check if email entered by user is valid or not
* @function
* @param {string} email - Entered email
* @return {boolean} - true if the email is valid, false otherwise
*/
export const validateEmail = (email) => {
return {
email: !/\S+@\S+\.\S+/.test(email) ? "Enter a valid Email" : "",
};
};