I have seen that one could change Jhipster landing page by including this,
registerAuthenticationSuccess() {
this.eventManager.subscribe('authenticationSuccess', (message) => {
this.principal.identity().then((account) => {
if (account.authorities.indexOf('ROLE_CONTRACTOR') >=0)
{
this.router.navigate(['/property']);
}
else
{
this.account = account;
}
});
});
}
in the home.component.ts and call it in the onInit method as,
this.principal.identity().then((account) => {
this.account = account;
});
this.registerAuthenticationSuccess();
However, in my case, this does not work. In my home page I have defined only admin authority to be able to access the home page in route.ts file as below,
export const HOME_ROUTE: Route = { path: '', component: HomeComponent, data: { authorities: ['ROLE_ADMIN'], pageTitle: 'home.title' }, canActivate: [UserRouteAccessService]};
But when I log in as a contractor, I am redirected to the sign-in page saying I do not have permission to go to the home page. It does not get redirected to the property page.
I guess, in Jhipster once we sign in it goes to the HomeComponent by default. In your case it is failing because only the ROLE_ADMIN can access the HomeComponent. So obviously it will not allow a user with ROLE_CONTRACTOR to login, it will get redirected to unauthorized page before calling the method where you have written the code for router navigation.
So try by removing the authorities: ['ROLE_ADMIN'] from the route path, so it will allow all the users to access the HomeComponent and if user is with ROLE_CONTRACTOR it will redirect to property page.
Related
Here is my scenario: After login to the app, on the next browser window opening - the user is already authenticated (using express-session) and it go straight to the content pages (without going through the login page). This behavior continues on the opening of the coming next browsers windows.
Now - the problem - I need to identify whenever the last browser window is closed - so I can clear the session.
I'm looking for some way to check whether the user closed the last (authenticated) browser window.
I assume this can be achieved by session storage, but couldn't figure out how to investigate it.
You should use angular auth guard, the standard way to maintain the authentication at the client-side.
Happy coding ... :)
Add canActivate in routes:
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{
path: 'dashboard',
loadChildren: './master/master.module#MasterModule',
canActivate: [AppRouteGuard]
},
{ path: '**', component: NotFoundComponent }
];
and create a AppRouteGuard Service like this:
export class AppRouteGuard implements CanActivate {
constructor(private router: Router) { }
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | Promise<boolean> | boolean {
if (localStorage.getItem('loginData') != null ) {
return true;
} else {
this.router.navigate(['/login'])
return false;
}
}
}
Give a try with below. I am not 100% sure that it will satisfy all cases, there might be some edge cases that may need to be taken care separately.
Generate a tabId on server side, store it and send it as a header to browser tab.
Tab will read this (in JS) and assign it to a local variable
Tab should send it to server along with each request
When tab is closed, call the server to inform the tabId is closed
If tabId is not found in the request, server should set a new tabId
When last tabId for a given session is closed, logout the session on server
In my routes array in main.js, I have set
meta : { requiresAuth: true }
for every component except on the UserLogin page.
Regarding navigation resolution, I have set a check before each route as follows:
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth().currentUser;
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
const currentUserState = !!currentUser;
console.table({ currentUserState, requiresAuth });
if (requiresAuth && !currentUser) {
next('/login');
} else {
next();
}
}
With this setup, everything almost works as expected/intended. Logged in users can go anywhere, and anonymous users are redirected to localhost/login.
However, this checking and redirecting only functions when users are navigating by clicking the links in the web app (which are all <router-link>s that go :to="{ name: 'SomeComponent' }".
When any user (registered or anonymous) attempts to navigate their way around the app by typing or copy pasting urls, the app will automatically send them to the login page.
From the console.table output, I can see that manually entering urls always results in currentUserState being false.
It does not remain false: a user that has logged in (currentUserState === true), who then manually navigates somewhere (currentUserState === false), has their currentUserState revert back to true
I can only vaguely guess that the problem is related to rendering and firebase.auth().
firebase.initializeApp(config)
// you need to let firebase try and get the auth state before instantiating the
// the Vue component, otherwise the router will be created and you haven't
// resolved whether or not you have a user
firebase.auth().onAuthStateChanged(function(user) {
app = new Vue({
el: '#app',
template: '<App/>',
components: { App },
router
})
});
I have found a solution to this bug.
In my Vuex store.js, a snapshot of the state is stored into localStorage. This snapshot is updated any time there is a change to the store itself, so the localStorage backup is always up-to-date with the actual state of the Vue.js application itself.
store.subscribe((mutation, state) => {
localStorage.setItem('store', JSON.stringify(state));
});
This store.subscribe is set in my store.js just before the export line.
A function called initStore is also defined in the store as follows
initStore(state) {
if (localStorage.getItem('store')) {
this.replaceState(Object.assign(state, JSON.parse(localStorage.getItem('store'))));
}
},
and after my store object, it is immediately called with
store.commit('initStore').
Trying to implement a really simple connection between 2 web application. In this case, I will use Site A and Site B for easy reference.
Site A
Implementing a simple method that opens a webpage and calls a SOAP OR REST webservice by Site B
Sends request with text "Site A request"
Site B Node JS
Receives request from Site B
If request contains text "Site A request", redirects user to page "www.SiteB.com"
If request does not contain required text, redirects user to page "www.google.com"
Will need to pass text "Site A" to Angular to allow www.siteB.com to display text "Welcome Site A"
Is such an implementation possible? If so, how do I perform the redirect from NodeJS?
To make it easier.. i just redirected from the same server to different routes..
app.get('/redirect/:test', (req, res)=>{
if (req.params.test == 'Site B request') {
res.cookie("test", req.params.test, {
maxAge: 1000
});
res.redirect(301, '/autologin/');
}
else {
res.redirect(301, 'http://google.com');
}
});
app.get('/autologin/', (req, res) => {
let cookie = undefined;
if(req.headers['cookie']){
cookie = req.headers['cookie'].substr(5);
}
console.log(cookie);
if(cookie){
res.cookie('test' ,cookie, { maxAge: 9000,expires: 600});
res.redirect(301,'/');
}else{
res.redirect(301, 'http://google.com');
}
})
and as for angular code...
I used ngx-cookie to use as cookie service..
export class AppComponent {
title = 'app';
constructor(
#Inject(PLATFORM_ID) private platformId: Object,
private _cookieService: CookieService
){}
ngOnInit(){
if(isPlatformBrowser(this.platformId)){
let x:any = this._cookieService.getAll();
if(x.test){
this.title = x.test;
}
}
}
}
I have a React/Redux/React Router front end, Node/Express back end. I’m using Passport (various strategies including Facebook, Google and Github) for authentication.
What I want to happen:
Unauthenticated user attempts to access protected client route
(something like /posts/:postid, and is redirected to /login.
(React Router is handling this part)
User clicks the ‘Log in with Facebook’ button (or other Social auth service)
After authentication, user is automatically redirected back to the route they were attempting to access in step 1.
What is happening instead:
The only way I’ve found to successfully handle Passport social authentication with a React front end is to wrap the ‘Log in with Facebook’ button in an <a> tag:
Facebook Login
If I try to do it as an API call instead of a link I always get an error message (this issue is explained in a lot more detail here: Authentication with Passport + Facebook + Express + create-react-app + React-Router + proxy)
So the user clicks the link, which hits the Express API, successfully authenticates with Passport, and then Passport redirects to the callback route (http://localhost:8080/auth/facebook/callback).
In the callback function I need to (1) return the user object and token to the client, and (2) redirect to a client route — either the protected route they were trying to access before they got redirected to /login, or some default route like / or /dashboard.
But since there isn’t a way to do both of these things in Express (I can’t res.send AND res.redirect, I have to choose one), I’ve been handling it in what feels like kind of a clunky way:
res.redirect(`${CLIENT_URL}/user/${userId}`)
This loads the /user route on the client, and then I’m pulling the userId out of the route params, saving it to Redux, then making ANOTHER call to the server to return the token to save token to localStorage.
This is all working, although it feels clunky, but I can’t figure out how to redirect to the protected route the user was trying to access before being prompted to log in.
I first tried saving the attempted route to Redux when the user tries to access it, thinking I could use that to redirect once they land on the profile page after authentication. But since the Passport auth flow takes the user off-site for 3d-party authentication and then reloads the SPA on res.redirect, the store is destroyed and the redirect path is lost.
What I ended up settling on is saving the attempted route to localStorage, checking to see if there is a redirectUrl key in localStorage when the /user component mounts on the front end, redirecting with this.props.history.push(redirectUrl) and then clearing the redirectUrl key from localStorage. This seems like a really dirty workaround and there has got to be a better way to do this. Has anybody else figuree out how to make this work?
In case anybody else is struggling with this, this is what I ended up going with:
1. When user tries to access protected route, redirect to /login with React-Router.
First define a <PrivateRoute> component:
// App.jsx
const PrivateRoute = ({ component: Component, loggedIn, ...rest }) => {
return (
<Route
{...rest}
render={props =>
loggedIn === true ? (
<Component {...rest} {...props} />
) : (
<Redirect
to={{ pathname: "/login", state: { from: props.location } }}
/>
)
}
/>
);
};
Then pass the loggedIn property to the route:
// App.jsx
<PrivateRoute
loggedIn={this.props.appState.loggedIn}
path="/poll/:id"
component={ViewPoll}
/>
2. In /login component, save previous route to localStorage so I can later redirect back there after authentication:
// Login.jsx
componentDidMount() {
const { from } = this.props.location.state || { from: { pathname: "/" } };
const pathname = from.pathname;
window.localStorage.setItem("redirectUrl", pathname);
}
3. In SocialAuth callback, redirect to profile page on client, adding userId and token as route params
// auth.ctrl.js
exports.socialAuthCallback = (req, res) => {
if (req.user.err) {
res.status(401).json({
success: false,
message: `social auth failed: ${req.user.err}`,
error: req.user.err
})
} else {
if (req.user) {
const user = req.user._doc;
const userInfo = helpers.setUserInfo(user);
const token = helpers.generateToken(userInfo);
return res.redirect(`${CLIENT_URL}/user/${userObj._doc._id}/${token}`);
} else {
return res.redirect('/login');
}
}
};
4. In the Profile component on the client, pull the userId and token
out of the route params, immediately remove them using
window.location.replaceState, and save them to localStorage. Then check for a redirectUrl in localStorage. If it exists, redirect and then clear the value
// Profile.jsx
componentWillMount() {
let userId, token, authCallback;
if (this.props.match.params.id) {
userId = this.props.match.params.id;
token = this.props.match.params.token;
authCallback = true;
// if logged in for first time through social auth,
// need to save userId & token to local storage
window.localStorage.setItem("userId", JSON.stringify(userId));
window.localStorage.setItem("authToken", JSON.stringify(token));
this.props.actions.setLoggedIn();
this.props.actions.setSpinner("hide");
// remove id & token from route params after saving to local storage
window.history.replaceState(null, null, `${window.location.origin}/user`);
} else {
console.log("user id not in route params");
// if userId is not in route params
// look in redux store or local storage
userId =
this.props.profile.user._id ||
JSON.parse(window.localStorage.getItem("userId"));
if (window.localStorage.getItem("authToken")) {
token = window.localStorage.getItem("authToken");
} else {
token = this.props.appState.authToken;
}
}
// retrieve user profile & save to app state
this.props.api.getProfile(token, userId).then(result => {
if (result.type === "GET_PROFILE_SUCCESS") {
this.props.actions.setLoggedIn();
if (authCallback) {
// if landing on profile page after social auth callback,
// check for redirect url in local storage
const redirect = window.localStorage.getItem("redirectUrl");
if (redirect) {
// redirect to originally requested page and then clear value
// from local storage
this.props.history.push(redirect);
window.localStorage.setItem("redirectUrl", null);
}
}
}
});
}
This blog post was helpful in figuring things out. The #4 (recommended) solution in the linked post is much simpler and would probably work fine in production, but I couldn't get it to work in development where the server and client have different base URLs, because a value set to localStorage by a page rendered at the server URL will not exist in local Storage for the client URL
Depending on your application architecture, I can give you a couple of ideas, but they are all based on the fundamental :
Once you have backend handling authentication, you need to store the state of the user in your backend as well ( via session cookie / JWT )
You can create a cookie-session store for your express app which cookie, you need to configure properly to use both the domains ( the backend domain and the front-end domain ) or use JWT for this.
Let's go with more details
Use React to check the authentication state
You can implement an end-point in express called /api/credentials/check which will return 403 if the user is not authenticated and 200 if is.
In your react app you will have to call this end-point and check if the user is authenticated or not. In case of not authenticated you can redirect to /login in your React front-end.
I use something similar :
class AuthRoute extends React.Component {
render() {
const isAuthenticated = this.props.user;
const props = assign( {}, this.props );
if ( isAuthenticated ) {
return <Route {...props} />;
} else {
return <Redirect to="/login"/>;
}
}
}
And then in your router
<AuthRoute exact path="/users" component={Users} />
<Route exact path="/login" component={Login} />
In my root component I add
componentDidMount() {
store.dispatch( CredentialsActions.check() );
}
Where CredentialsActions.check is just a call that populates props.user in case we return 200 from /credentials/check.
Use express to render your React app and dehydrate the user state inside the react app
This one is a bit tricky. And it has the presumption that your react app is served from your express app and not as static .html file.
In this case you can add a special <script>const state = { authenticated: true }</script> which will be served by express if the user was authenticated.
By doing this you can do:
const isAuthenticated = window.authenticated;
This is not the best practice, but it's the idea of hydrate and rehydration of your state.
References :
Hydration / rehydration in Redux
Hydrate / rehydrate idea
Example of React / Passport authentication
Example of cookie / Passport authentication
I use Firebase with VueJS. Sign-up, sign-in works fine so far but as soon as I reload the page with F5 or load a page with writing to the browsers address bar directly then the auth session is killed, the user isn't signed-in anymore. It works fine as long as I don't reload the page, even if I click route links or get redirected to other pages, the user session keeps alive. It doesn't matter where I reload the page btw.
My login method:
firebase.auth().signInWithEmailAndPassword(this.username, this.password).then(
(user) => {
if (user.emailVerified === false) {
firebase.auth().signOut()
} else {
// redirect to lobby page if user is verified and signed in
this.$router.push('/lobby')
}
},
function(err) {
console.log(err.message)
}
)
You need to call firebase.auth().onAuthStateChanged in order to detect whether a user is logged in.
This method gets invoked in the UI thread on changes in the authentication state:
Right after the listener has been registered
When a user is signed in
When the current user is signed out
When the current user changes
Source
Example usage can be like this:
firebase.auth().onAuthStateChanged(user => {
if (user) {
if (user.emailVerified === false) {
firebase.auth().signOut()
} else {
this.$router.push('/lobby')
}
} else {
// will get to here from a logout event
// this.$router.push('/login')
}
}
Use this in your main.js file:
firebase.auth().onAuthStateChanged(() => {
if (!app) {
app = createApp(App).use(store).use(router).mount('#app')
}})
Firebase will run before Vue app is created.