Connecting 2 different api to one react application with axios - reactjs

I have an existing api which I am using to get data to my react application, however, I created a new api which I want to connect to the react app. Wondering what is the solution to get data from 2 separated api on the same time?
I have a base url as it below:
let sessionToken = localStorage.getItem('currentUser.sessionToken')
axios.defaults.baseURL = urls.baseUrl(url),
if (sessionToken) {
axios.defaults.headers.common.Authorization = `Token token=${sessionToken}`
}
}```

Related

Django DRF + Allauth: OAuth2Error: Error retrieving access token on production build

We are integrating DRF (dj_rest_auth) and allauth with the frontend application based on React. Recently, the social login was added to handle login through LinkedIn, Facebook, Google and GitHub. Everything was working good on localhost with each of the providers. After the staging deployment, I updated the secrets and social applications for a new domain. Generating the URL for social login works fine, the user gets redirected to the provider login page and allowed access to login to our application, but after being redirected back to the frontend page responsible for logging in - it results in an error: (example for LinkedIn, happens for all of the providers)
allauth.socialaccount.providers.oauth2.client.OAuth2Error:
Error retrieving access token:
b'{"error":"invalid_redirect_uri","error_description":"Unable to retrieve access token: appid/redirect uri/code verifier does not match authorization code. Or authorization code expired. Or external member binding exists"}'
Our flow is:
go to frontend page -> click on provider's icon ->
redirect to {BACKEND_URL}/rest-auth/linkedin/url/ to make it a POST request (user submits the form) ->
login on provider's page ->
go back to our frontend page {frontend}/social-auth?source=linkedin&code={the code we are sending to rest-auth/$provider$ endpoint}&state={state}->
confirm the code & show the profile completion page
The adapter definition (same for every provider):
class LinkedInLogin(SocialLoginView):
adapter_class = LinkedInOAuth2Adapter
client_class = OAuth2Client
#property
def callback_url(self):
return self.request.build_absolute_uri(reverse('linkedin_oauth2_callback'))
Callback definition:
def linkedin_callback(request):
params = urllib.parse.urlencode(request.GET)
return redirect(f'{settings.HTTP_PROTOCOL}://{settings.FRONTEND_HOST}/social-auth?source=linkedin&{params}')
URLs:
path('rest-auth/linkedin/', LinkedInLogin.as_view(), name='linkedin_oauth2_callback'),
path('rest-auth/linkedin/callback/', linkedin_callback, name='linkedin_oauth2_callback'),
path('rest-auth/linkedin/url/', linkedin_views.oauth2_login),
Frontend call to send the access_token/code:
const handleSocialLogin = () => {
postSocialAuth({
code: decodeURIComponent(codeOrAccessToken),
provider: provider
}).then(response => {
if (!response.error) return history.push(`/complete-profile?source=${provider}`);
NotificationManager.error(
`There was an error while trying to log you in via ${provider}`,
"Error",
3000
);
return history.push("/login");
}).catch(_error => {
NotificationManager.error(
`There was an error while trying to log you in via ${provider}`,
"Error",
3000
);
return history.push("/login");
});
}
Mutation:
const postSocialUserAuth = builder => builder.mutation({
query: (data) => {
const payload = {
code: data?.code,
};
return {
url: `${API_BASE_URL}/rest-auth/${data?.provider}/`,
method: 'POST',
body: payload,
}
}
Callback URLs and client credentials are set for the staging environment both in our admin panel (Django) and provider's panel (i.e. developers.linkedin.com)
Again - everything from this setup is working ok in the local environment.
IMPORTANT
We are using two different domains for the backend and frontend - frontend has a different domain than a backend
The solution was to completely change the callback URL generation
For anyone looking for a solution in the future:
class LinkedInLogin(SocialLoginView):
adapter_class = CustomAdapterLinkedin
client_class = OAuth2Client
#property
def callback_url(self):
callback_url = reverse('linkedin_oauth2_callback')
site = Site.objects.get_current()
return f"{settings.HTTP_PROTOCOL}://{site}{callback_url}"
Custom adapter:
class CustomAdapterLinkedin(LinkedInOAuth2Adapter):
def get_callback_url(self, request, app):
callback_url = reverse(provider_id + "_callback")
site = Site.objects.get_current()
return f"{settings.HTTP_PROTOCOL}://{site}{callback_url}"
It is important to change your routes therefore for URL generation:
path('rest-auth/linkedin/url/', OAuth2LoginView.adapter_view(CustomAdapterLinkedin))
I am leaving this open since I think this is not expected behaviour.

reactjs fetch get failed but not when using axios?

I was using axios in my react app to get/send data from the rest server, but I'm trying to decrease the number of packages I'm importing so I wanted to try using regular fetch method. when I try to get all data using fetch my code works fine, but when I try to get data with specific Id it suddenly fails. but when I switch to axios using the same url, it works fine and I can also see the data using Postman.
this is my code:
const myurl = `${global.config.base_url}/users/${id}`; // http://localhost:8080/users/2
// const response = await fetch(myurl);
// const data = response.json();
// console.log(data); // returns < Pending > in developer tools
const response = await axios.get(myurl);
setUsername(response.data.username);
setEmail(response.data.email);
is there something wrong with my fetch method?

Nextjs + expressjs + Azure Web App : two factor authentication with express ('fs' can't be used on client side)

Stack : next.js/express.js/typescript/nodemon
I have a dependency on azure devops api which seems to be using 'fs' under the hood. So I can't use this library in any of the pages (including in getInitialProps).
I created a custom route (call it "get_data") in express server which provides me with the data. I call this route in getInitialProps of the page that will render the data (call it data.tsx) .
The whole app is behind two factor authentication in azure web apps. when get_data call is made in getInitialProps, I get a 401. Note that the origin is the same for serving the page and the get_data api.
Is there a way to pass current auth context when I make the get_data api call ?
In the express api, the method currently looks like this :
server.get('/get_data', (_req, res) => {
let ado = new azure_devops() // this uses the azure-devop-api package
ado.get_data().then((data) => {
res.setHeader('Content-Type', 'application/json');
res.json(data) // return data as json
})
});
Is there a way to merge the two (page and data serving) like the following (so I don't have to make extra api call with auth setup) ?
server.get('/data', (req, res) => { //note the change in route that serves the page data.tsx
const actualPage = '/data';
let ado = new azure_devops() // this uses the azure-devop-api package
ado.get_data().then((data) => {
res.setHeader('Content-Type', 'application/json');
res.write(data) // BUT this is where method returns instead i want to just add to the response body
app.render(req, res, actualPage); // THIS is not executed (so the page doesn't get rendered)
})
});
Appreciate any pointers.
Got the answer from this question.
Still making the API request from getInitialProps. I was missing adding cookie from the context.
const res = await fetch('http://' + context.req.headers.host + '/get_data', {
headers: {
cookie: context.req.headers.cookie, // WAS MISSING THIS
}
});

How to get authenticated token inside already logged in page

I'm new to React. I have already set up small size web service with Django backend on AWS EB. It has custom user model. And most contents are available after user logged in. It works fine.
When I start to make a mobile app with React Native, I set up the Django Rest into same place with sharing same db models with web service. I have chosen a Token authentication way for it. It works fine with React Native app on mobile. Once users log in through a mobile app, API returns auth Token. After getting Token from API, mobile app interacts with API including Token in JSON header.
During learn and develop React Native mobile app. I enjoyed it very much. So, I want to put small react app into one of my web pages, not writing a whole single page app. At this stage, one problem came to my mind that how my react app gets auth Token without inputting user ID and password again.
I spent hours to find any clue through googling, but failed. Can anyone give me a hint? How react app inside already logged web page interact with Token auth based API without log in again?
I think you can use cookies or localStorage to save your token and then in react part just get this token out of there.
You can use localStorage to set and get the token
ex:
set - > localStorage.setItem('token', 'dac43hh5r3nd23i4hrwe3i2un32u');
get - > localStorage.getItem('token');
For ReactJS, take a look at Where to store token. You can use localStorage or cookies.
Cookie usage:
document.cookie = cookie_name + "=" + value + ";expires=" + expire_date + ";";
PS: The expire date should be on GMT format.
localStorage usage:
// To set the token
localStorage.setItem('token', 'dac43hh5r3nd23i4hrwe3i2un32u');
// To get the token
localStorage.getItem('token');
The Django docs for Session, and read this question, it would help you.
Cookie or localStorage?
I would take cookies. Why? Because you can set expiration date(for automatic deletion, for example), domain, path and other things that can be useful. On the localStorage, you just store the info.
I'd like to leave my answer after I solved in my way through my long research and study. My solution is quite simple.
1. set DRF session authentication enable. Adding some code in setting.py
REST_FRAMEWORK = {
# ...
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
}
2. add 'credentials: "include"' into fetch code to use already logged in session cookie for authentication.
await fetch(API_URL, {
credentials: "include"
})
this solution solved my case.
React you can use any libray for the calling API, ex- axios is one of them. When you do the login first time save that token in the localstorage or session.
Now we have to add that token in header for that you can user the interceptor i.e when we make API call every time interceptor will get call, at that place you can get the token from the local storage or session add the request header.
below is sample code of interceptor.
import axios from 'axios';
import cookie from 'react-cookies';
import * as utils from './utils';
let axios_instance = axios.create();
axios_instance.interceptors.request.use(
(configuration) => {
const config = configuration;
const authToken = cookie.load('authToken');
if (authToken) {
config.headers.Authorization = `Token ${authToken}`;
}
return config;
},
(error) => Promise.reject(error)
);
axios_instance.interceptors.response.use((response) => {
return response;
}, (error) => {
if (error.response.status == 401) {
utils.clearCookie();
window.location.href = '/login';
return;
}
if (error.response.status == 403) {
utils.clearCookie();
window.location.href = '/login';
return;
}
if (error.response.status == 404) {
// window.location.href = '/not-found';
return;
}
if (error.response.status == 500) {
// window.location.href = '/server-error';
return;
}
return Promise.reject(error);
});
export default axios_instance;

ReactJS where do I store the API URI?

Where would I store the API URI centrally in a ReactJS Application? The URI only changes between environments and should be easily configurable (i.e. through environment variables).
I have looked into this package and into the new Context API, but am unsure it's the best way to achieve this. I have also looked into dotenv, but I don't like that I would have to use process.env.REACT_APP_SERVICE_URI in every component that wants to access the API. What is the usual approach?
I am not using Redux.
I don't think you need an external dependency to do that.
I usually create simple module called api-client.js, which is responsible for calls to external API and defining endpoints.
In your case you might have:
import axios from 'axios' // some http client lib
const endpoint = process.env.REACT_APP_SERVICE_URI? process.env.REACT_APP_SERVICE_URI : 'https://foo.api.net/'
export default {
getAllProducts () {
return axios.get(endpoint + 'products').then(response => {
log.debug(`api client fetched ${response.data.length} items`)
return response.data
}).catch(err => {
log.error(err.message)
throw err
})
}
},
getProductById (id) {
...
},
}
You read process.env.REACT_APP_SERVICE_URI only once.
I like to put this module inside api directory (and any other API related stuff).

Resources