i am required to make Register And Login process by connecting Ionic 4 to MySQL, the Login and Registration works, but i am having problem on configuring the guard to enable access based on user currently logged in, below is my code at tabs.router.module.ts which is the route for first loaded page after user logged in
{
path: 'profile-qr',
children: [
{
path: '',
loadChildren: () =>
import('../profile-qr/profile-qr.module').then(m => m.ProfileQRPageModule),
canActivate: [AuthGuard],
data: {
role: 'C'
}
}
]
},
below is my code to store login data in local storage during login process login.page.ts
async prosesLogin(){
if(this.loginForm.value.email != "" && this.loginForm.value.password != ""){
let body = {
email: this.loginForm.value.email,
password: this.loginForm.value.password,
action: 'login'
};
this.postPvdr.postData(body, 'user-api.php').subscribe(async data =>{
if(data.success){
this.storage.set('session_storage', data.result);
below is my code at AuthGuard auth.guard.ts
canActivate(route: ActivatedRouteSnapshot){
const expectedRole = route.data.role;
this.storage.get('session_storage').then((res)=>{
if (res == null){
this.router.navigate([`/home`]);
return false;
}else{
if (expectedRole == res.role) {
return true;
} else {
this.router.navigateByUrl('/login');
return false;
}
}
})
return false;
}
during login as customer#email.com which have a role of 'C' which is customer, i am unable to access any page which have set canActivate : [AuthGuard]
i have tried to copy other code style, which use Observable and Firebase, but i am unable to do so with MySQL because i do not really understand how to write Observable object.
i have checked on the mysql database and the user role are inserted correctly, there are no problem appear on my VSCode,
the problem are only at RBAC
kindly guide and explain on how to write code at auth.guard.ts
Related
I had did this using Redux library and MongoDB and its works fine with this but now i am doing same thing with mysql so its not working well. This logic always redirect all users to admin dashboard. i want do like, if i do isAdmin="true" it will redirect to admin dashboard and stop to going coordinator dashboard. and if isCoordinator="true" then redirect to coordinator dashboard and not able to access admin dashboard. how can i that?
*This is my Admin.js file. where i did logic to private access path.
import { useSelector } from "react-redux";
import { Route, Routes, Link, useNavigate } from "react-router-dom";
import UsersList from "./UsersList";
import UsersEntry from "./UsersEntry";
import Projects from "./projects/Projects";
import "./Admin.css"
export default function Admin() {
let navigate = useNavigate();
const userState = useSelector(state=> state.loginUserReducer)
const {currentUser} =userState;
useEffect(() => {
// This code check Role of user who logged in and if not coordinator then restrict(stop) to going on this private page. By getItem and check === !currentUser.isCoordinator.
if(localStorage.getItem('currentUser') === null || !currentUser.isAdmin){
navigate('/coordinators');
if(localStorage.getItem('currentUser') === null || !currentUser.isCoordinator){
navigate('/');
}
}
}, [])
*This is UserAction.js fie.
export const loginUser = (user) => async (dispatch) => {
dispatch({type: "USER_LOGIN_REQUEST"});
try {
const response = await axios.post("http://localhost:4000/login",user);
console.log(response.data);
dispatch({type:"USER_LOGIN_SUCCESS", payload: response.data});
localStorage.setItem('currentUser',JSON.stringify(response.data));
window.location.href = "/admin";
} catch (error) {
dispatch({type: "USER_LOGIN_FAIL", payload: error})
}
}
*This is UserReducer.js.
switch (action.type){
case "USER_LOGIN_REQUEST":
return{
loading:true,
};
case "USER_LOGIN_SUCCESS":
return{
success: true,
loading: false,
currentusers: action.payload,
};
case "USER_LOGIN_FAIL":
return{
error: action.payload,
loading:false,
};
default:
return state;
}
};
*This is Store.js.
import thunk from 'redux-thunk';
import {composeWithDevTools} from 'redux-devtools-extension';
import {loginUserReducer} from './UserReducer';
const currentUser = localStorage.getItem('currentUser') ? JSON.parse(localStorage.getItem('currentUser')) : null
const rootReducer = combineReducers({loginUserReducer : loginUserReducer});
const initialState = {
loginUserReducer: {
currentUser : currentUser
}
}
const middleware = [thunk]
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
In your useEffect inside the dependency array pass the currentUser so whenever it is changed useEffect will be triggered. In current scenario when the array is empty it just happens on the page load the very first time.
Here is the possible thing to try:
useEffect(() => {
// This code check Role of user who logged in and if not coordinator then restrict(stop) to going on this private page. By getItem and check === !currentUser.isCoordinator.
if(localStorage.getItem('currentUser') === null || !currentUser.isAdmin){
navigate('/coordinators');
if(localStorage.getItem('currentUser') === null || !currentUser.isCoordinator){
navigate('/');
}
}
}, [currentUser]) <================
Secondly also check your nested if condition. You can use an else if as well so incase its not admin it will go inside if and if it is admin then it can go inside the else if block. But that is totally up to you, just a suggestion.
I've been trying to make a very basic app pulling user information from a .json file and "logging them in", and have a json storing whether a user is logged into the app and their user ID. I'm stuck on a method which would take the given email and password, match them to an entry in the users json, then update the login json with the new information (login true, and user ID.) This is what I have so far in the method:
setUserLogIn(email, password):any{
if (this.users){
this.users.forEach(foundUser => {
if (foundUser.email === email && foundUser.password === password){
this.currentUser=foundUser;
let login:Login = {"id": 1, "loginStatus":true, "userId":foundUser.id}
return this.httpService.put<Observable<any>>('http://localhost:7800/loginCheck/1', login)
.pipe(map((log:Observable<Login>) =>{
console.log(log) //this isn't reached, never prints in console
if (log !== undefined){
return true;
}
return false;
}))
}
if (this.currentUser != null){
FetchUserService.isLoggedIn = true;
} else{
FetchUserService.isLoggedIn = false;
}
})
}
}
From my previous tests I know everything else in the method works correctly, just the put only returns undefined. I am subscribing to the method in the controller:
this.fetchUserService.setUserLogIn(this.userEmail, this.userPassword).subscribe(data => {
console.log(data);
})
This method of subscription returns an error. I also tried subscribing in the service itself, like:
return this.httpService.put<Observable<any>>('http://localhost:7800/loginCheck/1', login)
.pipe(map((log:Observable<Login>) =>{
console.log(log)
if (log !== undefined){
log.subscribe(data => {
return data
})
Taking this into the component and logging the result also just returns undefined.
Any suggestions? I have no idea what I'm doing wrong, after searching put methods for the past few hours I can't see any differences in what I have there. Any help is greatly appreciated!
There are multiple issues here.
Parallel subscriptions. Avoid them if possible. Here you could use forkJoin to combine all observables and trigger them in parallel.
Why would an HTTP request emit an Observable as it's response? Most probably it wouldn't.
Currently you aren't returning anything from the function.
Try the following
setUserLogIn (email, password): Observable<any> { // <-- return `Observable` here
if (!this.users) return NEVER;
return forkJoin(
this.users.map(foundUser => {
if (foundUser.email === email && foundUser.password === password) {
this.currentUser = foundUser;
FetchUserService.isLoggedIn = true;
let login: Login = {
"id": 1,
"loginStatus": true,
"userId": foundUser.id
};
return this.httpService.put('http://localhost:7800/loginCheck/1', login).pipe(
map((log: Login) => { // why would an HTTP request emit an observable?
console.log(log);
return (!!log);
})
);
}
FetchUserService.isLoggedIn = false;
return EMPTY; // `forkJoin` emits only when all observables complete
})
);
}
I have a react admin application with a customRoutes, as well as resources (say /users). One of my custom route is a private page (say /private), which I protect with the useAuthenticated() hook:
export default () => {
useAuthenticated();
return (<Card>
<Title title="Private Page" />
<CardContent>
This is a private page.
</CardContent>
</Card>)
}
When I browse this private page and I'm not authenticated, I'm entering an authentication process as it should be (I'm using OIDC). This process is triggered by the checkAuth() method of the authProvider. But when the process is completed, I'm redirected to the /users resource and not the /private page. Is there a way to tell the authProvider that I want to be redirected to the private page?
Thanks - C
I have not done this myself, but I imagine you can use your redirect path as an argument in the useAuthenticated() call. https://marmelab.com/react-admin/Authentication.html#useauthenticated-hook
export default () => {
useAuthenticated({ redirectPath: '/privatepage' });
return (
<Card>
<Title title="Private Page" />
<CardContent>
This is a private page.
</CardContent>
</Card>
)
}
From there you should be able to use that arg/parameter in your checkAuth method.
tl;dr In your login().then() aspect, do a redirect('/myDefaultUrl')
I had the same challenge as you, I think. After looking into the ReactAdmin code a bit I was unable to find a way to do it in any 'official' way. I did see something quite interesting in the useAuthProvider() hook.
The AuthProvider object maintained by RA seems to have a variable with a couple properties initialized with some defaults.
import { useContext } from 'react';
import { AuthProvider } from '../types';
import AuthContext from './AuthContext';
export const defaultAuthParams = {
loginUrl: '/login',
afterLoginUrl: '/',
};
/**
* Get the authProvider stored in the context
*/
const useAuthProvider = (): AuthProvider => useContext(AuthContext);
export default useAuthProvider;
Specifically the afterLoginUrl property looked interesting. I attempted to override that property in my authProvider but didn't have any luck.
What I ended up doing was this. In my invocation of the login (from useLogin()), when the authProvider.login resolves, I return back the user along with their profile (I use Cognito so it is a CognitoUser instance). In my user I have configured an attribute for where the user should go after login. I then simply use the redirect hook (from useRedirect) to then direct the user to that URL.
Here is my login from my AuthProvider:
const authProvider = {
login: (params) => {
if (params instanceof CognitoUser) {
return params;
}
if (params.error) {
return Promise.reject(params.error);
}
return new Promise((resolve, reject) => {
Auth.signIn(params.username,params.password).then(cognitoUser => {
if (cognitoUser.challengeName === 'NEW_PASSWORD_REQUIRED') {
reject({ code: 'NewPasswordRequired', cognitoUser: cognitoUser })
} else {
setAndStoreUserProfile(cognitoUser);
resolve(cognitoUser);
}
}, function(err) {
reject(err);
});
});
},
....
Then in my Login form, I do this:
raLogin(formData)
.then((cognitoUser) => {
console.log("User logged in. Result:", cognitoUser);
clearTransitionState();
redirect('/profile');
})
.catch(error => {
if (error.code && error.code === 'PasswordResetRequiredException') {
transition(PHASE.RESET_ACCOUNT, {username: formData.username });
} else if (error.code && error.code === 'NewPasswordRequired') {
transition(PHASE.NEW_PASSWORD, { cognitoUser: error.cognitoUser });
} else {
processAuthError(error);
}
});
Context: I am trying to get Google Maps place data via the place_id on the beforeEnter() route guard. Essentially, I want the data to load when someone enters the url exactly www.example.com/place/{place_id}. Currently, everything works directly when I use my autocomplete input and then enter the route but it does not work when I directly access the url from a fresh tab. I've been able to solve this using the beforeEnter() route guard in traditional Vue, but cannot solve for this using Nuxt. Please help!
Question: How can I access the Vuex Store before a page loads in Nuxt?
Error: Any solution I try (see below) I either end up with a blank page or the page will not load (I think it is stuck in a loop and cannot resolve the Promise).
Attempted Solutions:
Using Middleware like below:
middleware({ store, params }) {
return store.dispatch('myModule/fetchLocation', params.id)
}
Using asyncData like below:
data(){
return{
filteredLocation: {}
}
}
// snip
async asyncData({ store, params }) {
const { data } = await store.dispatch('myModule/fetchLocation', params.id)
return filteredLocation = data
}
I tried looking into fetch, but apparently you no longer have access to context
Example Code:
In one of my store modules:
/* global google */
import Vue from 'vue'
import * as VueGoogleMaps from '~/node_modules/vue2-google-maps/src/main'
Vue.use(VueGoogleMaps, {
load: {
key: process.env.VUE_APP_GMAP_KEY,
libraries: 'geometry,drawing,places'
}
})
export const state = () => ({
selectedLocation: {}
})
export const actions = {
fetchLocation({ commit }, params) {
return new Promise((resolve) => {
Vue.$gmapApiPromiseLazy().then(() => {
const request = {
placeId: params,
fields: [
'name',
'rating',
'formatted_phone_number',
'geometry',
'place_id',
'website',
'review',
'user_ratings_total',
'photo',
'vicinity',
'price_level'
]
}
const service = new google.maps.places.PlacesService(
document.createElement('div')
)
service.getDetails(request, function(place, status) {
if (status === 'OK') {
commit('SET_PLACE', place)
resolve()
}
})
})
})
}
}
export const mutations = {
SET_PLACE: (state, selection) => {
state.selectedInstructor = selection
}
}
EDIT: I already have it in a plugin named google-maps.js and in my nuxt.config.js file I have:
plugins: [
{ src: '~/plugins/google-maps.js' }
]
//
//
build: {
transpile: [/^vue2-google-maps.js($|\/)/],
extend(config, ctx) {}
}
Using Middleware is how we can access Vuex before page loads. try putting the configuration part in a custom Nuxt plugin.
Create a file in Plugins folder (you can name it global.js).
Put this
import Vue from 'vue'
import * as VueGoogleMaps from '~/node_modules/vue2-google-maps/src/main'
Vue.use(VueGoogleMaps, {
load: {
key: process.env.VUE_APP_GMAP_KEY,
libraries: 'geometry,drawing,places'
}
})
in global.js.
Then add the plugin in nuxt.config.js like this.
plugins: [
'~/plugins/global.js'
]
Also, make sure you're using underscore before 'page_id' name in your folder structure.
I created a simple login page using extjs MVC to understand MVC architecture of extjs. As you can see below, I am trying to get the json data into the store and then I will check each username and password in that data with the entered login credentials. The thing in which I am confused right now is that, how to check the username and password from the retrieved json data present in store folder into the view folder? (Below code is only the related code with the problem)
I aware that this could invoke security threats, as I am checking on client side.
'view' folder --> Code.js
function checkJson(username, password){
//if matched, return true.
//else, return false.
}
'model' folder --> Code.js
Ext.define('AM.model.User', {
extend: 'Ext.data.Model',
fields: ['name', 'email']
});
'store' folder --> Code.js
Ext.define('LoginPage.store.Code', {
extend: 'Ext.data.Store',
model: 'LoginPage.model.Code',
autoLoad: true,
proxy: {
type: 'ajax',
api: {
read: 'data/loginResponse.json',
update: 'data/checkCredentials.json' //Contains: {"success": true}
},
reader: {
type: 'json',
root: 'loginResponse',
successProperty: 'success'
}
}
});
loginResponse.json
{
"form": {
"login": [
{
"username": "venkat",
"password": "123"
},
{
"username": "admin",
"password": "345"
}
]
}
You should put your checking part of the code to the Controller (views are for presentation). In view define some form with login and password fields. In Controller catch click event on form OK (Login) button, get form values (login + password), then use Ext.data.Store.query() method to find wether credentials fits or not like:
Look here for examples how to use controllers in MVC to catch events;
In your Controller put:
init: function() {
this.control({
'#form_ok_button': { // this is the `id` property of your form's Login button
click: function(button) {
var fValues = button.up('form').getValues(); // Assume your button is bound to the form
// Or you can use `Controller.refs` property (http://docs.sencha.com/ext-js/4-1/#!/api/Ext.app.Controller-cfg-refs) to get form
var matched = store.query('username', fValues.username);
if(matched.length && matched[0].get('password') === fValues.password) {
// login OK!
}
}
}
});
},
How to use refs (in Controller):
refs: [
{ ref: 'usernameField', selector: '#username_field' }, // username field id is "username_field"
{ ref: 'passwordField', selector: '#password_field' }, // password field id is "password_field"
],
init: function() {
this.control({
'#form_ok_button': {
click: function() {
// with `refs` autogetters are created for every `ref`:
var username_field = this.getUsernameField();
var password_field = this.getPasswordField();
}
}
})
}
You can read about referencing here.
For every Store in Ext.app.Controller.stores array autogetters are created too (in your case for Code store use this.getCodeStore() inside controller).
Here is the flow:
You get username and password field values with this.getUsernameField() and this.getPasswordField();
You query() store for username
If username exist in store, you check if password fits.