Reputation: 3925
I am developing an login application using Nextjs on frontend and springboot on backend.
I am able to login from the frontend which calls the loginAPI named /authenticate
developed in springboot and it is successfully returning the authToken
.After login I got redirects to home (/allcoupons
) page.
In login , I am passing the value of token as response.data
like this
const handleSubmit = async (e) => {
// e.preventDefault();
let requestbody = {
username: credentials.username,
password: credentials.password,
};
try {
const response = await axios({
method: "post",
headers: {},
url: "http://localhost:8081/authenticate",
data: requestbody,
});
//console.log("credentials for login = ", credentials);
//console.log("response from api = ", response);
if (response.status === 200) {
router.push(
{
pathname: "/allcoupons",
query: { auth: JSON.stringify(response.data) },
},
undefined,
{
shallow: true
}
);
} else alert("invalid credentials");
} catch (error) {
setTimeout(() => {
setShowAlert(true);
}, 2000);
setShowAlert(false);
}
};
Then I redirects to /allcoupons
component
In this component I am using getServerSideProps()
. I am able to access the authtoken
value from context.query.auth
but now I am unable to send this authtoken
value as bearer token
with another API /allcoupons
in the headers.
home.js
import React, { Fragment, useState, useEffect } from "react";
import { useRouter } from "next/router";
import Footer from "./components/footer";
import { Alert } from "flowbite-react";
import axios from "axios";
const Allcoupons = ({ datafromAPI }) => {
const router = useRouter();
//console.log("datafromAPI in components = ", datafromAPI.coupons)
//let data = datafromAPI.coupons;
const val = router.query.auth ? JSON.parse(router.query.auth) : {};
console.log("authtoken form login = ", val);
const [showModal, setShowModal] = useState(false);
.
.
return (
<>
.
.
.
</>
);
};
export async function getServerSideProps(context) {
//Fetch data from get API
//console.log("context = ", context.query.auth);
const token = context.query.auth;
console.log("token = ",context.query.auth)
const res = await axios.get(
`http://localhost:8081/couponstore/v1.0/coupons`,
//using like this
{ headers: { Authorization: `Bearer ${token}` } }
);
const datafromAPI = res.data;
console.log("data from API server = ",datafromAPI);
return { props: { datafromAPI } };
}
But as soon as I redirects to this page , it endpoint change from /allcoupons
to /allcoupons?auth={authtokenvalue}
.
Also this log comes in the terminal
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcnVuYWJoIiwiZXhwIjoxNjY0NTQwODYwLCJpYXQiOjE2NjQ1MzM2NjB9.Ib3X-6TMyQ3YfoGz-PuS4hehzuHq-N6XHwzLF6cXD2U"
error - [AxiosError: Request failed with status code 403] {
code: 'ERR_BAD_REQUEST',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [Function: httpAdapter],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: [Function] },
validateStatus: [Function: validateStatus],
headers: {
Accept: 'application/json, text/plain, */*',
Authorization: 'Bearer "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcnVuYWJoIiwiZXhwIjoxNjY0NTQwODYwLCJpYXQiOjE2NjQ1MzM2NjB9.Ib3X-6TMyQ3YfoGz-PuS4hehzuHq-N6XHwzLF6cXD2U"',
'User-Agent': 'axios/0.27.2'
},
method: 'get',
url: 'http://localhost:8081/couponstore/v1.0/coupons',
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: 'localhost',
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular *1],
write: [Function: writeAfterFIN],
[Symbol(async_id_symbol)]: 285969,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 60,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
_header: 'GET /couponstore/v1.0/coupons HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Authorization: Bearer "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcnVuYWJoIiwiZXhwIjoxNjY0NTQwODYwLCJpYXQiOjE2NjQ1MzM2NjB9.Ib3X-6TMyQ3YfoGz-PuS4hehzuHq-N6XHwzLF6cXD2U"\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Host: localhost:8081\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype],
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/couponstore/v1.0/coupons',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 4,
_maxListeners: undefined,
socket: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [Array],
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 403,
statusMessage: '',
client: [Socket],
_consuming: false,
_dumped: false,
req: [Circular *1],
responseUrl: 'http://localhost:8081/couponstore/v1.0/coupons',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 18,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'localhost',
protocol: 'http:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: 'http://localhost:8081/couponstore/v1.0/coupons',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
authorization: [Array],
'user-agent': [Array],
host: [Array]
},
[Symbol(kUniqueHeaders)]: null
},
response: {
status: 403,
statusText: '',
headers: {
'x-content-type-options': 'nosniff',
'x-xss-protection': '1; mode=block',
'cache-control': 'no-cache, no-store, max-age=0, must-revalidate',
pragma: 'no-cache',
expires: '0',
'x-frame-options': 'DENY',
'content-length': '0',
date: 'Fri, 30 Sep 2022 10:27:40 GMT',
connection: 'close'
},
config: {
transitional: [Object],
adapter: [Function: httpAdapter],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: [Object],
validateStatus: [Function: validateStatus],
headers: [Object],
method: 'get',
url: 'http://localhost:8081/couponstore/v1.0/coupons',
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: [Socket],
_header: 'GET /couponstore/v1.0/coupons HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Authorization: Bearer "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcnVuYWJoIiwiZXhwIjoxNjY0NTQwODYwLCJpYXQiOjE2NjQ1MzM2NjB9.Ib3X-6TMyQ3YfoGz-PuS4hehzuHq-N6XHwzLF6cXD2U"\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Host: localhost:8081\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/couponstore/v1.0/coupons',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'localhost',
protocol: 'http:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(kUniqueHeaders)]: null
},
data: ''
},
page: '/allcoupons'
}
Whats the mistake here??
Upvotes: 5
Views: 10017
Reputation: 106
First of all I do not recommend returning authtoken in response of successful login. You should use httpOnly cookies to set authtoken. It is more secure and you can set it from backend like this.
Second axios provide a way to set Authorization token in request. I mean you can alter default headers like below:
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
Read more in doc
Upvotes: 1
Reputation: 1099
This how do usually when I create react apps
import { createContext } from "react";
import { GlobalContextType } from "../types";
const defaultValue: GlobalContextType = {
dispatch: () => {},
state: {
user: { authToken: "", username: "" }
},
};
export const GlobalContext = createContext(defaultValue);
import { useContext } from "react";
import { GlobalContext } from "../contexts";
export function useGlobalContext () {
const context = useContext(GlobalContext)
if (!context) throw new Error("Missing Context - Global context");
return context;
}
import { Dispatch, useMemo, useReducer } from "react";
import {
GlobalContextActions,
GlobalContextState,
} from "../types";
export function globalContextReducer(
state: GlobalContextState,
action: ReducerAction<GlobalContextActions>
): GlobalContextState {
switch (action.type) {
case GlobalContextActions.setUser:
return {
...state,
user: action.payload
};
default:
return state;
}
}
export function useGlobalContextReducer(reducer = globalContextReducer) {
const [state, dispatch] = useReducer(reducer, {
user : {authToken: "", username: ""}
});
return useMemo(() => {
return {
dispatch,
state,
};
}, [d, state]);
}
import { GlobalContext } from "../src/contexts";
import { useGlobalContextReducer } from "../src/hooks/use-global-context-reducer";
export default function MyApp(props: MyAppProps) {
const { Component pageProps } = props;
const globalContextReducer = useGlobalContextReducer();
return (
<GlobalContext.Provider value={globalContextReducer}>
<Component {...pageProps} />
</GlobalContext.Provider>
);
}
this just my setup
then in my login page
import {GlobalContextActions, useGlobalContext} from "../../context"
import {Routes} from "../routes"
function Login() {
const { dispatch} = useGlobalContext();
const router = useRouter();
const [form, updateForm] = useState({
email: "",
password: "",
});
const onFieldChange = (event: ChangeEvent<HTMLInputElement>) => {
const { target } = event;
const { id, value } = target;
updateForm((current) => ({ ...current, [id]: value }));
};
const isLoginDisabled = !validateEmail(form.email) || !form.password;
const onLogin = async () => {
const dataService = container.resolve(DataService);
const result = await dataService.getLoginData(form.email, form.password);
if (result.error) {
return dispatchError(result?.errorMessage as string);
}
useGlobalContextReducer({action:GlobalContextActions.setUser, payload: result.data});
router.push(Routes.home);
};
return (
<Box>
<form action="" onSubmit={(event) => event?.preventDefault()}>
<TextField
id="email"
label={"email"}
variant="standard"
required
value={form.email}
onChange={onFieldChange}
type="email"
fullWidth
data-testid="email"
/>
<TextField
id="password"
label={"password"}
variant="standard"
type="password"
value={form.password}
required
fullWidth
onChange={onFieldChange}
data-testid="password"
/>
<Button type="submit" variant="contained" disabled={isLoginDisabled} onClick={onLogin}>
Login
</Button>
</form>
</Box>
);
}
export default Login;
and this how I use the global state
function MyPage() {
const {state} = useGlobalContext
return (<div>{JSON.stringinfy(state)}</div>)
}
Upvotes: 3