how to redirect to another app after login? - reactjs

I have a React js application "login", which makes a request to the backend and gets the jwt. I need that after the authentication is done, the "login" application redirects to another react application (hosted on another server) which needs to use the jwt

You can redirect a user to another page via the res.redirect method of ExpressJS.
So for example after a user logged in to your backend, you can do something like this:
app.all('/login', (req, res) => {
let isAuthenticated = tryAuthenticate(req);
if (isAuthenticated)
return res.redirect('/anotherApplicationOnAnotherServer');
else
return res.redirect('/login');
});

Framework-agnostic way
Whenever you get your jwt from the backend you call this to redirect:
window.location.href = `https://path-to-another-app?token=${jwt}`;
Then, at the target app you can get your token like this:
const url = new URL(window.location.href);
const jwt = url.searchParams.get("token");

Related

In MERN how do I manage JWT cookies client-side?

Can anybody explain to me how to use cookies with the MERN stack? I'm trying to make an analog for social media. I understand first steps but then - I don't know how to proceed.
I create endpoints for login, register, logout.
I create react app and made register form, login form. And from there - I'm sending axios request to my express endpoints.
In express - I'm saving to MongoDB user or checking login + password from DB.
I'm creating a JWT token with id payload and sending it via cookie.
But what do I do next? How to save which user is logged in in the react app? And how do I check if the user is still logged in and the cookie exists? Thanks for any help!
I have a repo that does much of what you are trying to do. In a React component called SignInForm.js I authenticate the user using a POST request. If the authentication is successful the server sets a cookie called 'token' and the client then redirects to a component called Dashboard.js. In Dashboard.js I have, among other things, the following lines of code:
import {useCookies} from 'react-cookie'
export const AppContext = createContext()
export default function DashBoard() {
const [cookies, setCookie, removeCookie] = useCookies(['token'])
let [authenticated, setAuthenticated] = useState(cookies.token !== undefined)
return (
<AppContext.Provider value={{authenticated, setAuthenticated}}>
The above code looks for the cookie called 'token'. If it exists then it sets authenticated to true and loads it into the AppContext provider. The server API routes, of course, are protected server side. But client side I can use the value of authenticated in other components to allow or disallow access. For example in a component called UserForm.js, I check authenticated like so (some code removed for clarity):
import { AppContext } from '../DashBoard'
export default function UserForm(){
let { authenticated, setAuthenticated} = useContext(AppContext)
if(!authenticated){
document.location = '/signin'
return <></>
}
and if they aren't authenticated then I redirect them to the SignIn.js component.
When you hit the backend route in which you are sending the token, on success the token will be automatically set in the browser. You can check by opening cookies in the Networks tab in the browser. But in order to do so you need to set the proxy.
In the frontend package.json file add:
"proxy": "http://localhost:{your-port-number}/"
Now cookie will be set automatically.
Now in order to authenticate user, you need to send this token to backend from frontend. In order to do so whenever you make a request to backend do the following(example):
axios.get('userdashboard', { withCredentials: true })
This will make sure that cookies will be used. (In this case 'userdashboard' is the route which only logged in user can access.)

Authenticating to Asp.Net core 2.0 MVC application (cookie) and REST API (JWT) simultaneously

I am using the VS 2017 Asp.Net Core 2.0 web application template, with the React.js template option.
This gives me a basic MVC + React setup with:
1. a single MVC page that hosts the React application
2. A ClientApp folder for the React application
3. A Rest API to which the React application talks.
By default none of these are authenticated. I want to secure both the MVC page and the REST API.
I have modified the code so that I use cookie authentication for the MVC page, and JWT bearer authentication for the REST API.
For the MVC cookie authentication, I have an MVC login login form. This works fine.
For the REST API authentication, I have added a TokensController that receives a username and login, and generates a JWT token to be used for requests to the REST API. This also works fine when tested from Postman: if user If user tries to access REST API without valid JWT token, user gets 401 response. If users gets token from TokensController and uses that to access REST API, user gets 200 response.
My problem is combining these two. What I would like is that when an unauthenticated user comes to my MVC page, user is redirected to a login form. When submitting that form, user would be authenticated to both the MVC application (getting back a cookie) AND to the REST API (getting back a JWT token).
I tried to achieve this so that instead of using an MVC login page, I would have a React login component, and unauthenticated user would be redirected to that. On submitting the React login form, in the React onSubmit event I would first call TokensController to get JWT token, store that token in browser localStorage, and then submit the form to the MVC controller for the cookie login.
I cannot make MVC redirect to my React login component. Probably some client-side versus server-side routing issue I don't grasp.
In routes.tsx, I am setting my React login component to this route:
<Route path='/login' component={Login} />
In Startup.cs, I have this:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
// Cookie auth for MVC page that hosts React app
.AddCookie(options =>
{
options.LoginPath = new PathString("/Account/Login");
options.LogoutPath = new PathString("/Account/Logout");
options.Cookie = new CookieBuilder
{
HttpOnly = true,
Name = "OurCookieAuthentication",
Path = "/",
SameSite = SameSiteMode.Lax,
SecurePolicy = CookieSecurePolicy.SameAsRequest
};
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = true;
}
)
// JWT authenticaton for REST API called by React app
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("thesecret")),
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
});
;
services.AddMvc()
}
Above, this currently points to my MVC login page, which works fine for the cookie login but does not handle the JWT:
options.LoginPath = new PathString("/Account/Login");
If I change it to this in an attempt to do the redirect to my React login component, it does not work (my React component is not loaded, probably because request goes to "/login" path on the server MVC controller instead of my client React component):
options.LoginPath = new PathString("/login");
Any ideas on how I could best achieve what I'm after?
have you tried adding the the react view to the MVC AccountController like so:
public IActionResult Login()
{
return View("../Home/Index"); //this should be the cshtml view that serves up the spa
}
Then in your react routes change the login route to:
<Route path='/Account/Login' component={Login} />

