How can I use refresh token - reactjs

I have a get refresh token api like this http://token.net/api/auth/refresh-token . I want to use it in my login function but to be honest I don't know anything about refresh tokens. how can I implement the refresh token into this.
LoginAuth.js
export const useLogin = () => {
const LoginAuth = async (data: AuthenticationProps) => {
await axios.post(`client.com/auth/login`,
{
email: data.email,
password: data.password,
},
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
}
}
)
.then((res) => {
if(res.status === 200) {
console.log("works");
}
}, (err) => {
console.log(err);
})
}
return {
LoginAuth,
}
}

Refresh token is used to generate new access token for an application. If the access token has an expiration date, once it expires, the user would have to authenticate again to obtain an access token.
Steps:
After successful login response, store token in localStorage.
Add axios response interceptor method axios.interceptors.response to call refresh_token API and update localStorage with new access_token.
whenever token will get expired, API call will returnINVALID_TOKEN code in response and refresh_token API will be called.
Now, further any API will be called with new refreshed token.

Related

How to change axios request config?

For each request to the backend, I send an access token. If the token has not passed verification, then I save the config of the original request and make a request to update the tokens. If everything is fine, then I re-send the original request. The problem is that the original request is sent with the old token. Tell me how I can update the value in headers.Authorization?
import axios from 'axios'
import { setAccessToken } from '../store/authSlice'
export const axiosConfig = (accessToken: any, dispatch: any) => {
const API_URL = 'http://localhost:3001/api'
const $api = axios.create({
withCredentials: false,
baseURL: API_URL
})
$api.interceptors.request.use((config) => {
config.headers!.Authorization = `Bearer ${accessToken}`
return config
})
$api.interceptors.response.use(
(config) => {
return config
},
async (error) => {
const originalRequest = error.config
if (error.response.status === 403 && error.config && !error.config._isRetry) {
originalRequest._isRetry = true
try {
const response = await axios.get(`${API_URL}/auth/refresh-tokens`, {
withCredentials: false,
headers: {
Authorization: `Bearer ${localStorage.refreshToken}`
}
})
localStorage.setItem('refreshToken', response.data.refreshToken)
dispatch(setAccessToken(response.data.accessToken)) // new token
return $api.request(originalRequest) // <=== original request with old token
} catch (e) {
console.log('error')
}
}
throw error
}
)
return $api
}
You are using $api.interceptors.response.use() which is on the response not the request. You will not be able to change the Authorization header on a request that has already been sent.
I would use some type of error handler or axios response intercepter to direct user to the login page if they are expired/not logged in. Then I would have an error handler function that can try to attempt a re-authorization then have the error handler function resend the original request that would then have the updated Authorization token. But remember you still cant change the request that has already been sent.
myAxoisInstance.interceptors.request.use(
function (config) {
// This is run on each request so if you have or then update
the token for local storage your next request will get that updated token
const token = localStorage.getItem("token");
if (token) {
//Assign the token to the config
//If you need to send a request here to see if token is valid I don't recommend that. If you are using a JWT token you can check if it is expired here and do something else like a redirect to login.
config.headers!.Authorization = `Bearer ${token}`;
} else {
//Can retrieve and assign token here I typically like to do a redirect here as they are likely not logged or sesson expired. in or something like that. Then allow the sign-in process to add the token to the local storage.
}
return config;
},
function (error) {
return Promise.reject(error);
}
);

getting a status code of 403 from spotify web api when trying to fetch with OAuth 2.0 Token

