Migrating Auth Provider from 3.x.x to 4.x.x
Motivation behind the changes
Our motivation for modifying the authProvider
prop in refine v4 is to enhance its flexibility and customization options to handle a wider range of use cases without over complicating the process.
We wanted to create a common interface for the authProvider
methods to improve its transparency for developers for better understanding and debugging.
Previously, developers were expected to resolve the authProvider
methods on success and reject them on failure. However, this approach had limitations, as rejected promises are generally associated with errors or unusual scenarios. Since some errors, such as incorrect login credentials, aren't necessarily failures and must be communicated to the user, rejected promises often led to confusion for developers and hindered the debugging process.
Now, the authProvider
methods always return a resolved promise that includes an object with a success key. This key indicates whether the operation was successful or not, and in case of failure, an optional error key containing an Error
object can be used to notify users.
Furthermore, the auth hooks no longer have default redirection paths, which had previously been a source of confusion for some developers. Instead, by adding a redirectTo
property to the authProvider
methods' return object, developers can have more control over the redirection after a successful operation.
Naming changes
AuthPovider
interface changed toAuthBindings
.- import { AuthProvider } from "@refinedev/core";
+ import { AuthBindings } from "@refinedev/core";
- const authProvider: AuthProvider = {/* ... */}
+ const authProvider: AuthBindings = {/* ... */}getUserIdentity
method of theauthProvider
changed togetIdentity
.checkError
method of theauthProvider
changed toonError
.checkAuth
method of theauthProvider
changed tocheck
.const authProvider = {
- getUserIdentity,
+ getIdentity,
- useCheckError,
+ useOnError,
- checkAuth,
+ check,
}useAuthenticated
hook changed touseIsAuthenticated
.- import { useAuthenticated } from "@refinedev/core";
+ import { useIsAuthenticated } from "@refinedev/core";
Methods
login
login
method now requires promises to be resolved in all cases, with a return type ofAuthActionResponse
login
method expects promises to be resolved in all cases. You should always resolve the promise with asuccess
key and an additionalerror
key in case of a failure.useLogin
no longer has default redirection. You will need to addredirectTo
property to thelogin
method's return object.
const authProvider = {
login: async ({ email, password }) => {
const user = mockUsers.find((item) => item.email === email);
if (user) {
localStorage.setItem("auth", JSON.stringify(user));
- return Promise.resolve();
+ return {
+ success: true,
+ redirectTo: "/",
+ };
}
- return Promise.reject();
+ return {
+ success: false,
+ error: new Error("Invalid email or password"),
+ };
},
}
type AuthActionResponse = {
success: boolean;
redirectTo?: string;
error?: Error;
[key: string]: unknown;
};
logout
logout
method now requires promises to be resolved in all cases, with a return type ofAuthActionResponse
logout
method expects promises to be resolved in all cases. You should always resolve the promise with asuccess
key and an additionalerror
key in case of a failure.useLogout
no longer has default redirection. You will need to addredirectTo
property to thelogout
method's return object.
const authProvider = {
logout: async ({ email, password }) => {
localStorage.removeItem("auth");
- return Promise.resolve();
+ return {
+ success: true,
+ redirectTo: "/login",
+ };
},
}
type AuthActionResponse = {
success: boolean;
redirectTo?: string;
error?: Error;
[key: string]: unknown;
};
register
register
method now requires promises to be resolved in all cases, with a return type ofAuthActionResponse
register
method expects promises to be resolved in all cases. You should always resolve the promise with asuccess
key and an additionalerror
key in case of a failure.useRegister
no longer has default redirection. You will need to addredirectTo
property to theregister
method's return object.
const authProvider = {
register: async ({ email, password }) => {
const user = mockUsers.find((item) => item.email === email);
if (user) {
- return Promise.reject();
+ return {
+ success: false,
+ error: {
+ name: "Register Error"",
+ message: "User already exists",
+ },
+ };
}
mockUsers.push({ email });
- return Promise.resolve();
+ return {
+ success: true,
+ redirectTo: "/",
+ };
},
}
forgotPassword
forgotPassword
method now requires promises to be resolved in all cases, with a return type ofAuthActionResponse
forgotPassword
method expects promises to be resolved in all cases. You should always resolve the promise with asuccess
key and an additionalerror
key in case of a failure.useForgotPassword
no longer has default redirection. You will need to addredirectTo
property to theforgotPassword
method's return object.
const authProvider = {
forgotPassword: async ({ password }) => {
// send password reset link to the user's email address here
// if request is successful
- return Promise.resolve();
+ return {
+ success: true,
+ redirectTo: "/login",
+ };
// if request is not successful
- return Promise.reject();
+ return {
+ success: false,
+ error: {
+ name: "Forgot Password Error",
+ message: "Email address does not exist",
+ },
+ };
},
};
type AuthActionResponse = {
success: boolean;
redirectTo?: string;
error?: Error;
[key: string]: unknown;
};
updatePassword
updatePassword
method now requires promises to be resolved in all cases, with a return type ofAuthActionResponse
updatePassword
method expects promises to be resolved in all cases. You should always resolve the promise with asuccess
key and an additionalerror
key in case of a failure.useUpdatePassword
no longer has default redirection. You will need to addredirectTo
property to theupdatePassword
method's return object.
const authProvider = {
updatePassword: async ({ password }) => {
// update the user's password here
// if request is successful
- return Promise.resolve();
+ return {
+ success: true,
+ redirectTo: "/login",
+ });
// if request is not successful
- return Promise.reject();
+ return {
+ success: false,
+ error: {
+ name: "Forgot Password Error",
+ message: "Email address does not exist",
+ },
+ };
},
};
type AuthActionResponse = {
success: boolean;
redirectTo?: string;
error?: Error;
[key: string]: unknown;
};
check
checkAuth
method of the authProvider changed tocheck
.check
method now requires promises to be resolved in all cases, with a return type ofCheckResponse
check
method expects promises to be resolved in all cases. You should always resolve the promise with asuccess
key and an additionalerror
key in case of a failure.<Authenticated/>
component no longer has default redirection. You will need to addredirectTo
property to thecheck
method's return object.<Authenticated/>
component no longer calllogout
method by default. You will need to addlogout
property totrue
to thecheck
method's return object.
const authProvider = {
- checkAuth: async () => {
+ check: async () => {
const user = localStorage.getItem("auth");
if (user) {
- return Promise.resolve();
+ return {
+ authenticated: true,
+ };
}
- return Promise.reject();
+ return {
+ authenticated: false,
+ redirectTo: "/login",
+ logout: true,
+ error: new Error("User not found"),
+ };
},
};
type CheckResponse = {
authenticated: boolean;
redirectTo?: string;
logout?: boolean;
error?: Error;
};
onError
authProvider
'scheckError
method renamed toonError
.onError
method now requires promises to be resolved in all cases, with a return type ofOnErrorResponse
onError
method expects promises to be resolved in all cases. You should always resolve the promise with asuccess
key and an additionalerror
key in case of a failure.useOnError
no longer has default redirection. You will need to addredirectTo
property to theonError
method's return object.useOnError
component no longer calllogout
method by default. You will need to addlogout
property totrue
to thecheck
method's return object.
const authProvider = {
- checkError: async (error) => {
+ onError: async (error) => {
if (error.status === 401 || error.status === 403) {
- return Promise.reject();
+ return {
+ redirectTo: "/login",
+ logout: true,
+ error: error,
+ };
}
- return Promise.reject();
+ return {};
},
};
type OnErrorResponse = {
redirectTo?: string;
logout?: boolean;
error?: Error;
};
getPermissions
getPermissions
method now requires promises to be resolved in all cases, with a return type ofPermissionResponse
const authProvider = {
getPermissions: async () => {
const user = localStorage.getItem("auth");
if (user) {
const { roles } = JSON.parse(user);
return roles;
}
- return Promise.reject();
+ return null;
},
};
type PermissionResponse = unknown;
getIdentity
authProvider
'sgetUserIdentity
method renamed togetIdentity
.getIdentity
method now requires promises to be resolved in all cases, with a return type ofIdentityResponse
const authProvider: AuthProvider = {
- getUserIdentity: async () => {
+ getIdentity: async () => {
const user = localStorage.getItem("auth");
if (user) {
const { email, roles } = JSON.parse(user);
return Promise.resolve({ email, roles });
}
- return Promise.reject();
+ return null;
},
};
type IdentityResponse = unknown;
refine still supports the authProvider@v3
for backward compatibility. We changed name to legacyAuthProvider
and it will be removed in the next major version. If you want to continue using the authProvider@v3
you can use it as legacyAuthProvider
in your project.
- import { AuthProvider } from "@refinedev/core";
+ import { LegacyAuthProvider } from "@refinedev/core";
- const authProvider: AuthProvider = {/* ... */}
+ const authProvider: LegacyAuthProvider = {/* ... */}
const App = () => {
return (
<Refine
- authProvider={authProvider}
+ legacyAuthProvider={authProvider}
>
<AppLayout />
</Refine>
);
};
Also you need to add v3LegacyAuthProviderCompatible: true
to your auth hooks to continue using the authProvider@v3
in your project.
import { useLogin } from "@refinedev/core";
const login = useLogin({
v3LegacyAuthProviderCompatible: true,
});