rival-black
rival-blackβ€’2y ago

404 after logging in

Why does refine application show a 404 after signing in? I have a custom auth provider(keycloak.js) and this started happening after I injected it into refine application. As a framework I thought refine should not care what that auth provider does, whenever you hit a route like on screenshot refine should take control and redirect you if you're logged in. And it actually happened before when there was no keycloak auth provider. But now this happens. Also, as a matter of fact, I have a custom catchAll page(basically the one you see on screenshot) and it's code will be below. Should this behaviour somehow be moderated in catchAll component?
No description
62 Replies
rival-black
rival-blackβ€’2y ago
The code for catchall component:
import { Info } from '@mui/icons-material';
import { useLogout, useNavigation, usePermissions } from '@pankod/refine-core';
import { Button, Grid, Stack, Tooltip, Typography } from '@pankod/refine-mui';
import StatusCodes from 'http-status-codes';

const ErrorComponent = () => {
const { data: isAdmin } = usePermissions<boolean>();
const { push } = useNavigation();
const { mutate } = useLogout();

const logout = () => mutate();

const goHome = () => push('/');

return (
<Grid display="flex" justifyContent="center" alignItems="center" mt={20}>
<Grid container direction="column" display="flex" alignItems="center">
{isAdmin ? (
<>
<Typography variant="h1">{StatusCodes.NOT_FOUND}</Typography>
<Stack direction="row" spacing="2">
<Typography>
Sorry, the page you visited does not exist.
</Typography>
</Stack>
<Button onClick={goHome}>Back Home</Button>
</>
) : (
<>
<Typography variant="h1">{StatusCodes.FORBIDDEN}</Typography>
<Stack direction="row" spacing="2">
<Typography>
Sorry, you don't have access to this page.
</Typography>

<Tooltip
title={
'Please contact admin panel administrators to grant you access.'
}
>
<Info data-testid="error-component-tooltip" />
</Tooltip>
</Stack>
<Button onClick={logout}>Log out</Button>
</>
)}
</Grid>
</Grid>
);
};

export default ErrorComponent;
import { Info } from '@mui/icons-material';
import { useLogout, useNavigation, usePermissions } from '@pankod/refine-core';
import { Button, Grid, Stack, Tooltip, Typography } from '@pankod/refine-mui';
import StatusCodes from 'http-status-codes';

const ErrorComponent = () => {
const { data: isAdmin } = usePermissions<boolean>();
const { push } = useNavigation();
const { mutate } = useLogout();

const logout = () => mutate();

const goHome = () => push('/');

return (
<Grid display="flex" justifyContent="center" alignItems="center" mt={20}>
<Grid container direction="column" display="flex" alignItems="center">
{isAdmin ? (
<>
<Typography variant="h1">{StatusCodes.NOT_FOUND}</Typography>
<Stack direction="row" spacing="2">
<Typography>
Sorry, the page you visited does not exist.
</Typography>
</Stack>
<Button onClick={goHome}>Back Home</Button>
</>
) : (
<>
<Typography variant="h1">{StatusCodes.FORBIDDEN}</Typography>
<Stack direction="row" spacing="2">
<Typography>
Sorry, you don't have access to this page.
</Typography>

<Tooltip
title={
'Please contact admin panel administrators to grant you access.'
}
>
<Info data-testid="error-component-tooltip" />
</Tooltip>
</Stack>
<Button onClick={logout}>Log out</Button>
</>
)}
</Grid>
</Grid>
);
};

export default ErrorComponent;
Omer
Omerβ€’2y ago
Hey @metammodern , This is not expected behavior. Can you share your AuthProvider?
rival-black
rival-blackβ€’2y ago
Sure, here is authProvider:
import { AuthProvider } from '@pankod/refine-core';
import UserService from 'services/UserService';

/**
* @doc https://refine.dev/docs/api-reference/core/providers/auth-provider/#creating-an-authprovider
*/
export const authProvider: AuthProvider = {
login: () => {
UserService.doLogin();
return Promise.resolve();
},
logout: () => {
UserService.doLogout();
return Promise.resolve();
},

checkAuth: () => {
return UserService.isLoggedIn() ? Promise.resolve() : Promise.reject();
},

getPermissions: () => {
return Promise.resolve(UserService.isAdmin());
},
checkError: (error) => {
if (error.status === 401) {
return Promise.reject();
}
return Promise.resolve();
},
getUserIdentity: () =>
Promise.resolve({
name: UserService.getName(),
avatar: UserService.getPicture(),
}),
};
import { AuthProvider } from '@pankod/refine-core';
import UserService from 'services/UserService';

/**
* @doc https://refine.dev/docs/api-reference/core/providers/auth-provider/#creating-an-authprovider
*/
export const authProvider: AuthProvider = {
login: () => {
UserService.doLogin();
return Promise.resolve();
},
logout: () => {
UserService.doLogout();
return Promise.resolve();
},

checkAuth: () => {
return UserService.isLoggedIn() ? Promise.resolve() : Promise.reject();
},

getPermissions: () => {
return Promise.resolve(UserService.isAdmin());
},
checkError: (error) => {
if (error.status === 401) {
return Promise.reject();
}
return Promise.resolve();
},
getUserIdentity: () =>
Promise.resolve({
name: UserService.getName(),
avatar: UserService.getPicture(),
}),
};
(if you know how to fold code blocks on discord let me know) And here is UserService.ts
/* Source and author: https://github.com/dasniko/keycloak-reactjs-demo/blob/main/src/services/UserService.js */
import Keycloak from 'keycloak-js';