I'm playing around with the Spotify Web API, and I'm trying to fetch my most played songs. I'm using the client credentials OAuth flow (you can read more about it at https://developer.spotify.com/documentation/general/guides/authorization/client-credentials/) to get an access token so that I can create requests. I'm getting the access token just fine, but when I try to fetch the data with the token, I'm getting a 403, indicating that my request is not being authorized.
Error code:
GET https://api.spotify.com/v1/me/top/tracks 403
I'm using React, so I'm fetching the data on page load with useEffect.
API File (spotify.ts)
import { Buffer } from 'buffer';
const clientId = "" // omitted for privacy
const clientSecret = "" // omitted for privacy
const getToken = async (): Promise<string> => {
const res = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'client_credentials',
scope: 'user-top-read',
}),
});
const data = await res.json();
return data.access_token;
};
const getMostRecentSong = async (token: string) => {
const res = await fetch('https://api.spotify.com/v1/me/top/tracks', {
headers: {
'Authorization': `Bearer ${token}`,
},
});
const data = await res.json();
return data;
}
App.tsx
import React, { useEffect } from 'react'
import { getToken, getMostRecentSong } from './services/spotify'
const App = () => {
useEffect(() => {
const getData = async () => {
const accessToken = await getToken();
const data = await getMostRecentSong(accessToken);
console.log(data);
}
getData();
}, [])
return (
...
)
}
I've included my App.tsx file as well for convenience, but the only error I'm getting is with the request itself. Any help is greatly appreciated :)
The /me/top/{type} route requires the user-top-read scope, so using the Client Credentials flow will always result in an error. Here's a summary of the Client Credentials flow:
The Client Credentials flow is used in server-to-server authentication. Since this flow does not include authorization, only endpoints that do not access user information can be accessed.
Instead, you will need to use the Authorization Code flow and proxy the Spotify requests using a request mechanism that isn't restricted by CORS (e.g. a server or serverless function), or use the Implicit Grant flow which can be implemented without an additional cooperating process (you can do it all in your client React app).

Hide Authorization Token in Request Headers || ReactJS

I am using axios in my react project to fetch data from an API which is authenticated using a token. Now, I m able to get the data but the when I inspect, the token is clearly visible in the Request Headers which is risky.
Please suggest If there is a way to abstract or hide the token in the request headers to make it more secure.
Below the snippet.
const makeRequest = async () => {
const response = await axios.get(BACKEND_API_URL, { headers: { 'Authorization': 'Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' } });
if (response.status === 200) {
// do something...
}
}
makeRequest()

JWT Secure Routes in React

