mirror of
https://github.com/outline/outline.git
synced 2026-01-06 11:09:55 -06:00
fix: OIDC logout redirect unreliable (#9508)
This commit is contained in:
@@ -20,7 +20,6 @@ import SearchQuery from "~/models/SearchQuery";
|
||||
import KeyboardShortcuts from "~/scenes/KeyboardShortcuts";
|
||||
import { createAction } from "~/actions";
|
||||
import { NavigationSection, RecentSearchesSection } from "~/actions/sections";
|
||||
import env from "~/env";
|
||||
import Desktop from "~/utils/Desktop";
|
||||
import history from "~/utils/history";
|
||||
import isCloudHosted from "~/utils/isCloudHosted";
|
||||
@@ -231,12 +230,7 @@ export const logout = createAction({
|
||||
section: NavigationSection,
|
||||
icon: <LogoutIcon />,
|
||||
perform: async () => {
|
||||
await stores.auth.logout();
|
||||
if (env.OIDC_LOGOUT_URI) {
|
||||
setTimeout(() => {
|
||||
window.location.replace(env.OIDC_LOGOUT_URI);
|
||||
}, 200);
|
||||
}
|
||||
await stores.auth.logout({ userInitiated: true });
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -31,7 +31,12 @@ const Authenticated = ({ children }: Props) => {
|
||||
return <LoadingIndicator />;
|
||||
}
|
||||
|
||||
void auth.logout(true);
|
||||
void auth.logout({ savePath: true });
|
||||
|
||||
if (auth.logoutRedirectUri) {
|
||||
window.location.href = auth.logoutRedirectUri;
|
||||
return null;
|
||||
}
|
||||
return <Redirect to="/" />;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,13 +6,7 @@ import { logoutPath } from "~/utils/routeHelpers";
|
||||
const Logout = () => {
|
||||
const { auth } = useStores();
|
||||
|
||||
void auth.logout().then(() => {
|
||||
if (env.OIDC_LOGOUT_URI) {
|
||||
setTimeout(() => {
|
||||
window.location.replace(env.OIDC_LOGOUT_URI);
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
void auth.logout({ userInitiated: true });
|
||||
|
||||
if (env.OIDC_LOGOUT_URI) {
|
||||
return null; // user will be redirected to logout URI after logout
|
||||
|
||||
@@ -48,7 +48,11 @@ function TeamDelete({ onSubmit }: Props) {
|
||||
async (data: FormData) => {
|
||||
try {
|
||||
await auth.deleteTeam(data);
|
||||
await auth.logout();
|
||||
await auth.logout({
|
||||
savePath: false,
|
||||
revokeToken: false,
|
||||
userInitiated: true,
|
||||
});
|
||||
onSubmit();
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
|
||||
@@ -47,7 +47,11 @@ function UserDelete({ onSubmit }: Props) {
|
||||
async (data: FormData) => {
|
||||
try {
|
||||
await auth.deleteUser(data);
|
||||
await auth.logout();
|
||||
await auth.logout({
|
||||
savePath: false,
|
||||
revokeToken: false,
|
||||
userInitiated: true,
|
||||
});
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
|
||||
@@ -50,6 +50,10 @@ export default class AuthStore extends Store<Team> {
|
||||
@observable
|
||||
public collaborationToken?: string | null;
|
||||
|
||||
/* When set, the user will be redirected to this URL after logging out. */
|
||||
@observable
|
||||
public logoutRedirectUri?: string;
|
||||
|
||||
/* A list of teams that the current user has access to. */
|
||||
@observable
|
||||
public availableTeams?: {
|
||||
@@ -109,7 +113,11 @@ export default class AuthStore extends Store<Team> {
|
||||
// we are signed in and the received data contains no user then sign out
|
||||
if (this.authenticated) {
|
||||
if (isNil(newData.user)) {
|
||||
void this.logout(false, false);
|
||||
void this.logout({
|
||||
savePath: false,
|
||||
revokeToken: false,
|
||||
userInitiated: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.rehydrate(newData);
|
||||
@@ -298,18 +306,26 @@ export default class AuthStore extends Store<Team> {
|
||||
* Logs the user out and optionally revokes the authentication token.
|
||||
*
|
||||
* @param savePath Whether the current path should be saved and returned to after login.
|
||||
* @param tryRevokingToken Whether the auth token should attempt to be revoked, this should be
|
||||
* @param revokeToken Whether the auth token should attempt to be revoked, this should be
|
||||
* disabled with requests from ApiClient to prevent infinite loops.
|
||||
*/
|
||||
@action
|
||||
logout = async (savePath = false, tryRevokingToken = true) => {
|
||||
logout = async ({
|
||||
savePath = false,
|
||||
revokeToken = true,
|
||||
userInitiated = false,
|
||||
}: {
|
||||
savePath?: boolean;
|
||||
revokeToken?: boolean;
|
||||
userInitiated?: boolean;
|
||||
}) => {
|
||||
// if this logout was forced from an authenticated route then
|
||||
// save the current path so we can go back there once signed in
|
||||
if (savePath) {
|
||||
setPostLoginPath(window.location.pathname + window.location.search);
|
||||
}
|
||||
|
||||
if (tryRevokingToken) {
|
||||
if (revokeToken) {
|
||||
try {
|
||||
// invalidate authentication token on server and unset auth cookie
|
||||
await client.post(`/auth.delete`);
|
||||
@@ -329,6 +345,10 @@ export default class AuthStore extends Store<Team> {
|
||||
});
|
||||
}
|
||||
|
||||
if (userInitiated) {
|
||||
this.logoutRedirectUri = env.OIDC_LOGOUT_URI;
|
||||
}
|
||||
|
||||
// clear all credentials from cache (and local storage via autorun)
|
||||
this.currentUserId = null;
|
||||
this.currentTeamId = null;
|
||||
|
||||
@@ -153,7 +153,10 @@ class ApiClient {
|
||||
|
||||
// Handle 401, log out user
|
||||
if (response.status === 401) {
|
||||
await stores.auth.logout(true, false);
|
||||
await stores.auth.logout({
|
||||
savePath: true,
|
||||
revokeToken: false,
|
||||
});
|
||||
throw new AuthorizationError();
|
||||
}
|
||||
|
||||
@@ -201,7 +204,10 @@ class ApiClient {
|
||||
|
||||
if (response.status === 403) {
|
||||
if (error.error === "user_suspended") {
|
||||
await stores.auth.logout(false, false);
|
||||
await stores.auth.logout({
|
||||
savePath: false,
|
||||
revokeToken: false,
|
||||
});
|
||||
}
|
||||
|
||||
throw new AuthorizationError(error.message);
|
||||
|
||||
Reference in New Issue
Block a user