How to call an external URL from a react express node component? - node.js

I'm trying to create a login application that passes a JWT in the headers of another application.
I started from this application User authentication keycloak 2 adapted to my Kecloak installation and it works fine.
Now I need to create a component that call an external URL passing the authorization token
In this component
import React, { Component } from 'react';
class callUrl1 extends Component {
constructor(props) {
super(props);
this.state = { response: null };
}
authorizationHeader() {
if(!this.props.keycloak) return {};
return {
headers: {
"Authorization": "Bearer " + this.props.keycloak.token
}
};
}
handleClick = () => {
console.log("callUrl1 called")
}
}
export default callUrl1;
I need something that call an external URL; something like:
SOME_FUNCTION('https://www.h.net/users', this.authorizationHeader())
to put inside handleClick.
All that I tried gives compilation errors.
How can I go from "http://localhost:3000" to "https://www.h.net/users" passing the JWT?

The built in library for making requests in frontend JS is called Fetch. Here's an example of how you might do it in your case:
handleClick = () => {
fetch('https://www.h.net/users', this.authorizationHeader())
.then((response) => {
// do something with the response here...
});
}
or with async/await:
handleClick = async () => {
const response = await fetch('https://www.h.net/users', this.authorizationHeader());
// do something with response like:
const data = await response.json();
}
For more information on Fetch, check out mdn: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Related

TikTok oAuth API auth code is always expired

