Nuxt.js custom role middleware doesn't work when page refresh - vue.js

I have a simple application with some pages that need to be protected if the connected user is not an administrator.
I use the nuxt/auth package to handle the authentication:
auth: {
strategies: {
local: {
scopeKey: 'roles',
token: {
property: 'access_token',
type: 'Bearer',
name: 'Authorization',
},
user: {
property: 'user',
},
endpoints: {
login: {url: '/api/auth/login', method: 'post'},
// logout: { url: '/api/auth/logout', method: 'post' },
user: {url: '/api/auth/me', method: 'get'}
}
},
},
redirect: {
login: '/',
logout: '/',
callback: '/housing',
home: '/home'
},
plugins: [
'~/plugins/auth.js',
]
},
This works well but I have trouble achieving my middleware.
So what I want is to redirect the users to the home page if they don't have the role ROLE_ADMIN
export default function ({app, $auth, $axios}) {
if (!$auth.user.roles.includes('ROLE_ADMIN')) {
console.log("unauthorized");
app.router.push(app.localePath('home'));
}
}
And I use the middleware on the page I want.
It works perfectly when for example the user is logged and goes to the administration page without refreshing the page.
But if they go and refresh the page or use the direct URL, instantly after being redirected to the home page by my middleware, nuxt/auth redirect again my user to the "unauthorized" page.
Why this behavior?

Sorry for the bad answer last time. Here is a new one.
In the case of a hard refresh or moving to an URL from the search bar (same thing), you lose all your state and your app is loaded from scratch once again. At this point, you will have all of your middlewares executed again.
Here is a quote from this documentation page
The middleware will be executed in series in this order:
nuxt.config.js (in the order within the file)
Matched layouts
Matched pages
So, you'll have your auth (#nuxt/auth) middleware executed once again, then you will have your own custom one executed. Now, if you do not persist the info of the user (the one successfully logged in before the hard refresh), the auth module will have no clue of who you are and hence prompt you for a login once again.
So, nothing abnormal here so far.
Here is a link to an answer on how to persist data through a hard refresh: https://stackoverflow.com/a/66872372/8816585

The simple answer is: disable the redirect for auth/nuxt login and handle it on your own
redirect: {
login: false,
logout: '/',
callback: '/housing',
home: '/home'
},
If you don't disable it, it always is going to redirect the page to home after login

Related

Nuxt auth with keycloak: ssr this.$auth.loggedIn always false on page load

I have a setup with nuxt and keycloak as auth strategy which in general is working. I can login via keycloak and then will have this.$auth.loggedIn === true on the page. When navigating via vue-router, this.$auth.loggedIn will also be true when switching to a new page.
But when I then reload the page (CMD+r/F5), server side rendering will have false for this.$auth.loggedIn, while on client side it will be true. This forced me to do a lot of <client-only> blocks in the templates to prevent ssr mismatches.
I wonder if it is possible that on first page load server side rendering can return a page with authorized content? I would think this should be possible since cookies with auth info are set and sent to the server.
Or is that never possible and efficient server side rendering can only be used for non-authorized content?
Versions:
nuxt: 2.15.8
#nuxtjs/auth-next: 5.0.0-1643791578.532b3d6
nuxt.config.js:
auth: {
strategies: {
keycloak: {
scheme: 'oauth2',
endpoints: {
authorization: `${ process.env.KEYCLOAK }/protocol/openid-connect/auth`,
userInfo: `${ process.env.KEYCLOAK }/protocol/openid-connect/userinfo`,
token: `${ process.env.KEYCLOAK }/protocol/openid-connect/token`,
logout: `${ process.env.KEYCLOAK }/protocol/openid-connect/logout`,
},
token: {
property: 'access_token',
type: 'Bearer',
maxAge: 1800,
},
refreshToken: {
property: 'refresh_token',
maxAge: 60 * 60 * 24 * 30,
},
responseType: 'code',
grantType: 'authorization_code',
clientId: process.env.CLIENT_ID,
scope: ['openid', 'profile', 'email', 'roles'],
codeChallengeMethod: 'S256',
redirect: {
logout: '/',
callback: '/',
home: '/',
},
},
},
},
Having a Vue component with this:
created() {
console.log(this.$auth.loggedIn);
},
Will return false for SSR and true on client side on page load/refresh when logged in.
After manually implementing a server side authenticator, I found out that the problem was my local docker setup.
Didn't think this was the problem before, so I forgot to mention it.
I have a local docker container with keycloak and a local docker container with nuxt.
Long story short, it seems that the nuxt server wasn't able to communicate with keycloak, hence wasn't able to fetch the user. After changing some addresses so that keycloak was available on the same address from the browser and from within my nuxt server docker container, the nuxt server did get $auth.loggedIn=true automatically on page load if the is was logged in.
Not sure if I didn't see it, but I wished nuxt auth would give me an error if the nuxt server failed to communicate with the authorization server. Would have saved me a lot of debugging.

