I'm making a registration page, and if the user is logged in I need to send the user object from the app.vue file to every page in the pages/ directory. Here's the app.vue:
<script setup>
import { ref } from 'vue';
const userId = useCookie('user');
const userObj = await $fetch('/api/user/getLoggedInUser', {
method: 'post',
body: {
id: userId.value
}
});
const user = ref(userObj);
</script>
<template>
<div>
<Header />
<NuxtLayout>
<NuxtPage :user="user.value" />
</NuxtLayout>
</div>
</template>
How would I get the user in /pages/index.vue, or any of the files in that directory?
When I need to handle user authentication I usually like to use #vueuse useLocalStorage, which is basically a wrapper for reactive local storage, so the user stays logged in between sessions. You need to:
Authenticate the user
Save the user object/response in the localStorage (your userObj)
Access it wherever you need
Example:
// useUser composable
export const useUser = () => {
const user = useLocalStorage("user", null); // by default is not logged in, so null or some other value you can check
const login = async() => {
const userObj = await ...;
user.value = userObj; // here you set the user in the storage
}
return {
user,
login
}
}
Then where you need to auth the user:
const { login } = useUser();
await login();
And where you need to access the user object:
const { user } = useUser();
const someData = user.someValue;
The user object will persist until you manually empty the localStorage, or until you create a logout function which clears the storage key and logs out the user.
Related
In a React Native app, I have a Login component that stores the userId in the app Context.
After loging-in, the user goes to the Dashboard component where the userId is accessed by using the useContext hook. The userId is required to fetch posts from the database.
I use the useEffect hook to call the fetching function when the component mounts.
The probelm is that when the useEffect executes and the function is called, the userId is undefined. However, if I re render the component then I can see, by the logs, that the id is now defined but because the useEffect has executed I do not get the data from the server.
I have tried to solve the issue using the setTimeOut() method in the useEffect hook but again the fetch takes place before the id is defined.
What is most strange to me is that, in the Dashboard component, I follow the same steps to access a Token from the context and this Token is immediately available.
Below, I show you my code and the logs of the app:
This is the Context file
//AuthContext.tsx
import { createContext, useState } from "react";
import { AuthUser } from "../types/AuthUser";
type AuthContextProviderProps = {
children: React.ReactNode
}
type AuthContextType= {
isAuthenticated : boolean
setIsAuthenticated : React.Dispatch<React.SetStateAction<boolean>>
token : string
setToken : React.Dispatch<React.SetStateAction<string>>
userId : string
setUserId : React.Dispatch<React.SetStateAction<string>>
userName : string
setUserName : React.Dispatch<React.SetStateAction<string>>
}
export const AuthContext = createContext({} as AuthContextType)
export const AuthContextProvider = ({children}: AuthContextProviderProps)=>{
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false)
const [token, setToken] = useState<string>('')
const [userId, setUserId] = useState<string>('')
const [userName, setUserName] = useState<string>('')
//const [authUser, setAuthUser] = useState<AuthUser>({userId:'', userName:''})
return(
<AuthContext.Provider value={{isAuthenticated, setIsAuthenticated,token, setToken, userId,setUserId,userName,setUserName}}>
{children}
</AuthContext.Provider>
)
}
In the Login screen I store userId and token in the Context
const loginUser = async(): Promise<ISuccessFullResponse | boolean>=>{
try {
const body = {email, password}
const url = REMOTE_SERVER+'/authentication/loginUser'
console.log(url)
const response = await fetch(url,{
method : 'POST',
headers:{'content-type':'application/json'},
body : JSON.stringify(body)
})
console.log(loggedUser)
const id = loggedUser.data.user._id
const name = loggedUser.data.user.userName
setToken(loggedUser.data.token)
setUserId(id)
setUserName(name)
return loggedUser
} catch (error) {
console.log(error)
}
return false
}
}
After login, there is a redirection to the Dashboard.
const Dashboard : React.FC <DashScreenProps>= (props)=>{
//interfaces
interface IPost{
contentString : string
urls : string[]
}
//global state
const authContext = useContext(AuthContext)
const token = authContext.token
console.log('token in dashboard', token)
const id = authContext.userId
console.log('id in dashboard', id)
const profilePicContext = useContext(ProfilePicContext)
const setProfilePic = profilePicContext.setProfilePic
//local state
const [arrayOfPosts, setArrayOfPosts] = useState<IPost[]>([])
const [status, setStatus] = useState<string>('')
const [errMsg, setErrMsg] = useState<string>("")
//props
const navigation = props.navigation
//function definitions
const getProfilePicture = async():Promise<boolean>=>{
try {
const response = await fetch(REMOTE_SERVER+'/dashboard/downloadProfilePicture', {
headers : {token}
})
const parseResponse = await response.json()
if(parseResponse.data){
setProfilePic(parseResponse.data)
}
} catch (error) {
console.log(error)
if(error instanceof Error){
console.log('this is the error',error)
return false
}
}
return true
}
const getPostsFromFollowingUsers = async():Promise<boolean>=>{
setStatus('LOADING')
try {
console.log('inside function id', id)
const response = await fetch(REMOTE_SERVER+`/userPost/getPostsFromFollowingUsers/${id}`, {
method : 'GET',
headers : {'Content-Type': 'application/json'}
})
const parseResponse = await response.text()
console.log('this is the parseresponse',parseResponse)
const responseObject = JSON.parse(parseResponse)
if(responseObject.data){
setArrayOfPosts(responseObject.data)
}
} catch (error) {
console.log('this is the error',error)
setStatus('ERROR')
if(error instanceof Error){
setErrMsg(error.message)
}
}
return true
}
useEffect(()=>{
getProfilePicture()
}, [])
useEffect(()=>{
getPostsFromFollowingUsers()
}, [])
return(
As you can see, the constant token was stored and accessed in the same way as the userId, yet the token is accessible in the dashboard and is successfully used in getProfilePicture().
On the other hand, userId, at first is undefined and is becomes defined after getPostFromFollowingUsers() is called.
Here are the logs:
token in dashboard eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjJkMTg1YjI2NTliZDEzMjlhNDVjMzBkIiwiaWF0IjoxNjczODAzMDQ5LCJleHAiOjE2NzM4MzkwNDl9.HdJn0YAfi-yM9wPZMek6zmw4SUE2TsR_AzUOK06MeBg
LOG id in dashboard
LOG name in dashboard
LOG inside function id
LOG token in dashboard eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjJkMTg1YjI2NTliZDEzMjlhNDVjMzBkIiwiaWF0IjoxNjczODAzMDQ5LCJleHAiOjE2NzM4MzkwNDl9.HdJn0YAfi-yM9wPZMek6zmw4SUE2TsR_AzUOK06MeBg
LOG id in dashboard 62d185b2659bd1329a45c30d
LOG name in dashboard
LOG token in dashboard eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjJkMTg1YjI2NTliZDEzMjlhNDVjMzBkIiwiaWF0IjoxNjczODAzMDQ5LCJleHAiOjE2NzM4MzkwNDl9.HdJn0YAfi-yM9wPZMek6zmw4SUE2TsR_AzUOK06MeBg
LOG id in dashboard 62d185b2659bd1329a45c30d
LOG name in dashboard Joaquin
LOG token in dashboard eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjJkMTg1YjI2NTliZDEzMjlhNDVjMzBkIiwiaWF0IjoxNjczODAzMDQ5LCJleHAiOjE2NzM4MzkwNDl9.HdJn0YAfi-yM9wPZMek6zmw4SUE2TsR_AzUOK06MeBg
LOG id in dashboard 62d185b2659bd1329a45c30d
LOG name in dashboard Joaquin
LOG this is the parseresponse <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /userPost/getPostsFromFollowingUsers/</pre>
</body>
</html>
LOG this is the error [SyntaxError: JSON Parse error: Unrecognized token '<']
Naturally, the route needed a parameter to work and that is why I get this error. Now id I type a space in Visual Studio Code useEffect is called again and now the userId is defined so the fetch is performed and the data from the server is retrieved. If someone shared an idea on how to solve this issue, I would be grateful.
add a loading state to your component and set it true when you are fetching the data and then set it to false when the data is fetched, show an activity loader when it is loading and this might solve your issue.
const { userId } = useContext(AppContext);
useEffect(() => {
if (userId) {
fetchData(userId).then(data => {
setIsLoading(false);
});
}
}, [userId]);
I'm discovering Nuxt 3 since a few days and I'm trying to do a JWT authentication to a distinct API.
As #nuxtjs/auth-next doesn't seem to be up to date and as I read it was possible to use the new global method fetch in Nuxt 3 instead of #nuxtjs/axios (not up to date also), I thought it won't be too hard to code the authentication myself! But it stays a mystery to me and I only found documentation on Vue project (using Pinia to keep user logged in) and I'm a bit at a lost.
What I would like to achieve:
a login page with email and password, login request send to API (edit: done!)
get JWT token and user info from API (edit: done!) and store both (to keep user logged even if a page is refresh)
set the JWT token globally to header $fetch requests (?) so I don't have to add it to each request
don't allow access to other pages if user is not logged in
Then I reckon I'll have to tackle the refresh token subject, but one step at a time!
It will be really awesome to have some help on this, I'm not a beginner but neither a senior and authentication stuff still frightens me :D
Here is my login.vue page (I'll have to use Vuetify and vee-validate after that but again one step at a time!)
// pages/login.vue
<script setup lang="ts">
import { useAuthStore } from "~/store/auth";
const authStore = useAuthStore();
interface loginForm {
email: string;
password: string;
}
let loginForm: loginForm = {
email: "",
password: "",
};
function login() {
authStore.login(loginForm);
}
</script>
<template>
<v-container>
<form #submit.prevent="login">
<label>E-mail</label>
<input v-model="loginForm.email" required type="email" />
<label>Password</label>
<input v-model="loginForm.password" required type="password" />
<button type="submit">Login</button>
</form>
</v-container>
</template>
The store/auth.ts for now.
// store/auth.ts
import { defineStore } from 'pinia'
import { encodeURL } from '~~/services/utils/functions'
export const useAuthStore = defineStore({
id: 'auth,
state: () => ({
// TODO Initialize state from local storage to enable user to stay logged in
user: '',
token: '',
})
actions: {
async login(loginForm) {
const URL_ENCODED_FORM = encodeURL({
email: loginForm.email,
password: loginForm.password,
});
return await $fetch('api_route', {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
method: 'POST',
body: URL_ENCODED_FORM
}
}
}
})
i'm gonna share everything, even the parts you marked as done, for completeness sake.
Firstly, you will need something to generate a JWT in the backend, you can do that plainly without any packages, but i would recommend this package for that. Also i'll use objection.js for querying the database, should be easy to understand even if you don't know objection.js
Your login view needs to send a request for the login attempt like this
const token = await $fetch('/api/login', {
method: 'post',
body: {
username: this.username,
password: this.password,
},
});
in my case it requests login.post.ts in /server/api/
import jwt from 'jsonwebtoken';
import { User } from '../models';
export default defineEventHandler(async (event) => {
const body = await useBody(event);
const { id } = await User.query().findOne('username', body.username);
const token: string = await jwt.sign({ id }, 'mysecrettoken');
return token;
});
For the sake of simplicity i didn't query for a password here, this depends on how you generate a user password.
'mysecrettoken' is a token that your users should never get to know, because they could login as everybody else. of course this string can be any string you want, the longer the better.
now your user gets a token as the response, should just be a simple string. i'll write later on how to save this one for future requests.
To make authenticated requests with this token you will need to do requests like this:
$fetch('/api/getauthuser', {
method: 'post',
headers: {
authentication: myJsonWebToken,
},
});
i prefer to add a middleware for accessing the authenticated user in my api endpoints easier. this middleware is named setAuth.ts and is inside the server/middleware folder. it looks like this:
import jwt from 'jsonwebtoken';
export default defineEventHandler(async (event) => {
if (event.req.headers.authentication) {
event.context.auth = { id: await jwt.verify(event.req.headers.authentication, 'mysecrettoken').id };
}
});
What this does is verify that if an authentication header was passed, it checks if the token is valid (with the same secret token you signed the jwt with) and if it is valid, add the userId to the request context for easier endpoint access.
now, in my server/api/getauthuser.ts endpoint in can get the auth user like this
import { User } from '../models';
export default defineEventHandler(async (event) => {
return await User.query().findById(event.context.auth.id)
});
since users can't set the requests context, you can be sure your middleware set this auth.id
you have your basic authentication now.
The token we generated has unlimited lifetime, this might not be a good idea. if this token gets exposed to other people, they have your login indefinitely, explaining further would be out of the scope of this answer tho.
you can save your auth token in the localStorage to access it again on the next pageload. some people consider this a bad practice and prefer cookies to store this. i'll keep it simple and use the localStorage tho.
now for the part that users shouldnt access pages other than login: i set a global middleware in middleware/auth.global.ts (you can also do one that isnt global and specify it for specific pages)
auth.global.ts looks like this:
import { useAuthStore } from '../stores';
export default defineNuxtRouteMiddleware(async (to) => {
const authStore = useAuthStore();
if (to.name !== 'Login' && !localStorage.getItem('auth-token')) {
return navigateTo('/login');
} else if (to.name !== 'Login' && !authStore.user) {
authStore.setAuthUser(await $fetch('/api/getauthuser', {
headers: authHeader,
}));
}
});
I'm using pinia to store the auth user in my authStore, but only if the localstorage has an auth-token (jwt) in it. if it has one and it hasnt been fetched yet, fetch the auth user through the getauthuser endpoint. if it doesnt have an authtoken and the page is not the login page, redirect the user to it
With the help of #Nais_One I managed to do a manual authentication to a third-party API with Nuxt 3 app using client-side rendering (ssr: false, target: 'static' in nuxt.config.ts)
I still have to set the API URL somewhere else and to handle JWT token refresh but the authentication works, as well as getting data from a protected API route with the token in header and redirection when user is not logged.
Here are my finals files:
// pages/login.vue
<script setup lang="ts">
import { useAuthStore } from "~/store/auth";
const authStore = useAuthStore();
const router = useRouter();
interface loginForm {
email: string;
password: string;
}
let loginForm: loginForm = {
email: "",
password: "",
};
/**
* If success: redirect to home page
* Else display alert error
*/
function login() {
authStore
.login(loginForm)
.then((_response) => router.push("/"))
.catch((error) => console.log("API error", error));
}
</script>
<template>
<v-container>
<form #submit.prevent="login">
<label>E-mail</label>
<input v-model="loginForm.email" required type="email" />
<label>Password</label>
<input v-model="loginForm.password" required type="password" />
<button type="submit">Login</button>
</form>
</v-container>
</template>
For the auth store:
// store/auth.ts
import { defineStore } from 'pinia'
const baseUrl = 'API_URL'
export const useAuthStore = defineStore({
id: 'auth',
state: () => ({
/* Initialize state from local storage to enable user to stay logged in */
user: JSON.parse(localStorage.getItem('user')),
token: JSON.parse(localStorage.getItem('token')),
}),
actions: {
async login(loginForm) {
await $fetch(`${baseUrl}/login`, {
method: 'POST',
body: loginForm
})
.then(response => {
/* Update Pinia state */
this.user = response
this.token = this.user.jwt_token
/* Store user in local storage to keep them logged in between page refreshes */
localStorage.setItem('user', JSON.stringify(this.user))
localStorage.setItem('token', JSON.stringify(this.token))
})
.catch(error => { throw error })
},
logout() {
this.user = null
this.token = null
localStorage.removeItem('user')
localStorage.removeItem('token')
}
}
})
I also use the middleware/auth.global.ts proposed by Nais_One.
And this fetch-wrapper exemple I found here as well to avoid having to add token to every requests: https://jasonwatmore.com/post/2022/05/26/vue-3-pinia-jwt-authentication-tutorial-example and it seems to work perfectly. (I just didn't test yet the handleResponse() method).
Hope it can help others :)
That temporary alternative https://www.npmjs.com/package/#nuxtjs-alt/auth is up to date
And that https://www.npmjs.com/package/nuxtjs-custom-auth and https://www.npmjs.com/package/nuxtjs-custom-http work with Nuxt 3 $fetch and no need to use axios
Recently a new package was released that wraps NextAuth for Nuxt3. This means that it already supports many providers out of the box and may be a good alternative to look into.
You can install it via:
npm i -D #sidebase/nuxt-auth
Then it is pretty simple to add to your projects as you only need to include the module:
export default defineNuxtConfig({
modules: ['#sidebase/nuxt-auth'],
})
And configure at least one provider (like this example with Github):
import GithubProvider from 'next-auth/providers/github'
export default defineNuxtConfig({
modules: ['#sidebase/nuxt-auth'],
auth: {
nextAuth: {
options: {
providers: [GithubProvider({ clientId: 'enter-your-client-id-here', clientSecret: 'enter-your-client-secret-here' })]
}
}
}
})
Afterwards you can then get access to all the user data and signin/signup functions!
If you want to have a look at how this package can be used in a "real world" example, look at the demo repo in which it has been fully integrated:
https://github.com/sidebase/nuxt-auth-example
I hope this package may be of help to you and others!
Stumbling on the same issue for a personal project and what I do is declare a composable importing my authStore which is basically a wrapper over $fetch
Still a newb on Nuxt3 and Vue but it seems to work fine on development, still have to try and deploy it though
import { useAuthStore } from "../store/useAuthStore";
export const authFetch = (url: string, opts?: any | undefined | null) => {
const { jwt } = useAuthStore();
return $fetch(url, {
...(opts ? opts : {}),
headers: {
Authorization:`Bearer ${jwt}`,
},
});
};
And then I can just use it in my actions or components
// #/store/myStore.ts
export const useMyStore = defineStore('myStore', () => {
async getSomething() {
...
return authFetch('/api/something')
}
})
// #components/myComponent.vue
...
<script setup lang="ts">
const handleSomething = () => {
...
authFetch('/api/something')
}
</script>
Hope it helps someone !
im trying to use nuxt 3, pinia and firebase on auth transaction in my application
everything is working fine about onAuthStateChange except when I refresh a page. in this case im getting a kind of delay until load the current user data from firebase and set it with pinia store.
At this time i have a store, where i run onAuthStateChange. if has user, I set the data on state, and return this store as a Promisse
currentUser() {
const auth = getAuth()
return new Promise((resolve, reject) => {
onAuthStateChanged(auth, (user) => {
if (user) {
console.log("user", user)
resolve(user)
this.setUser(user)
} else {
console.log("no user")
resolve(undefined)
}
})
})
},
and, I have a plugin, called init, where i call this store.
export default defineNuxtPlugin((nuxtApp) => {
const store = useAuthStore()
store.currentUser()
})
my question is. how can i make my application wait for this transaction before loading all?
just one example of what this cause, is where i using a getter to show or not a login form. the form is appearing in begining, and then desappear if user is loggedin.
<form-signin v-if="!store.loggedin" />
<div v-else>already logged as {{ store.userData.email }}</div>
I'm currently building a blog sample app, using NextJS, ApolloClient and MongoDB + MongoRealm. The NextJS skeleton was built after the framework's official page tutorial.
At the moment, new users can signup, by accessing a SignUp form which is routed at 'pages/signup'. After entering their credentials, they are redirected to the home page. Then, the freshly signed in users have to visit another page(the one associated with 'pages/login' root), which contains the login form, which is responsible with their email/password authentication.
Also, I've set up Realm to send a confirmation email at the user's email address. The email contains a link to a customized page from my NextJs app, which will handle their confirmation(users also have to be confirmed, after requesting a sign in)
The workflow should be established with this. However, I want to automatically login a user, after he/she just logged in(so that they won't need to sign in and also visit the log in page, when creating their accounts).
The problem I'm encountering is that my React component that handles the user confirmation, doesn't have access to the user instance's email and password. I need a way to login the user, without having access to his/her credentials.
Below, I will try to explain exactly why this access restriction happens in the first place. Although the entire '_app.js' is wrapped in some custom providers, I'll try to keep things as simple as possible, so I'll present only what is needed for this topic.
My signup.js file looks something like this:
import { useForm } from "react-hook-form";
// Used 'useForm' hook to simplify data extraction from the //input form
import { useAuth } from "members";
const SignUpForm = () => {
const router = useRouter();
const { handleSubmit, register } = useForm();
const { signup } = useAuth();
const signUpAndRedirect = (form) => {
signup(form.email, form.password);
router.push("/");
// after signing up, redirect client back to home
};
return (
{/*My form's 'email' and 'password' fields are only accessible in the SignUpForm component*/}
<div>
<form onSubmit={handleSubmit(signUpAndRedirect)}>
...
...
</form>
</div>
);
};
export default SignUpForm;
My login.js file is built after the same concept, the only difference being that 'signUpAndRedirect' is replaced with
'authenticateAndRedirect':
const authenticateAndRedirect = (form) => {
login(form.email, form.password);
router.push("/");
};
And here is my confirm.js file, which is responsible with extracting the token and tokenId from the confirmation URL. This component is normally only rendered when the client receives the email and clicks on the confirmation link(which basically has the form /confirm, where each token is a string and is added into the URL by Realm).
import Link from "next/link";
import { useEffect } from "react";
import { useRouter } from "next/router";
import { useAuth } from "members";
const Confirm = () => {
const router = useRouter();
const { confirm, login } = useAuth();
useEffect(() => {
const token = router.query.token;
const tokenId = router.query.tokenId;
if (token && tokenId) {
confirm(token, tokenId);
login(email, password); // !!! I don't have access to these
}
}, [router]);
//used useEffect() to assure the confirmation only happens once, after the component was rendered.
return (
<div>
<h2>
Thank you for confirming your email. Your profile was successfully
activated.
</h2>
<Link href="/">
<a>Go back to home</a>
</Link>
</div>
);
};
export default Confirm;
And finally, just a quick look into the signup, login and confirm methods that I have access to through my customized providers. I am quite positive that they work correctly:
const client = () => {
const { app, credentials } = useRealm();
const [currentUser, setCurrentUser] = useState(app.currentUser || false);
const [isAuthenticated, setIsAuthenticated] = useState(user ? true : false);
// Login and logout using email/password.
const login = async (email, password) => {
try {
const userCredentials = await credentials(email, password);
await app.logIn(userCredentials);
setCurrentUser(app.currentUser);
setIsAuthenticated(true);
} catch (e) {
throw e;
}
};
const logout = async () => {
try {
setUser(null);
// Sign out from Realm and Auth0.
await app.currentUser?.logOut();
// Update the user object.
setCurrentUser(app.currentUser);
setIsAuthenticated(false);
setUser(false);
} catch (e) {
throw e;
}
};
const signup = async (email, password) => {
try {
await app.emailPasswordAuth.registerUser(email, password);
// await app.emailPasswordAuth.resendConfirmation(email);
} catch (e) {
throw e;
}
};
const confirm = async (token, tokenId) => {
try {
await app.emailPasswordAuth.confirmUser(token, tokenId);
} catch (e) {
throw e;
}
};
return {
currentUser,
login,
logout,
signup,
confirm,
};
};
export default client;
The currentUser will basically represent the Realm.app.currentUser and will be provided to the _app by my providers.
So, the problem is that my Confirm component doesn't have access to the email and password fields.
I've tried to use the useContext hook, to pass data between sibling components, but quickly abandoned this approach, because I don't want to pass sensitive data throughout my NextJS pages(The only place where I should use the password is during the MongoDB POST request, since it gets encrypted by Realm Web).
Is there any way I could solve this issue? Maybe an entirely different approach?
Thank you very much in advance! Any help would be very much appreciated!
If you disable the user email confirmation, you could potentially call the login function when the register is finished like that :
registerAndLogin(email, password)
.then(() =>
loginAndRedirect(email, password)
.then(() => router.push('/')
.catch(err => throw err)
)
.catch(err => throw err)
I used your post to resolve an error I had, so thank you by the way.
Hope my answer works, I didn't had the time to test.
I have created a custom Login page in a new react-admin app and intend to login. How do i access the userLogin action from the store.
I tried wrapping the root component in a provider but can't access the store.
Currently using the login example
class MyLoginPage extends Component {
submit = (e) => {
e.preventDefault();
// gather your data/credentials here
const credentials = { };
// Dispatch the userLogin action (injected by connect)
this.props.userLogin(credentials);
}
render() {
return (
<MuiThemeProvider theme={this.props.theme}>
<form onSubmit={this.submit}>
...
</form>
</MuiThemeProvider>
);
}
};
export default connect(undefined, { userLogin })(MyLoginPage);
I expect to retrieve the userLogin action from react-admin and push to the redux store