I'm now trying to code a login functionality using nuxt-auth.
I've got a FastAPI server that is set to work with HTTPOnly cookies, thus it needs a csrf token for throwing a user to my client. I can't handle the token because it's HTTPOnly so no LocalStorage
Login works fine but I can't manage to get the stored user. I made that after request to my /login endpoint, Nuxt also requests a user on /me endpoint. But I'm getting the 401 response and
Missing cookie access_token_cookie
error on /me. I don't know how to handle it.
my login request method
async userLogin() {
await this.$auth.loginWith('cookie', {
data: `grant_type=&username=${this.emailInput}&password=${this.passwordInput}&scope=&client_id=&client_secret=&`,
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
})
await this.$router.push('/account')
}
I read that nuxt-auth is bad at cookie patterns but the post was from 2018 and we have a 'cookie' strategy now. So is there a workaround of it's better to handle authentication manually?
my auth key in nuxt.config.js
auth: {
strategies: {
cookie: {
endpoints: {
login: {
url: "/api/v1/login/login",
method: "post",
withCredentials: true
},
logout: { url: "/api/v1/login/logout", method: "post" },
user: {
url: "/api/v1/users/me",
method: "get"
}
},
tokenType: "bearer"
}
}
}
I have a working http-only cookie based setup on Nuxt + Django.
My Nuxt application reverse proxies API requests to backend. So, it can read cookies on server side.
So, I create auth-ssr.ts middleware to check is user loggedIn
import { Context, Middleware } from '#nuxt/types'
import { parse as parseCookie } from 'cookie' // this is lib https://github.com/jshttp/cookie
/**
* This middleware is needed when running with SSR
* it checks if the token in cookie is set and injects it into the nuxtjs/auth module
* otherwise it will redirect to login
* #param context
*/
const authMiddleware: Middleware = async (context: Context) => {
if (process.server && context.req.headers.cookie != null) {
const cookies = parseCookie(context.req.headers.cookie)
const token = cookies['session'] || '' // here your cookie name
if (token) {
context.$auth.$state.loggedIn = true
}
}
}
export default authMiddleware
And here my nuxt.config.js
auth: {
strategies: {
cookie: {
user: {
property: 'user',
},
endpoints: {
login: {
url: '/api/v2/auth/login/',
method: 'post',
},
user: {
url: '/api/v2/auth/user/',
method: 'get',
},
logout: {
url: '/api/v2/auth/logout/',
method: 'post',
},
},
},
},
redirect: {
login: '/login',
},
plugins: ['#plugins/axios.ts'],
},
router: {
middleware: ['auth-ssr', 'auth'],
},
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
proxy: true,
},
proxy: {
'/api': {
target: 'https://backend.com/',
},
},
...
Related
I'm using the Nuxt auth module v5 and the Laravel sanctum provider. My csrf-cookie route works fine, and my login route works fine, but when trying to call this.$axios from a function, such as when creating a user's account (since auth module doesn't offer this) I'm getting a CSRF token mismatch.
It would appear that using axios directly like this doesn't have access to setting the cookie since no user logged in, how can I get the cookie to be set?
Method for account creation
/*
** Create accounr
*/
createAccount () {
this.feedback.isShown = false
this.isCreatingAccount = true
if (this.apiAccountCreationSource) this.apiAccountCreationSource.cancel('aborted')
const CancelToken = this.$axios.CancelToken
this.apiAccountCreationSource = CancelToken.source()
this.$axios.post(`${this.$config.apiUrl}/api/account`, this.account, {
cancelToken: this.apiAccountCreationSource.token,
timeout: 30 * 1000
}).then(res => {
this.apiAccountCreationSource = null
this.setContextualResponse(res)
setTimeout(() => {
this.login()
}, 250)
}).catch(err => {
this.setContextualResponse(err.response ? err.response.data : null)
}).finally(() => {
this.isCreatingAccount = false
})
},
Nuxt config
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
credentials: true,
baseURL: process.env.API_DOMAIN
},
// Auth module configuration: https://auth.nuxtjs.org/
auth: {
redirect: {
login: '/account/login/',
logout: '/account/login/',
callback: '/account/login/',
home: '/account/dashboard/'
},
strategies: {
'laravelSanctum': {
provider: 'laravel/sanctum',
url: process.env.API_DOMAIN,
endpoints: {
login: { url: '/api/login', method: 'post' },
logout: { url: '/api/account/logout', method: 'post' },
user: { url: '/api/account', method: 'get', propertyName: 'user' }
}
}
}
},
If you need to get the CSRF token all you need to do is make a request to your token endpoint and your browser should save the XSRF token. Then axios will automatically send this token in every subsequent request.
So all that you need to do is make a axios GET request to your csrf-cookie route before you send your POST request.
this.$axios.get(`${this.$config.apiUrl}/sanctum/csrf-cookie`)
Or you can chain both requests doing something like this:
this.$axios.get(`${this.$config.apiUrl}/sanctum/csrf-cookie`).then(() => {
return this.$axios.post(`${this.$config.apiUrl}/api/account`, this.account, {
cancelToken: this.apiAccountCreationSource.token,
timeout: 30 * 1000
}).then((res) => {
this.apiAccountCreationSource = null
this.setContextualResponse(res)
setTimeout(() => {
this.login()
}, 250)
}).catch((err) => {
this.setContextualResponse(err.response ? err.response.data : null)
}).finally(() => {
this.isCreatingAccount = false
})
})
Your authentication strategy works without this hassle because it handles this csrf request internally (https://github.com/nuxt-community/auth-module/blob/dev/src/providers/laravel-sanctum.ts)
References:
https://laravel.com/docs/8.x/sanctum#csrf-protection
https://github.com/axios/axios/issues/708#issuecomment-280920224
I'm new to Nuxt Auth and express session, when I perform this.$auth.loginWith('local', { data: '' }), I will set req.session.loggedIn = true in the server, and I can see my req.session.id = 'xxx' for example.
After that, the nuxt auth will make another call to /api/auth/user, but I can see the sessionID has been changed, and req.session.loggedIn was undefined.
How can I maintain the same session for every request?
Below is my config
auth: {
strategies: {
local: {
token: {
required: false,
type: false
},
endpoints: {
login: { url: '/api/auth/login', method: 'POST' },
logout: { url: '/api/auth/logout', method: 'POST' },
user: { url: '/api/auth/user', method: 'get' }
}
}
}
},
I'm using v5
"#nuxtjs/auth-next": "5.0.0-1616003482.75c20e6",
I'm using NuxtJs v2.13 with its auth module and Laravel with passport for my backend. for login i use the documented method:
async signIn(formData){
await this.$auth.loginWith('local',{
data: formData
})
if(this.$auth.user.depth > 1){
this.goTo('/cms/product')
}else{
this.goTo('/')
}
}
when the email or password is wrong it send me too nuxt error page! i should remain on login page.
what should i do!!?
BTW, i gonna use vee-validate on my form too. and this is my auth config on nuxt.config.js:
auth: {
strategies: {
local: {
endpoints: {
login: { url: 'auth/login', method: 'post', propertyName: '' },
logout: { url: 'auth/logout', method: 'post' },
user: { url: 'auth/info', method: 'get', propertyName: 'data' }
}
}
},
redirect: {
login: '/login',
logout: '/',
callback: '/login',
home: '/'
},
cookie: {
prefix: 'auth.',
options: {
path: '/',
maxAge: process.env.AUTH_COOKIE_MAX_AGE
}
}
},
Nuxt is redirecting because the error isn't being handled. You can simply wrap this code in an error handler. It's also good to put this code near the login component or page so you can use the status code of the error to display some meaningful response to the user, e.g. that the credentials were invalid.
try {
await this.$auth.loginWith('local', {
data: formData,
})
if (this.$auth.user.depth > 1) {
this.goTo('/cms/product')
} else {
this.goTo('/')
}
} catch (error) {
if (error.response) {
// Get the error status, inform the user of the error.
}
// Unexpected error, tell the user to try again later.
}
Since the #nuxtjs/auth package requires the #nuxtjs/axios package you can also read about intercepting errors on a global level with Axios Interceptors. I personally use try/catch blocks at the method level and use interceptors for catching 401 Unauthenticated errors and deleting the user information from Vuex.
I have some unfamiliar API calls in the current app I'm building. It needs a couple of parameters to setup the following api's. Currently based on subdomain in window it picks up the url parameter to create the first API call. After that I use Vuex store to store the relevant information for the organization data.
I'm currently trying to setup authorization and unfortunately due to it being inside nuxt.config.js I can't dynamically add data from the vuex store. Here is an example of the strategies I've set up. I'm currently using the local default strategies, but wan't to make the url requests dynamic:
nuxt.config.js
/*
** Nuxt.js modules
*/
modules: ['#nuxtjs/axios', '#nuxtjs/auth'],
/*
** Axios configuration
*/
axios: {
baseURL: 'https://api.getconflux.com',
headers: {
common: {
Accept: 'application/json, text/plain, */*'
}
}
},
/*
** Auth configuration
*/
auth: {
strategies: {
local: {
endpoints: {
login: {
method: 'post',
propertyName: 'token'
},
logout: {
url: '/logout',
method: 'post'
},
user: {
url: '/supporters/me',
headers: {
'organization-id': getters.companyId,
// Authorization Bearer needs to be automatically called from login
},
method: 'get',
propertyName: 'voter'
},
}
}
},
customStrategy: {
user: '~/schemas/user'
},
redirect: {
home: '/'
}
}
The current login I manually setup, with no url as I call that when my index page loads. This is the first api call to build out my state with the required data I need to call for my subsequent api calls after that.
For my auth api request to work, especially the fetchUser() call in the user strategy I need to get the companyId getter/state from my store. I have no idea how to do this here?
Does anyone have an idea of how I can best implement that?
Any help will be appreciated!
Thanks in advance!
You could disable the user endpoint in the nuxt.config.js file and then manually call the endpoint in Vuex to fetch the user details then pass it on to the setUser method of the auth NuxtJS module. So in your nuxt.config.js file, you should have this:
/*
** Nuxt.js modules
*/
modules: ['#nuxtjs/axios', '#nuxtjs/auth'],
/*
** Axios configuration
*/
axios: {
baseURL: 'https://api.getconflux.com',
headers: {
common: {
Accept: 'application/json, text/plain, */*'
}
}
},
/*
** Auth configuration
*/
auth: {
strategies: {
local: {
endpoints: {
login: {
method: 'post',
propertyName: 'token'
},
logout: {
url: '/logout',
method: 'post'
},
user: false
},
customStrategy: {
user: '~/schemas/user'
},
redirect: {
home: '/'
}
}
Then in your store, you have an action(or a mutation making the API request to get the user details). Then you call setUser as described here
I can't refresh page or open new tab of secure page after refresh or new tab will redirect me to login
again
Version
Nuxt.js v2.9.1
#nuxtjs/module: 4.8.4
secure page
middleware: ['auth'],
middleware of auth-module
login page
middleware: ['guest'],
middleware/guest.js
export default async function({ store, redirect }) {
// console.log(store.state.auth)
if (store.state.auth.loggedIn) {
return redirect('/')
}
}
console.log(store.state.auth) = { user: null, loggedIn: false, strategy: 'local' }
nuxt.config.js
auth: {
strategies: {
local: {
endpoints: {
// register: { url: 'member', method: 'post', propertyName: 'data.accessToken' },
login: { url: 'api/authen-admin', method: 'post', propertyName: 'custom' },
user: { url: 'api/admin', method: 'get', propertyName: 'custom' },
logout: false
},
tokenRequired: 'Authorization',
tokenType: false
}
},
watchLoggedIn: true,
localStorage: {
prefix: 'auth.'
},
cookie: {
prefix: 'auth.', // Default token prefix used in building a key for token storage in the browser's localStorage.
options: {
path: '/', // Path where the cookie is visible. Default is '/'.
expires: 5 // Can be used to specify cookie lifetime in Number of days or specific Date. Default is session only.
// domain: '', // Domain (and by extension subdomain/s) where the cookie is visible. Default is domain and all subdomains.
// secure - false, // Sets whether the cookie requires a secure protocol (https). Default is false, should be set to true if possible.
}
},
redirect: {
login: '/login',
logout: '/login',
home: '/'
},
resetOnError: true
}
I try to use vuex-persist to persist local storage but doesn't work and when login not redirect to home path still stay login path
maybe you can use nuxtServerInit to check the login user. place in the store/index.js folder as root folder. every time you open the web for the first time, this code will run. example i use the cookie to check user loggedIn or not:
export const actions = {
async nuxtServerInit ({ commit }, { req }) {
let auth = null
if (req.headers.cookie) {
// cookie found
try {
// check data user login with cookie
const { data } = await this.$axios.post('/api/auths/me')
// server return the data is cookie valid loggedIn is true
auth = data // set the data auth
} catch (err) {
// No valid cookie found
auth = null
}
}
commit('SET_AUTH', auth) // set state auth
},
}
here the documentation
Extending Fauzan Edris answer.
I was using Auth Nuxt, following fixed my issue.
export const actions = {
async nuxtServerInit({
commit
}, {
req
}) {
let auth = null
if (req.headers.cookie) {
// cookie found
try {
// check data user login with cookie
const {
data
} = await this.$axios.post('/user/profile')
// server return the data is cookie valid loggedIn is true
auth = data.data // set the data auth
} catch (err) {
// No valid cookie found
auth = null
}
}
// How we can set the user for AuthNuxt
// Source: https://auth.nuxtjs.org/api/auth
this.$auth.setUser(auth)
},
}
You set propertyName of user endpoint to 'custom', do you receive the response with this property name? when page reload, auth plugin will try to fetchUser method to sure client still authenticated, if you didnt config user endpoint correctly, regardless of whether receive, user will set null, so you will redirect to login page, you can check what user property set by run this code:
let user = await this.$auth.requestWith(
'local', null, { url: 'api/admin', method: 'get', propertyName: 'custom' } );
console.log(user);
I'm using Nuxt with Laravel Sanctum and the thing that solved the problem for me was an issue with the SESSION_DOMAIN. I'm running the project on a subdomain and the SESSIOn_DOMAIN was set to ".domain.com", but it has to be set to "sub.domain.com".
I've got same and find out on server message, that looked impossible
[404] /api/admin
So I've tried to add BASE_URL to this request url into nuxt.config.js
auth: {
strategies: {
local: {
endpoints: {
user: { url: `${BASE_URL}/api/admin`, ... },
...
}
and issue gone