NuxtJs - Cannot read property 'headers' of undefined - authentication

I'm a newbie in NuxtJs. I'm trying to implement an external API Call with axios which I get token and store it on cookie. Everything works well in development. But when I try to run npm run generate it gives me errors that I don't know what to do.
When I delete nuxtSeverInit, npm run generate runs smoothly. And after some research, i think that nuxtServerInit that I'm using shouldn't be used. Can anyone please tell me how to make it work.
This is the first project in a new company, so I'm trying to prove myself. Please help me with it. Will you.
Click here for image that shows the error that appears after npm run generate
This is store/index.js file
import Vuex from 'vuex'
var cookieparser = require('cookieparser')
const createStore = () => {
return new Vuex.Store({
state: {
auth: null,
},
mutations: {
update (state, data) {
state.auth = data
}
},
actions: {
nuxtServerInit ({ commit }, { req }) {
let accessToken = null
if (req.headers.cookie) {
var parsed = cookieparser.parse(req.headers.cookie)
if(parsed){
accessToken = parsed.auth
}
}
commit('update', accessToken)
},
}
})
}
export default createStore
middleware/authenticated.js file
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.auth) {
return redirect('/login')
}
}
middleware/notAuthenticated.js file
export default function ({ store, redirect }) {
// If the user is authenticated redirect to home page
if (store.state.auth) {
return redirect('/app/dashboard')
}
}
login.vue file
validateBeforeSubmit() {
this.$validator.validateAll().then((result) => {
if (result) {
this.button_title = 'One moment ...';
let submitted_user_data = {
'username': this.emailAddress,
'client_id': this.user_uuid,
'password': this.password,
}
MerchantServices.do_user_login(submitted_user_data)
.then(response => {
let access_token = response.data.access_token;
this.postLogin(access_token);
})
.catch(error => {
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
});
return;
}
});
},
postLogin: function(access_token_val) {
if(access_token_val != ''){
setTimeout(() => {
const auth = {
accessToken: access_token_val
}
this.$store.commit('update', auth)
Cookie.set('auth', auth)
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
this.$router.push('/app/dashboard')
}, 1000)
}else{
alert('hello')
}
},
and the last user login api call which also returns the token.
do_user_login(user){
var user_details = 'username='+user.username+'&client_id='+ user.client_id +'&grant_type=password&password='+user.password+''
return axios.post('myapiurl', user_details )
.then(response => {
return response;
});
},

Acording to Nuxt Docs req is not available on nuxt generate.
You should use nuxt build and than nuxt start after that.

Related

Urql config with guest token for SSG on next js

So I have a project using the latest Next js 13, React 18, Urql 3, and using typescript
Currently, I have issues when trying to query the urql from the getstaticprops function. My urql request needs a guest token, and I'm storing the token on session storage(other suggestions ?).
It has no issue when the query is running on the client, but I have it when querying inside the function.
My concern is related to the token reading, so the server cannot read the session storage value.
I'm asking what is the better and simplest way to make this work.
Does use cookies to store guest tokens will make this work?
Or the configuration that doesn't work?
This is my current config for urql.ts
import {
createClient,
ssrExchange,
dedupExchange,
cacheExchange,
fetchExchange,
} from "urql";
import { GRAPH_URL } from "#lib/constant/env";
import type { TypedDocumentNode } from "#urql/core";
const isServerSide = typeof window === "undefined";
const ssrCache = ssrExchange({
isClient: !isServerSide,
});
const client = createClient({
url: GRAPH_URL,
exchanges: [dedupExchange, cacheExchange, ssrCache, fetchExchange],
fetchOptions: () => {
const token = sessionStorage.getItem("accessToken");
return {
headers: {
authorization: token ? `Bearer ${token}` : "",
},
};
},
});
const query = async (
query: TypedDocumentNode<any, object>,
variables?: Record<string, string | string[] | unknown>
) => {
try {
const response = await client.query(query, variables as any).toPromise();
return response;
} catch (error) {
if (error instanceof Error) console.error(error.message);
}
};
const mutation = async (
mutation: TypedDocumentNode<any, object>,
variables?: Record<string, string | string[] | unknown>
) => {
try {
const response = await client
.mutation(mutation, variables as any)
.toPromise();
return response;
} catch (error) {
if (error instanceof Error) console.error(error.message);
}
};
export { client, query, mutation, ssrCache };
And this some of the code for the blog index page
export const getStaticProps = async () => {
await fetchArticlesSummary();
return {
props: {
urqlState: ssrCache.extractData(),
},
revalidate: 600,
};
};
export default withUrqlClient(() => ({
url: GRAPH_URL,
}))(BlogPage);
This is for the fetchArticlesSummary
export const fetchArticlesSummary = async () => {
try {
const {
data: { listArticles },
}: any = await query(getListArticle);
return listArticles.items;
} catch (error) {
return {
notFound: true,
};
}
};
I also doing a setup on _app.tsx
export default function App({ Component, pageProps }: AppProps) {
if (pageProps.urqlState) {
ssrCache.restoreData(pageProps.urqlState);
}
return (
<Provider value={client}>
<Component {...pageProps} />
</Provider>
);
}
Thank you
I have followed urql documentation about server-side configuration and many others but still don't have any solutions.