I am adding JWT authentication to a blog app I'm working on. On the server side (built with Nodejs) I am creating the token and sending it back with a successful login. On the client side I am saving the token in LocalStorage. When I log in and check the application tab in dev tools I can see the token. On the server route where blogs are posted to I check authentication. If the token is authenticated the blog posts to the database, but if I delete the token or change it and then make the post request the request fails, as expected.
So far so good.
What I'm confused about is how to restrict access to the page where the blog editor resides on the client. If people aren't authenticated they should not be able to access this page at all, even though if not authenticated they can't post anyway.
Login route on server:
router.post('/login', async (req, res, next) => {
const cursor = User.collection.find({username: req.body.username}, {username: 1, _id: 1, password: 1});
if(!(await cursor.hasNext())) {
return res.status(401).json({ message: 'Cannot find user with that username' });
}
const user = await cursor.next();
try {
if(await bcrypt.compare(req.body.password, user.password)) {
const token = jwt.sign({
email: user.email,
userId: user._id
}, process.env.JWT_SECRET, { expiresIn: "1h" })
return res.status(201).json({
message: 'User Authenticated',
token: token
});
} else {
return res.status(400).json({
authenticated: false,
username: req.body.username,
password: req.body.password
})
}
} catch (err) {
return res.status(500).json({ message: err })
}
});
How I'm checking the token authentication on the server:
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization;
console.log(token);
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.userData = decoded;
next();
} catch (error) {
return res.status(401).json({ message: 'Auth Failed' })
}
}
My client side login route fetch:
handleSubmit(event) {
event.preventDefault();
const formData = {
username: event.target.username.value,
password: event.target.password.value
}
fetch('http://localhost:4000/user/login', {
method: "POST",
mode: "cors",
body: JSON.stringify(formData),
headers: {
"Content-Type": "application/json"
}
})
.then(res => res.json())
.then(res => {
localStorage.setItem('authorization', res.token);
console.log(res);
})
.catch(err => console.error(err))
}
And here is my fetch call from the client on the blog posting route where the editor resides:
handleSubmit = (event) => {
event.preventDefault();
const data = new FormData(event.target);
const body = event.target.postBody.value;
const postTitle = event.target.title.value;
console.log(event.target);
console.log(data);
console.log(event.target.postBody.value);
fetch('http://localhost:4000/blog', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
"Authorization": localStorage.getItem('authorization')
},
mode: 'cors',
body: JSON.stringify({
title: postTitle,
postBody: body
})
})
.then(res => res.json())
.then(err => console.error(err))
}
So, like I said, everything is working as expected but I don't want people to be able to access the editor page if they are not authenticated. I guess I would check to see if the token exists in localstorage and then redirect? But wouldn't I also need to check to see if the token on the client can be authenticated on the server as well? So would I essentially need to post to the server to do the check whenever someone navigates to that page, or any other page I want to restrict access to? Come to think of it, if a user is already authenticated I don't want them to be able to access the login page either.
I have heard that people use Redux to manage state across components, but I really don't want to go down that road, at least not yet because this project is for learning purposes and I don't really want to start with Redux or anything else like that until I have a better grasp of React on it's own. I don't know if I need Redux or not and from what I understand, that's enough to know that I probably don't need it.
This is just such a different flow than I'm used to from PHP sessions and I'm having some trouble wrapping my head around it.
I realize that you folks may not really need to see all this code, but I also would like some more experienced eyes to see it and point out anywhere I might be making mistakes or where I could improve here.
So this is what I have come up with for now, if anyone knows a better way, I'm definitely open to suggestions.
I created a class called CheckAuth which essentially just makes a GET request to the server and sends the jwt along with it.
checkAuth.js:
class CheckAuth {
constructor() {
this.auth = false;
}
async checkLogin() {
console.log(localStorage.getItem("authorization"));
let data = await fetch('http://localhost:4000/auth', {
method: "GET",
mode: "cors",
headers: {
"Content-Type": "application/json",
"authorization": localStorage.getItem("authorization")
}
})
return data.json();
}
logout(cb) {
localStorage.removeItem('authenticated')
this.auth = false;
cb();
}
async isAuthenticated() {
const data = await this.checkLogin()
return data;
}
}
export default new CheckAuth();
Then on pages that only logged in users should see I am doing a simple check to see if they have the token and if it's valid inside of componentDidMount().
componentDidMount() {
const check = checkAuth.isAuthenticated();
console.log(check);
check.then(res => {
console.log(res);
if(res.authenticated !== true) {
this.props.history.push("/login");
}
})
.catch(err => { console.error(err) })
}

Check if user is logged in React Js

Hello guys i have a JWT authentication in my react project i want to make a function to check if the user logged . My login function is like this :
export function login(data) {
const endpoint = '/api/auth/jwt/'
const csrfToken = cookie.load('csrftoken')
let thisComp = this
if (csrfToken !== undefined) {
let lookupOptions = {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
credentials: 'include'
}
fetch(endpoint, lookupOptions)
.then(function(response){
return response.json()
}).then(function(responseData){
console.log(responseData)
localStorage.token = responseData.token
localStorage.expires = responseData.expires // Store the token
console.log("Token Stored", localStorage.token)
console.log("Token Expires", responseData.expires)
refreshToken(data) // Put in to the refresh function
}).catch(function(error){
console.log("error", error)
})
}
}
and my function isLoggedIn is like this :
export function isLoggedIn() {
// Check if we have a token stored
if (localStorage.token !== undefined) {
// I also want to check if the token is still work and don't expire
// i have acces to the expiration date like this :
// localStorage.token == > Token Expires 2018-06-19T14:51:59.451703Z in
the console
// How can check if the token still work ?
return true
}
return false
}
Can't you just check the expires property you stored in your local storage and compare it to current Date ?
Other ways to handle it: store the expiration date in the token itself, then use a jwt decoding library to decode the token and extract it from there. If you are using refresh tokens you can simply wait for the server to return a 401 Unauthorized response and send the refresh token to request a new one.

Resources