const _kc = new Keycloak({
clientId: import.meta.env.VITE_CLIENT_ID,
url: import.meta.env.VITE_AUTH_URL,
realm: import.meta.env.VITE_REALM,
});

/**
* Initializes Keycloak instance and calls the provided callback function if successfully authenticated.
*/
const initKeycloak = (onAuthenticatedCallback: () => any) => {
_kc
.init({
onLoad: 'check-sso',
checkLoginIframe: false,
})
.then((authenticated) => {
if (!authenticated) {
console.debug('User is not authenticated.');
}
onAuthenticatedCallback();
})
.catch(console.error);
};

const doLogin = () => {
const loginUrl = _kc.createLoginUrl();
location.assign(loginUrl);
};

const doLogout = () => {
const logoutUrl = _kc.createLogoutUrl();
location.assign(logoutUrl);
};

const getToken = () => _kc.token;

const isLoggedIn = () => !!getToken();

const updateTokenOrLogin = async () => {
try {
await _kc.updateToken(5);
} catch (error) {
doLogin();
}
};

const getUsername = () => _kc.tokenParsed?.preferred_username;
const getName = () => _kc.tokenParsed?.name;
const getPicture = () => _kc.tokenParsed?.picture;

const isAdmin = () => _kc.hasResourceRole('admin');

const UserService = {
initKeycloak,
doLogin,
doLogout,
isLoggedIn,
isAdmin,
getToken,
updateTokenOrLogin,
getUsername,
getName,
getPicture,
};

export default UserService;
/* Source and author: https://github.com/dasniko/keycloak-reactjs-demo/blob/main/src/services/UserService.js */
import Keycloak from 'keycloak-js';

const _kc = new Keycloak({
clientId: import.meta.env.VITE_CLIENT_ID,
url: import.meta.env.VITE_AUTH_URL,
realm: import.meta.env.VITE_REALM,
});

/**
* Initializes Keycloak instance and calls the provided callback function if successfully authenticated.
*/
const initKeycloak = (onAuthenticatedCallback: () => any) => {
_kc
.init({
onLoad: 'check-sso',
checkLoginIframe: false,
})
.then((authenticated) => {
if (!authenticated) {
console.debug('User is not authenticated.');
}
onAuthenticatedCallback();
})
.catch(console.error);
};

const doLogin = () => {
const loginUrl = _kc.createLoginUrl();
location.assign(loginUrl);
};

const doLogout = () => {
const logoutUrl = _kc.createLogoutUrl();
location.assign(logoutUrl);
};

const getToken = () => _kc.token;

const isLoggedIn = () => !!getToken();

const updateTokenOrLogin = async () => {
try {
await _kc.updateToken(5);
} catch (error) {
doLogin();
}
};

const getUsername = () => _kc.tokenParsed?.preferred_username;
const getName = () => _kc.tokenParsed?.name;
const getPicture = () => _kc.tokenParsed?.picture;

const isAdmin = () => _kc.hasResourceRole('admin');

const UserService = {
initKeycloak,
doLogin,
doLogout,
isLoggedIn,
isAdmin,
getToken,
updateTokenOrLogin,
getUsername,
getName,
getPicture,
};

export default UserService;
Omer
Omerβ€’2y ago
My guess is that Keycloak is running as async so when run checkAuth it returns Promise.reject for first load. To avoid a similar situation in our Auth0 example, we wait for Auth0 to be init, https://github.com/refinedev/refine/blob/next/examples/auth-auth0/src/App.tsx#L23 Hey @_rassie , I hope you are very well. Maybe you can help us with this πŸ™‚
rival-black
rival-blackβ€’2y ago
@Omer I can log and check)
rival-black
rival-blackβ€’2y ago
@Omer I did it like this. The logs are the ones that ran only after redirect back from keycloak
No description
No description
Omer
Omerβ€’2y ago
How can we reproduce this issue?
Omer
Omerβ€’2y ago
Auth0 Login | refine
Auth0 is a flexible, drop-in solution for adding authentication and authorization services to your applications. Your team and organization can avoid the cost, time, and risk that comes with building your own solution to authenticate and authorize users. You can check the Auth0 document for details.
Google Auth | refine
You can use Google Login to control access and provide identity for your app. This example will guide you through how to connect Google Login into your project using refine.
rival-black
rival-blackβ€’2y ago
Hm, I guess I'll have to create a stackblitz for that, that should not be anyhow related to keycloak...
Omer
Omerβ€’2y ago
Hey @dontpanicaim , I hope you are very well. Maybe you can help us with this πŸ™‚
conventional-tan
conventional-tanβ€’2y ago
does the 404 go away with a manual page reload?
rival-black
rival-blackβ€’2y ago
@dontpanicaim hi, no it doesn't
conventional-tan
conventional-tanβ€’2y ago
@Omer thanks! im doing well! hope you are being awesome as normally too πŸ™‚
Omer
Omerβ€’2y ago
Glad to see you! Are you planning to join the hackathon? ⚑️
conventional-tan
conventional-tanβ€’2y ago
ooh is there a hackathon? @metammodern hmm everything looks pretty good, do you know if getUserIdentity is being called? I've been thinking of replace supabase with Hasura in the project I was working on