Fix login and logout flow for MCS (#185)

fixes: https://github.com/minio/mcs/issues/184

There was a bug in Safari in related to the browser not setting the session token
correctly in localstorage, this was because we were using
window.location.href for redirect instead of history.push after login, the redirect execution was faster
was faster that the promise function getting the response after the login request
and it seems to be that Safari will kill all current request of a
window when the page is getting redirected.

Test this:

Try to sign-in using Safari browser (latest version is recommended)
This commit is contained in:
Lenin Alevski
2020-06-29 20:58:56 -07:00
committed by GitHub
parent 1e7f272a67
commit 59a5c9dbf0
5 changed files with 45 additions and 20 deletions

View File

@@ -17,6 +17,7 @@
import storage from "local-storage-fallback";
import request from "superagent";
import get from "lodash/get";
import { clearSession } from "../utils";
export class API {
invoke(method: string, url: string, data?: object) {
@@ -28,8 +29,11 @@ export class API {
.catch((err) => {
// if we get unauthorized, kick out the user
if (err.status === 401) {
storage.removeItem("token");
window.location.href = "/";
clearSession();
// Refresh the whole page to ensure cache is clear
// and we dont end on an infinite loop
window.location.href = "/login";
return;
}
return this.onError(err);
});
@@ -48,7 +52,8 @@ export class API {
return Promise.reject(throwMessage);
} else {
return Promise.reject("Unknown error");
clearSession();
window.location.href = "/login";
}
}
}

View File

@@ -14,6 +14,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import storage from "local-storage-fallback";
export const units = [
"B",
"KiB",
@@ -50,6 +52,20 @@ export const setCookie = (name: string, val: string) => {
name + "=" + value + "; expires=" + date.toUTCString() + "; path=/";
};
export const deleteCookie = (name: string) => {
document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT;";
};
export const setSession = (token: string) => {
setCookie("token", token);
storage.setItem("token", token);
};
export const clearSession = () => {
storage.removeItem("token");
deleteCookie("token");
};
// timeFromdate gets time string from date input
export const timeFromDate = (d: Date) => {
let h = d.getHours() < 10 ? `0${d.getHours()}` : `${d.getHours()}`;

View File

@@ -68,6 +68,7 @@ import ListTenants from "./Tenants/ListTenants/ListTenants";
import { ISessionResponse } from "./types";
import { saveSessionResponse } from "./actions";
import TenantDetails from "./Tenants/TenantDetails/TenantDetails";
import { clearSession } from "../../common/utils";
function Copyright() {
return (
@@ -206,9 +207,12 @@ const Console = ({
.then((res) => {
saveSessionResponse(res);
})
.catch((err) => {
storage.removeItem("token");
history.push("/");
.catch(() => {
// if server returns 401 for /api/v1/session call invoke function will internally call clearSession()
// and redirecto to window.location.href = "/"; and this code will be not reached
// in case that not happen we clear session here and redirect as well
clearSession();
window.location.href = "/login";
});
}, [saveSessionResponse]);

View File

@@ -50,6 +50,7 @@ import {
UsersIcon,
WarpIcon,
} from "../../../icons";
import { clearSession } from "../../../common/utils";
const styles = (theme: Theme) =>
createStyles({
@@ -156,9 +157,9 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
const logout = () => {
const deleteSession = () => {
storage.removeItem("token");
clearSession();
userLoggedIn(false);
history.push("/");
history.push("/login");
};
api
.invoke("POST", `/api/v1/logout`)

View File

@@ -28,7 +28,8 @@ import { SystemState } from "../../types";
import { userLoggedIn } from "../../actions";
import api from "../../common/api";
import { ILoginDetails, loginStrategyType } from "./types";
import { setCookie } from "../../common/utils";
import { setSession } from "../../common/utils";
import history from "../../history";
const styles = (theme: Theme) =>
createStyles({
@@ -120,13 +121,13 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
});
const loginStrategyEndpoints: LoginStrategyRoutes = {
"form": "/api/v1/login",
form: "/api/v1/login",
"service-account": "/api/v1/login/mkube",
}
};
const loginStrategyPayload: LoginStrategyPayload = {
"form": { accessKey, secretKey },
form: { accessKey, secretKey },
"service-account": { jwt },
}
};
const fetchConfiguration = () => {
setLoading(true);
@@ -147,15 +148,15 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
const formSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
request
.post(loginStrategyEndpoints[loginStrategy.loginStrategy] || "/api/v1/login")
.post(
loginStrategyEndpoints[loginStrategy.loginStrategy] || "/api/v1/login"
)
.send(loginStrategyPayload[loginStrategy.loginStrategy])
.then((res: any) => {
const bodyResponse = res.body;
if (bodyResponse.sessionId) {
// store the jwt token
setCookie("token", bodyResponse.sessionId);
storage.setItem("token", bodyResponse.sessionId);
//return res.body.sessionId;
setSession(bodyResponse.sessionId);
} else if (bodyResponse.error) {
// throw will be moved to catch block once bad login returns 403
throw bodyResponse.error;
@@ -164,9 +165,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
.then(() => {
// We set the state in redux
userLoggedIn(true);
// There is a browser cache issue if we change the policy associated to an account and then logout and history.push("/") after login
// therefore after login we need to use window.location redirect
window.location.href = "/";
history.push("/");
})
.catch((err) => {
setError(err.message);