Vue doesn't fetch data from API in first render

After logging in I call await router.push('/'); to redirect to the home page where I load users and I get this error GET http://localhost:8080/users 401 then when I refrehs the page in the exact same component I get the data just fine with a 200 status. I'm not sure what's going on
async login (username, password) {
const response = await axios.post('/auth/login', {
username: username,
password: password
});
this.user = response.data;
localStorage.setItem('user', JSON.stringify(this.user));
await router.push('/');
},
This is the function I call after logging in
This is the router.js
import { createRouter, createWebHistory } from 'vue-router';
import Login from '../views/Auth/Login.vue';
import { useAuthStore } from '../stores/auth.store.js';
import IndexUser from "../views/Users/IndexUser.vue";
import IndexHive from '../views/Hives/IndexHive.vue';
const routes = [
{ path: '/', name: 'Home', component: IndexUser },
{ path: '/login', name: 'Login', component: Login },
{ path: '/users', redirect: { name: 'Home' } },
{ path: '/users/create', name: 'CreateUser', component: CreateUser },
{ path: '/hives', name: 'IndexHive', component: IndexHive }
];
import CreateUser from '../views/Users/CreateUser.vue';
const router = createRouter({
history: createWebHistory(),
routes
});
router.beforeEach(to => {
const authStore = useAuthStore();
const publicPages = ['/login'];
const authRequired = !publicPages.includes(to.path);
if (authRequired && !authStore.user) {
return '/login';
}
})
export default router;
This is the component I redirect to after logging in
onMounted( async () => {
const response = await axios.get('/users');
users.value = response.data;
})
Devtools
Network tab
Axios Error
details of request/response
Response of login
Update 2
Having seen the code, I think the problem is here:
import axios from "axios";
axios.defaults.baseURL = import.meta.env.VITE_API_URL;
if (localStorage.getItem('user')) {
const user = JSON.parse(localStorage.getItem('user'));
axios.defaults.headers.common['Authorization'] = `Bearer ${user?.accessToken}`;
}
this will read the axios.defaults.headers when the helpers/axios.js file is loaded. This is why axios.get('/users'); only works on second load, or rather only when the authentication is already loaded into localStorage. A change to the user object or a local storage will not update since this code only runs once at the beginning, the change to axios.defaults.headers needs to be dynamic.
Update
if setTimeout didn't work that could be due to a different issue. Also, if your request works a second time, but it also works if the authentication is passed directly, it seems to me that it has something to do with the authentication being handled implicitly.
I think what's happening is that you are creating multiple instances of axios and relying on shared authentication
// create single axios instance
export const api = axios.create({
withCredentials: true,
baseURL: BASE_URL // optional
})
// then use
await api.post('/auth/login', {
username: username,
password: password
});
// and
await api.get('/users');
This might make the axios instance remember the authentication information between calls. It may still require handling race condition if you have an app that doesn't wait on the login request to finish.
I think this is just an issue with a race condition
POST:/login and GET:/users requests appear to be done in parallel.
onMounted( async () => {
// this should wait until the `login` has been handled
const response = await axios.get('/users');
users.value = response.data;
})
I don't see how you call login so can't offer the the exact solution, but if you can store the login request state as a reactive variable, you can do something like
watch: {
loginState:{
immediate: true
handler(value){
if (value === LOADED) {
const response = await axios.get('/users');
users.value = response.data;
}
}
}
})
here's what the changes to the authStore might look like
export const STATES = {
INIT:"INIT",
PROCESSING:"PROCESSING",
ERROR:"ERROR",
LOADED:"LOADED",
}
export const loginState = ref(STATES.INIT);
async login (username, password) {
loginState.value = STATES.PROCESSING
try{
const response = await axios.post('/auth/login', {
username: username,
password: password
});
loginState.value = STATES.LOADED
this.user = response.data;
localStorage.setItem('user', JSON.stringify(this.user));
await router.push('/');
}catch(e){
// handle error
loginState.value = STATES.ERROR
}
},