Howto redirect from axios interceptor in reactjs app?

I am trying to check each axios request for presence of token inside the header for non-public paths. I want to take the user to login page in case token is not found.
But this is inside reactjs app. What is the way to make this happen?
axios.interceptors.request.use(function (config) {
//If the header does not contain the token and the url not public, redirect to login
var accessToken = null;
if(sessionStorage.getItem('currentUserString')){
accessToken = 'bearer '+JSON.parse(sessionStorage.getItem('currentUserString')).tokenDetails.accessToken;
}
var configURL = config.url;
//if token is found add it to the header
if (accessToken) {
if (config.method !== 'OPTIONS') {
config.headers.authorization = accessToken;
}
}
//otherwise if the request is not for login page/auth service/home redirect to login page
else if(!isNotOpenPath(config.url)){
//we may want to store current path in the cookies. This can be retrieved after login is successful
//WHAT TO DO HERE TO TAKE USER TO LOGIN PAGE IN THE REACT JS APP????
}
}
How to signal react app, particularly we don't have dispatch here.
Can I just use
document.location.href='\login'?
I think the way to have routes inside your app is using React Router...
Then to navigate you could try this...
https://github.com/ReactTraining/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md

Node API - How to link Facebook login to Angular front end?

Rewriting this question to be clearer.
I've used passport-facebook to handle login with facebook on my site.
My front end is in Angular so I know now need to understand whats the correct way of calling that api route. I already have several calls using Angular's $http service - however as this login with facebook actually re-routes the facebook page can i still use the usual:
self.loginFacebook = function )() {
var deferred = $q.defer();
var theReq = {
method: 'GET',
url: API + '/login/facebook'
};
$http(theReq)
.then(function(data){
deferred.resolve(data);
})
return deferred.promise;
}
or is it perfectly ok/secure/correct procedure to directly hit that URL in a window location:
self.loginFacebook = function (){
$window.location.href = API + '/login/facebook';
}
Furthermore, from this how do I then send a token back from the API? I can't seem to modify the callback function to do that?
router.get('/login/facebook/callback',
passport.authenticate('facebook', {
successRedirect : 'http://localhost:3000/#/',
failureRedirect : 'http://localhost:3000/#/login'
})
);
Thanks.
I was stacked on the same problem.
First part:
I allow in backend using cors and in frontend i use $httpProvider, like this:
angular.module('core', [
'ui.router',
'user'
]).config(config);
function config($httpProvider) {
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
};
The second part:
<span class="fa fa-facebook"></span> Login with facebook
This call my auth/facebook route that use passport to redirect to facebook page allowing a user to be authenticated.
If the user grant access, the callback /api/auth/facebook/callback is called and the facebook.strategy save the user with the profile data.
After saving the user, i create a special token with facebook token, id and email. This info is used to validate every time the user access to private states in the front.
My routes are something like this:
router.get('/facebook', passport.authenticate('facebook',
{ session: false, scope : 'email' }));
// handle the callback after facebook has authenticated the user
router.get('/facebook/callback',
passport.authenticate('facebook',
{session: false, failureRedirect: '/error' }),
function(req, res, next) {
var token = jwt.encode(req.user.facebook, config.secret);
res.redirect("/fb/"+token);
});
In frontend i catch the /fb/:token using a state and assign the token to my local storage, then every time the user go to a private section, the token is sent to backend and validate, if the validation pass, then the validate function return the token with the decoded data.
The only bad thing is that i don't know how to redirect to the previous state that was when the user click on login with facebook.
Also, i don't know how you are using the callback, but you need to have domain name to allow the redirect from facebook. I have created a server droplet in digitalocean to test this facebook strategy.
In the strategy you have to put the real domain in the callback function, like this:
callbackURL: "http://yourdomain.com/api/auth/facebook/callback"
In the same object where you put the secretId and clientSecret. Then, in your application in facebook developers you have to allow this domain.
Sorry for my english, i hope this info help you.
Depending on your front-end, you will need some logic that actually makes that call to your node/express API. Your HTML element could look like
<a class='btn' href='login/facebook'>Login</a>
Clicking on this element will make a call to your Express router using the endpoint of /login/facebook. Simple at that.