I am trying to login using TikTok oAuth API
I have a Firebase Cloud Function (Nodejs) set up to complete the oauth flow, based
on the TikTok API Documentation, but when i reach the point (https://open-api.tiktok.com/oauth/access_token) to get the actual user access token it fails and i get an error.
The response i get is status 200 and
{
"data": {
"captcha": "",
"desc_url": "",
"description": "Authorization code expired",
"error_code": 10007
},
"message": "error"
}
The TikTok API always gives me the same authorization code. So i am guessing something is wrong. Any suggestion is welcomed.
Here is the code sample from the backend
The /linkTikTok/oauth and point used to redirect the user to tikTok oauth and the /linkTikTok/validate is used to request the access token. The code runs fine but when it reaches const URL = https://open-api.tiktok.com/oauth/access_token; and actually requests the user access token i get response above.
import * as express from 'express';
import * as cors from 'cors';
import axios from 'axios';
import * as cookieParser from 'cookie-parser';
import { config } from 'firebase-functions';
import { firestore } from 'firebase-admin';
import { colRefs } from '../../constants/db-refs';
const app = express();
app.use(cors());
app.use(cookieParser());
app.listen();
const { client_key, client_secret } = config().tikTokCredentials;
const redirectURI = `https://xxxxx.firebaseapp.com/linkTikTok/validate`;
app.get('/linkTikTok/oauth', async (req, res) => {
// The user's id;
const uid = 'a_user_id';
if (!uid) {
return res.status(401).send('This action requires user authentication');
}
// Random state
const csrfState = Math.random().toString(36).substring(7);
const state: any = {
state: csrfState,
timestamp: firestore.Timestamp.now(),
uid,
};
// A state object kepts in firestore
await colRefs.tikTokAuthState.doc(uid).set(state);
res.cookie('__session', { state: csrfState });
let url = 'https://open-api.tiktok.com/platform/oauth/connect/';
url += `?client_key=${client_key}`;
url += '&scope=user.info.basic,video.list';
url += '&response_type=code';
url += `&redirect_uri=${redirectURI}`;
url += '&state=' + csrfState;
return res.redirect(url);
});
app.get('/linkTikTok/validate', async (req, res) => {
// Query state
const state = req.query.state as string;
if (!state) {
return res.status(403).send('No state found');
}
const code = req.query.code as string;
if (!code) {
return res.status(403).send('No code found');
}
const sessionCookie = req.cookies['__session'] ?? {};
const sessionState = sessionCookie.state;
if (state !== sessionState) {
return res.status(403).send('Wrong state');
}
// Retrieve the uid from firestore
const uid = await (async () => {
const states = (await colRefs.tikTokAuthState.where('state', '==', state).get()).docs.map(d => d.data());
if (states.length !== 0 && states.length > 1) {
console.warn('More than one state');
}
return states[0].uid;
})();
console.log({ uid });
const URL = `https://open-api.tiktok.com/oauth/access_token`;
const params = {
client_key,
client_secret,
code,
grant_type: 'authorization_code',
};
try {
const result = await axios.post<any>(URL, '', {
params,
});
const data = result.data.data;
const {
access_token: accessToken,
refresh_token,
refresh_expires_in,
open_id: openId,
expires_in,
} = data;
if (!accessToken) {
throw new Error('No access token found');
}
// Application logic
...
});
would you share the piece of code you've written so that we could find the spot.
I got the same error in my code, however, in my case, I was doing duplicate authentication with the TikTok API, because I forgot the "code" GET parameter in my URL and when I was saving settings in my app again, the GET parameter fired again the authentication sequence and I got always the "Authorization code expired" error - but only the second time I was making requests.
You should check if you don't also have duplicate authentication requests in your app.

How to check session in angular 8

I have created login form with angular 8 and node js. I have set the session using node js in back end. i couldnt check session set or not in angular for avoid access dashboard without logged in. Kindly suggest the way to use login system using angular 8 and node js. Thanks.....
A very popular method is to use JWT (JSON Web Tokens) npm package to authenticate.
The process would be:
Send credentials to the server
Server generates and sends back JWT or a Bearer Token
FrontEnd would store it in browser cookies or localStorage
localStorage.setItem('TOKEN', tokenReceivedFromServer);
In subsequent Api Calls the token would be sent to the server in a Header (Authorization).
Authorization: `JWT ${localStorage.getItem('TOKEN')}`
FYI: JWT keyword is removed from string on the server before parsing token
The frontend can check if the token is set in storage to show login page / dashboard
First we need to check the login credentials valid or not in application.
In angular application component typescript file, we have send the data service in argument, the service send the values to backend using httpclient. If credentials valid we set the value in localstorage.
submitLogin(data:any)
{
this.LoginService.loginData(data).subscribe(data =>{
if(data.body.status_code == 404)
{
Swal.fire({
icon: 'warning',
title: 'Invalid E-Mail/Password!',
}).then(function(){
});
}else if(data.body.status_code ==200)
{
localStorage.setItem("user_id",data.body.token);
this.router.navigate(['/Dashboard']);
}else
{
Swal.fire({
icon: 'error',
title: 'Process Failed!',
}).then(function(){
});
}
});
}
In service.ts file make sure about those packages import
import { HttpClient } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import {Login} from './login';
in loginData function
url = "http://localhost:3000/loginCheck";
loginData(Login:Login):Observable<any>
{
return this.http.post(this.url,Login,{observe: 'response'});
}
in backend i have used node.js
in file app.js
first install jsonwebtoken package and include in the file.
npm install jsonwebtoken
then set the jsonwebtoken when where condition satisfies
let payload = {subject:employee_id}
let token = jwt.sign(payload,'secretKey')
var response = {
'token': token,
"status_code":200,
}
res.send(response);
res.end();
Whenever we use the login in angular we must use the authguard routing its helps to access dashboard without logged in.
ng generate guard auth
in auth.guard.ts file we must include the package and service
import { CanActivate, Router } from '#angular/router';
import {LoginService} from './login.service';
export class AuthGuard implements CanActivate {
constructor(private LoginService:LoginService,private router:Router) {}
canActivate(): boolean
{
if(this.LoginService.loggedIn())
{
return true
}else
{
this.router.navigate(['/login']);
return false;
}
}
}
In this file we just checking the localstorage value set or not in boolean datatype.
in service file
add the following code for get and return in boolean type
loggedIn()
{
return !!localStorage.getItem('user_id')
}
getToken()
{
return localStorage.getItem('user_id')
}
if its returns true we can access the dasboard, else its redirected to login page.
We must use this canActive function in routing otherwise it will not working
In app-routing.module.ts file
import { AuthGuard } from './auth.guard';
const routes: Routes = [
{path:'Dashboard',component:DashboardComponent},
{path:'receipt',component:ReciptComponentComponent,canActivate:[AuthGuard]},
];
It will helpus to access dashboard without loggedin but we need to check the token valid or not in backend, we can do that using angular interceptors
we should create the new service with interceptors name
ng g service token-interceptor
In interceptor file we need to import the following
import { Injectable,Injector } from '#angular/core';
import { HttpInterceptor } from '#angular/common/http';
import { LoginService } from './login.service';
In interceptors services inject in different way compared to component.
export class TokenInterceptorService implements HttpInterceptor{
constructor(private Injector:Injector) { }
intercept(req:any,next:any)
{
let loginService = this.Injector.get(LoginService);
let tokenzedReq = req.clone({
setHeaders:
{
Authorization: `Bearer ${loginService.getToken()}`
}
});
return next.handle(tokenzedReq)
}
}
we need to create a function in interceptors with the name intercept, then we need to inject the service as per injector.
In backend we need to create the helper function to verify the jsonwebtoken
if the authorization not set we can send the response 401 not found and can redirected to login page
function verifyToken(req,res,next)
{
if(!req.headers.authorization)
{
return res.status(401).send('Unauthorized request');
}
var token = req.headers.authorization.split(' ')[1];
if(!token)
{
return res.status(401).send('Unauthorized request');
}
if(token === 'null')
{
return res.status(401).send('Unauthorized request');
}
//let payload = jwt.verify(token,'secretKey');
let payload = jwt.decode(token,'secretKey');
if(!payload)
{
return res.status(401).send('Unauthorized request');
}
req.userId = payload.subject;
next();
}
then we can use this middleware function wherever we need
for example
app.get('/dashboard',verifyToken,function(req,res){
let events = [];
res.json(events);
});
In dashboard component ts file
this.dashboardService.getData().subscribe(data=>this.dashboardData=data,
err=>{
if(err instanceof HttpErrorResponse)
{
if(err.status===401)
{
this.router.navigate(['/login']);
}
}
})
in dashboard service ts file
url = "http://localhost:3000/dashboard";
getData()
{
return this.http.get<any>(this.url);
}
in app.module.ts file
import { AuthGuard } from './auth.guard';
import { ReciptComponentComponent } from './recipt-component/recipt-component.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { TokenInterceptorService } from './token-interceptor.service';
import { DashboardServiceService } from './dashboard-service.service';
in providers
providers: [AuthGuard,{provide:HTTP_INTERCEPTORS,useClass:TokenInterceptorService,multi:true},DashboardServiceService],

How to asynchronously submit POST request using React/Node.js/Express framework?

I am attempting to set up an asynchronous POST request using fetch with React and the Express framework with Node.js.
When the form is submitted, I can see on the server side that the data is being received by node.js.
My difficulty is that after the form is submitted on the client side, the web page acts like it is rendering for a very long time, and the browser says Waiting for localhost... while it is loading.
If I add res.send('received POST data') on the server side, I am redirected to localhost:9000/jobSearch which displays 'received POST data'.
I would like to retrieve data from localhost:9000/jobSearch and display it on the client side at localhost:3000 without reloading the page. I have read that this might be easier using axios or jQuery, however I would like to do this using only React and Express with Node.js. I have copied several examples, but I cannot get my implementation to work. Am I missing something?
React:
App.js
import React, { Component } from 'react';
import { FormControl, Button } from 'react-bootstrap'
class App extends Component {
constructor(props) {
super(props);
this.state = {
jobSearch: {
jobTitle: '',
location: ''
}
}
}
onSubmit = (e) => {
e.preventDefault();
const { jobTitle, location } = this.state;
fetch('http://localhost:9000/jobSearch',{
method: "POST",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(this.state.jobSearch)
}).then(res => res.json())
.then((result) => {
console.log('callback for the response')
})
}
render() {
return (
<form method="POST" action="http://localhost:9000/jobSearch">
<FormControl name="jobTitle"/>
<FormControl name="location"/>
<Button type="submit">Find Jobs</Button>
</form>
)
}
}
export default App;
Node.js:
jobSearch.js
var express = require('express');
var router = express.Router();
router.post('/', function(req, res, next) {
console.log('req.body here -> ', req.body)
});
module.exports = router;
Default HTML form behavior is to redirect to new page when the form is submitted. this is not expected behavior in most scenarios from a web application. its recommended to use controlled component to achieve this behavior. more info here.
basic idea behind this is to use a form submit handle function. this function will be called with a SyntheticEvent. the default html form behavior is prevented by calling preventDefault on the SyntheticEvent. implementation looks something like this:
class Form extends React.Component {
handleSubmit = (event) {
event.preventDefault();
// on submit logic
}
render() {
return (
<form onSubmit={this.handleSubmit}>
// form fields
</form>
);
}
}

Set access token in Loopback with React

I'm new to Loopback and Reactjs. I wanted to build an application with User login, logout functionality. But, I'm confused how to set the Access Token in Loopback with react front end and further access the other methods. I'm using the User Model provided with Loopback.
So far, I've written this little code, for access login, but I'm confused how to further set the access Token to authenticate.
import React, {Component} from 'react';
import axios from 'axios';
class Login extends Component{
constructor(props) {
super(props);
this.state = {
"username": ""
}
}
login(newUser) {
axios.request({
method:'post',
url:'http://localhost:3000/api/Users/login',
data: newUser
}).then(response => {
this.props.history.push('/');
}).catch(err => console.log(err));
}
onSubmit(e){
const newUser = {
username: this.refs.username.value,
password: this.refs.password.value
}
this.login(newUser);
e.preventDefault();
}
}
This code snippet as expected does not set the Access Token, so I was wondering if I require some additional middleware or something to do it.
try this code.
login(newUser) {
axios.request({
method:'post',
url:'http://localhost:3000/api/Users/login',
data: newUser
}).then(response => {
localStorage.ptspotter_accessToken = response.data.id;
localStorage.ptspotter_userId = response.data.userId
auth0.login();
this.props.history.push('/');
}).catch(err => console.log(err));
window.login();
}
you can store login userId and token in localStorage and access anywhere.

How to implement auto refresh token in graphql for jwt based authentication?

I am trying to figure out this scenario for my JWT based authentication in Apollo based graphql server (2.0) .
Basically after login a user gets accessToken and refreshToken from server.
AccessToken gets expired after certain period of time and server sends an error message indicating that token expired (TokenExpiredError) and then client need to communicate with server for new accessToken via passing refreshToken.
Flow is as following -
TokenExpiredError occurs
Get that error on client side
Queue all requests with old accessToken(so that server is not flooded with too many refreshToken calls and many accessTokens are generated by server)
Call refreshToken api on graphql server to get new accessToken
update accessToken for all authorised calls with new accessToken
Logout user incase refreshToken itself is expired
Prevent any kind of race condition b/w calls
I have already implemented refreshToken mutation on client side but can't figure out about when error occurs stop all requests -> request new token -> make all pending request again and if refresh token is expired logout user.
I followed this approach to solve my problem finally
Posting my approach for others
// #flow
import { ApolloLink, Observable } from 'apollo-link';
import type { ApolloClient } from 'apollo-client';
import type { Operation, NextLink } from 'apollo-link';
import { refreshToken2, getToken } from './token-service';
import { GraphQLError } from 'graphql';
export class AuthLink extends ApolloLink {
tokenRefreshingPromise: Promise<boolean> | null;
injectClient = (client: ApolloClient): void => {
this.client = client;
};
refreshToken = (): Promise<boolean> => {
//if (!this.tokenRefreshingPromise) this.tokenRefreshingPromise = refreshToken(this.client);
if (!this.tokenRefreshingPromise) this.tokenRefreshingPromise = refreshToken2();
return this.tokenRefreshingPromise;
};
setTokenHeader = (operation: Operation): void => {
const token = getToken();
if (token) operation.setContext({ headers: { authorization: `Bearer ${token}` } });
};
request(operation: Operation, forward: NextLink) {
// set token in header
this.setTokenHeader(operation);
// try refreshing token once if it has expired
return new Observable(observer => {
let subscription, innerSubscription, inner2Subscription;
try {
subscription = forward(operation).subscribe({
next: result => {
if (result.errors) {
console.log("---->", JSON.stringify(result.errors))
for (let err of result.errors) {
switch (err.extensions.code) {
case 'E140':
console.log('E140', result)
observer.error(result.errors)
break;
case 'G130':
this.refreshToken().then(response => {
if (response.data && !response.errors) {
this.setTokenHeader(operation);
innerSubscription = forward(operation).subscribe(observer);
} else {
console.log("After refresh token", JSON.stringify(response));
observer.next(response)
}
}).catch(console.log);
break;
}
}
}
observer.next(result)
},
complete: observer.complete.bind(observer),
error: netowrkError => {
observer.error(netowrkError);
}
},
});
} catch (e) {
observer.error(e);
}
return () => {
if (subscription) subscription.unsubscribe();
if (innerSubscription) innerSubscription.unsubscribe();
if (inner2Subscription) inner2Subscription.unsubscribe();
};
});
}
}

Resources