is there a way to use Laravel sanctum token to be able to access protected routes (Laravel Project)

I'm building a website witch has some protected routes that I want to prevent non-authenticated users from accessing it, So I'm using Laravel Sanctum for that purpose. I was testing this using postman, but now I want to actually use it in production from backend, So how I suppose to do that token that was generated after login!
Thanks in advance.
I think the answer is late, but it may be useful to someone else
You will set your login routes
// Login Route
Route::middleware('guest')->group(function () {
Route::post('/login', LoginAction::class)->name('auth.login');
});
// Logout Route
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', LogoutAction::class)->name('auth.logout');
});
public function __invoke(AuthRequest $request)
{
$username = $request->input('username');
$password = $request->input('password');
$remember = $request->input('remember');
// I use filter_var to know username is email or just the regular username
$field = filter_var($username, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
// User::where('username', $username)->first();
// User::where('email', $username)->first();
$user = User::where($field, $username)->first();
if (
!$user || !Hash::check($password, $user->password)
|| !Auth::attempt(
[
$field => $username,
'password' => $password
],
$remember
)
) {
// Return Error Message
return $this->error(['message' => trans('auth.failed')], Response::HTTP_UNAUTHORIZED);
}
// After Check Useranme And Password Create Token
$token = $user->createToken($request->device)->plainTextToken;
// Return Success Message With Token
return $this->success(['message' => trans('auth.login', ['user' => $user->username]), 'token' => $token, 'user' => $user]);
in vuex Actions
export const loginAction = ({ commit }, userInfo) => {
let urlParams = new URLSearchParams(window.location.search);
return new Promise((resolve, reject) => {
login(userInfo)
.then((response) => {
if (response.success) {
commit("SET_LOGIN_USER", response.payload.user);
// Set Token Mutaion
commit("SET_TOKEN", response.payload.token);
// Notify User for success logged in
Notify.create({
type: "positive",
message: `welcome ${response.payload.user.username}`,
});
// Redirect to dashboard
router.push(
urlParams.get("redirect") || { name: "dashboard" }
);
}
resolve(response);
})
.catch((error) => {
// For any error remove any auth user details and notify him
Notify.create({
type: "negative",
message: `${error.response?.data.payload}`,
});
commit("REMOVE_AUTH_DETAILS");
reject(error);
});
});
};
in vuex mutations 3 methods
// Helpers Fuctions
import { setLoginUser, setToken, removeToken, removeLoginUser } from '../../../utils/auth';
export const SET_LOGIN_USER = (state, user) => {
state.loginUser = user
setLoginUser(user)
}
export const SET_TOKEN = (state, token) => {
state.token = token
setToken(token)
}
export const REMOVE_AUTH_DETAILS = (state) => {
state.loginUser = null
state.token = null
removeLoginUser()
removeToken()
}
My Helper Functions
// I use Quasar Framework But you can use any cookie package like js-cookie
import { Cookies } from "quasar";
const TokenName = "TOKEN";
const LoginUser = "LOGIN_USER";
export function setToken(token) {
return Cookies.set(TokenName, token);
}
export function getToken() {
return Cookies.get(TokenName);
}
export function removeToken() {
return Cookies.remove(TokenName);
}
export function setLoginUser(loginUser) {
return Cookies.set(LoginUser, JSON.stringify(loginUser));
}
export function getLoginUser() {
return Cookies.get(LoginUser);
}
export function removeLoginUser() {
return Cookies.remove(LoginUser);
}
I use Vue js for frontend and axios for making requests
in axios file request.js
// It's helper function to get token from cookie
import { getToken } from "./auth";
import router from "../router";
import store from "../store";
import axios from "axios";
// Create axios instance
const service = axios.create({
baseURL: "/api/v1/",
timeout: 10000 // Request timeout
});
// Request intercepter
service.interceptors.request.use(
config => {
if (getToken()) {
config.headers["Authorization"] = "Bearer " + getToken(); // Set Token
}
return config;
},
error => {
// Do something with request error
console.log("error-axios", error); // for debug
Promise.reject(error);
}
);
// response pre-processing
service.interceptors.response.use(
response => {
return response.data;
},
error => {
// remove auth user informations from cookies and return to login page if unauthorized
if ([401].includes(error.response.status)) {
store.commit('auth/REMOVE_AUTH_DETAILS')
router.push({ name: "login" });
}
return Promise.reject(error);
}
);
export default service;
// Logout Fuction in controller or action
public function __invoke(Request $request)
{
if(! $request->user()) {
return $this->error(['message' => trans('auth.no_auth_user')]);
}
// Remove Token
$request->user()->tokens()->delete();
return $this->success([
'message' => trans('auth.logout', ['user' => auth()->user()->username])
], Response::HTTP_OK);
}
// Logout mutation (vuex)
export const logoutAction = ({ commit }) => {
return new Promise((resolve, reject) => {
logout()
.then(() => {
Notify.create({
type: "negative",
message: `see you later ${getLoginUser().username}`,
});
commit("REMOVE_AUTH_DETAILS");
router.push({ name: "login" });
resolve();
})
.catch((error) => {
router.push({ name: "login" });
commit("REMOVE_AUTH_DETAILS");
reject(error);
});
});
};
Any other help I will not be late

React native axios call throws 403 but postman correctly outputs the data

I'm working on a RN app, which has redux in it. Now I can login with the help of jwt but when Im trying the to get the data from my other component its giving me 403 error. Please find below the relevant code.
Here is my reducer:
const initState = {
isLoadingCollegeDashList : false,
collegeDashList:{},
collegeDashListFail:false
}
const collegeReducer = ( state = initState, action) => {
switch(action.type){
case 'IS_LOADING_COLLEGE_DASH_LIST' :
return{
...state,
isLoadingCollegeDashList: true,
collegeDashList : false
}
case 'COLLEGE_DASH_LIST' :
return {
...state,
isLoadingCollegeDashList : false,
collegeDashList : true,
userData : action.userData
}
case 'COLLEGE_DASH_LIST_FAIL' :
return{
...state,
isLoadingCollegeDashList:false,
collegeDashList: false,
collegeDashListFail: action.error
}
default :
return state
}
}
and here's my action that's making get request
export const populateCollege = (token) => {
const headers = {
'api-secret' : ...secret...,
'authorization':...authToken...,
'Content-Type': 'application/json',
}
return dispatch => {
dispatch(isLoadingCollegeDashList(true));
return axios.get( '...api/api/...', {
},{
headers:headers,
})
.then((response) => {
if(response.status < 300){
dispatch(isLoadingCollegeDashList(false))
dispatch(collegeDashList(response))
console.log(response);
}
else{
response.json().then((responseJSON) => {
console.log("responseJSON",responseJSON);
dispatch(isLoadingCollegeDashList(false))
dispatch(collegeDashListFail(responseJSON.message))
})
}
})
.catch((error) => {
console.log("error",error);
dispatch(isLoadingCollegeDashList(false))
dispatch(collegeDashListFail(error))
})
}
}
export const isLoadingCollegeDashList = (bool) => {
return{
type:'IS_LOADING_COLLEGE_DASH_LIST',
isLoadingCollegeDashList:bool
}
}
export const collegeDashList = (userData) => {
return{
type:'COLLEGE_DASH_LIST',
userData
}
}
export const collegeDashListFail = (error) => {
return{
type:'COLLEGE_DASH_LIST_FAIL',
error
}
}
here's action that im calling if you want to check it
const mapDispatchToProps = dispatch => ({
populateCollege : (token) => dispatch(actions.populateCollege({token}))
});
PS I've for now stored token in the state of one hence passing the token from this dispatch itself.
Let me know if you need any clarification / more information then do let me know. Thanks in advance
Make sure you have the authorisation schema before your token. The schema can be like Basic, Bearer or any other value based on your authorisation details. (eg. Authorization: Bearer TOKEN).
Also, try to reuse your auth headers while creating the axios instance so you won't need to inject them on every call.

Multiple API providers with nuxt-axios and a plugin for default headers

I have a Nuxt app with many request to the same API, but also i need to make request to different providers apart of my main API and i don't know how to manage the default headers.
This is my working setup create a plugin to add the headers to all the request like this:
plugins/axios.js
export default function({ $axios, store, redirect }) {
$axios.onRequest(config => {
config.headers.common.Authorization = 'token 123';
config.headers.common["Custom-header"] = 'blablabla';
}
}
nuxt.config.js
module.exports = {
plugins: ["#/plugins/axios"],
axios: {
baseURL: process.env.API_URL,
}
}
store.js
async changeKeyVersionOnline({ commit }) {
const response = await this.$axios.get(
`users/1`
);
return response;
},
This works great for the main API but the problem is i need also to make request to other endpoints of third party service provider and of course the headers should be different.
How can i do that, i read about the proxy option of the nuxt-axios package but what i understand is this only changes the request base URL, i cant find how to set different headers to a specific request.
My final solution was based on create some actions in a central store so the axios requests are made trough this actions.
central.js (Where the axios related actions live)
import qs from "qs";
export const state = () => ({
accessToken: "",
clientId: 0
});
export const getters = {
getHeadersWithAuth: state => {
const config = {
headers: {
Authorization: "Bearer " + state.accessToken
}
};
return config;
},
getHeadersWithAuthClient: state => {
const config = {
headers: {
Authorization: "Bearer " + state.accessToken,
Client: state.clientId
}
};
return config;
}
};
export const mutations = {};
export const actions = {
async getWithAuth({ getters }, { path, params }) {
const config = getters.getHeadersWithAuth;
config.params = params;
config.paramsSerializer = function(params) {
return qs.stringify(params, { encode: false });
};
const result = await this.$axios.get(path, config);
return result;
},
async getWithAuthClient({ getters }, { path, params }) {
const config = getters.getHeadersWithAuthClient;
config.params = params;
config.paramsSerializer = function(params) {
return qs.stringify(params, { encode: false });
};
const result = await this.$axios.get(path, config);
return result;
},
async putWithAuthClient({ getters }, { path, body, params }) {
const config = getters.getHeadersWithAuthClient;
config.params = params;
config.paramsSerializer = function(params) {
return qs.stringify(params, { encode: false });
};
const result = await this.$axios.put(path, body, config);
return result;
}
};
test.js Other store which use the custom axios requests
async updateProductDetailsAction({ commit, dispatch, state }, productData) {
const request = {
path: `endpoints/` + productData.id + `/details`,
body: {
length: 123,
name: 'The product name'
},
params: {}
};
const result = await dispatch("auth/putWithAuthClient", request, {
root: true
});
await commit("setProductDetails", productData.id);
return result;
}