Token API and Backend Google OAuth in Node and Angular

We are transitioning from an API using cookies for state (ExpressJS sessions) to a stateless (token) API.
We use a single PassportJS authentication strategy (GoogleStrategy). When the OAuth flow completes, Google calls back to a backend route with an access token.
Previously, we would set a cookie at this point using req.session and redirect the user to our dashboard.
With a token API, we generate a token based on the email (acting as a username) and access token (acting as password) when Google calls back to the backend route.
How do we pass this token to the front-end (Angularjs) so that it can make authenticated requests?
Do we need to switch to Google's front-end OAuth APIs?
One way to pass the token to a client-side web application is to put the signed JSON web token into the cookie , which your client-side app can access and use (either appending the token to every GET request or using it in your web socket authentication). Just to be clear, you're no longer using cookies as a reference for server-recorded state, instead you're are simply using them as a storage mechanism that both client and server can access, where you can store your encoded token.
Here's a great example from the generator-angular-fullstack:
In the router, receiving the callback from the google authentication:
.get('/callback', passport.authenticate('google', {
failureRedirect: '/signup',
session: false
}), auth.setTokenCookie);
And the setTokenCookie function:
function setTokenCookie(req, res) {
if (!req.user) return res.json(404, { message: 'Something went wrong, please try again.'});
var token = signToken(req.user._id, req.user.role);
res.cookie('token', JSON.stringify(token));
res.redirect('/');
}
And the signToken function:
function signToken(id) {
return jwt.sign({ _id: id }, config.secrets.session, { expiresInMinutes: 60*5 });
}

Resources