Nuxt Auth Module - How to create Success Redirect

I'm trying to a page in my nuxt app, where the user is on a dynamic route. On this route he has the possibility to log in and should then be redirected to exactly the same route. For the authentification I want to use the auth0 strategy.
I thought the following line would work:
this.$auth.loginWith('auth0')
.then(() => {
this.$router.push("/the-same-route-im-already-on");
})
But apperently I'm only redirected to my home route "/loggedin". And changing the home redirect in my nuxt.config to false, redirects me to my callback-route "/login"
Right now my nuxt.config.js looks like this:
auth: {
auth0: {
...
},
redirect: {
login: '/',
logout: '/',
callback: '/login',
home: '/loggedin',
}
}
I think the default behavior of rewriteRedirect in nuxt Auth Module, should redirect you to the original guarded route.
rewriteRedirect
If this is not working maybe you could redirect the user to login route manually passing a query like this
this.$router.push({ path: '/login', redirect: 'actualRoute' });
And then check in your login page if redirect is setted and then after login you call $router passing the value of redirect.
I don't know if this will help you, so let me know.

Handling routing in Vue with Strip Connect's redirect link

Flow: backend sends o-auth link to Vue, user clicks on link which directs to stripe's form, after completion of the form it directs user to a URL I have specified.
I have a vue client app that I want to use stripe connect with. From my backend app, I am sending a [o-auth link][1] where it will let users fill out their Stripe information and upon completion, it will redirect me to a URL I specify in Stripe settings(http://localhost:8080/test and this is valid in test mode).
The problem I am facing is that even though the link is correct, vue won't redirect to test page and stay on the current page. test page renders when I redirect it via <router-view></router-view> in the template. I do have my router configured properly so I don't why it doesn't go to the redirect link and render that page.
my router setup:
routes: [
{
path: '/',
name: 'mainContent',
component: MainContent
},
{
path: '/test',
name: 'test',
component: test
},
{
path: '/manage',
name: 'manage',
component: Manage,
meta: {
requiresAuth: true,
}
},
]
})
[1]: https://stripe.com/docs/connect/express-accounts

vue-auth - Redirection after login to inital request

I'm using the last version of vue-js and vue-auth whose documentation could be find here
By default vue-auth would redirect the user to /login if auth is true and if the user is not logged in.
So if I type http://example.com/mycomponent I will be redirect to /login.
Issue
After being loged, I'm redirect to /
Questions
What can I do so after being logged one is redirected to his initial request ?
If I type /mycomponent I'd like to be redirect to /mycomponent after login
Routes
{
path: '/mycomponent',
name: 'mycomponent',
component: mycomponent,
meta: { auth: true }
}
As stated on the documentation, you can call this.$auth.redirect(); on your login method to detect if the user was redirected to the login page.
You can then add the redirect option with a path.
var redirectObj = this.$auth.redirect();
this.$auth.login({
redirect: {
path: redirectObj ? redirectObj.from.path : '/'
},
});
You can also use a named route instead of the path.
this.$auth.login({
redirect: {
name: redirectObj ? redirectObj.from.name : 'default-named-route'
},
});

VueJS (resource, router and #websanova/vue-auth) login with JWT tokens refresh token error

I'm building an app using VueJS and Electron, now I'm trying to create a login using the #websanova/vue-auth package and everything goes well (login, logout, route protection, etc..) the only thing I'm stuck on is that everytime I log in and refresh or restart electron, it kicks me back to the login page. The weird thing is, it refreshed the token successfully, if I look in the localstorage it is updated and when I try to make a manual request using a REST client the token works. I just can't get into the app using that token.
I'm using the latest versions of VueJS, vue-router, vue-resource and #websanova/vue-auth as of today (19-sep-2016).
The API side is a Laravel 5.3 installation and I'm using the tymondesigns/jwt-auth package to handle the JWT tokens.
this is how I use my routes:
'/': {
auth: true,
name: 'dashboard',
component: HomeView
},
'/login': {
auth: false,
name: 'login',
component: LoginView
}
The views are being compiled using browserify and vueify.
My login function is like this:
this.$auth.login({
body: this.body,
success: function () {
this.loading = false;
},
error: function () {
this.error.status = true;
this.loading = false;
this.body.password = '';
},
rememberMe: true
});
If you need more information in order to be able to help me, just let me know.
Edit: If you want to take a look, here are the links to the repo's:
API: https://github.com/luukhoeben/rmi-app-api
Electron app: https://github.com/luukhoeben/rmi-app-electron
Thanks,
Luuk
If you set
Vue.use(Auth, {
router: Router,
http: Vue.$http,
tokenExpired: () => {
return false
}
})
Setting tokenExpired to return false, like this, you will skip the token refresh all together, which is not that bad. Downside is that your clients will have an expired token at some point and will be forced to re-login.
Another method is to try and check when the token will expire and refresh based on that.