Can I Hook Comcast Directly to a Tv

React template for JWT hallmark with private routes and redirects

JB

This guide is a template to using JWT authentication in React with a MERN app. This code tin be used equally a template and adapted equally needed for React apps with JWT hallmark. The full codebase is here.

This template is for storing user data in an HTTP-only cookie (not localStorage) and accessing the user's JWT token through that cookie. We can't read the cookie direct in the browser, and then we volition take to become the JWT info from the backend, using this recipe with React custom hooks, React frontend components, and a server calls:

  • React custom hook: The frontend (client side) makes a request for the backend (the server) to read the cookie.
  • Server call: The backend reads the cookie with an API telephone call, decodes the JWT if in that location is one, and sends the results to the frontend.
  • React frontend component: If a user was returned, they are stored in the frontend's global context. This context lets the app to reference the user and allow them to access protected routes. If a user is not returned, they cannot access protected routes.

This diagram explains how my Iron components interact with my custom hooks:

The backend for this app is an Express server and the database is MongoDB. Custom hooks will handle my land (no state management library). Routing is handled by React router. API calls with exist handled by Axios. This guide is going to focus on the frontend React piece mainly, and will not get too deeply into the backend. However, I will show the API I made for hallmark. If yous're only interested in the frontend, curlicue by the Authentication API section to pace i.

All steps:

  1. Create context to store the user, and so they can exist accessed across the entire application.

2a. Cookie a user and store them in context on log in or registration, and then their session volition persist.

2b. Create a custom hook to check if a user has a session cookie when they arrive on the site.

3. Shop user in global context.

4. Create private routes for authenticated users.

5. Redirect users and conditionally return components by authentication status.

Pre-requisite: Hallmark API

I have to different routers: viewRouter, for calls on pageview (checking if a user is logged in), and authRouter for registration, login, and logout. This is in my app.js.

          app.use('/', viewRouter);          app.utilise('/auth/', authRouter);        

> authRouter.js

          const express = require('express');
const authController = require('./../controllers/authController');
const router = express.Router();
router.mail('/register', authController.registerUser);
router.mail service('/login', authController.loginUser);
router.get('/logout', authController.logoutUser);
module.exports = router;

> viewRouter.js

          const express = crave('limited');
const authController = require('../controllers/authController');
const router = express.Router();
router.get('/user', authController.checkUser); module.exports = router;

All of this logic is handled in my authController component, which has different functions for login, registration, logout, setting a token, and signing a token. I will exit this code below, but the rest of the boilerplate, such as the userSchema and error handling, volition exist in the codebase.

> authController.js

          const User = require('./../models/userModel');
const AppError = require('./../utils/AppError');
const catchAsync = require('./../utils/catchAsync');
const jwt = require('jsonwebtoken');
const { promisify } = require('util');
//sign JWT token for authenticated user
const signToken = id => {
return jwt.sign({ id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN
});
}
//create JWT token for authenticated user
const createUserToken = async(user, code, req, res) => {
const token = signToken(user._id);
//set up decease to 1 month
allow d = new Date();
d.setDate(d.getDate() + thirty);

//get-go-political party cookie settings
res.cookie('jwt', token, {
expires: d,
httpOnly: true,
secure: req.secure || req.headers['x-forwarded-proto'] === 'https',
sameSite: 'none'
});

//remove user password from output for security
user.countersign = undefined;
res.status(code).json({
condition: 'success',
token,
information: {
user
}
});
};
//create new user
exports.registerUser = async(req, res, next) => {
//pass in asking data hither to create user from user schema
effort {
const newUser = await User.create({
username: req.torso.username,
email: req.torso.email,
password: req.torso.password,
passwordConfirm: req.trunk.passwordConfirm
});
createUserToken(newUser, 201, req, res); //if user tin't be created, throw an error
} catch(err) {
next(err);
}
};
//log user in exports.loginUser = catchAsync(async(req, res, next) => {
const { username, password } = req.body;

//check if email & password exist
if (!username || !password) {
return adjacent(new AppError('Delight provide a username and password!', 400));
}

//cheque if user & countersign are correct
const user = await User.findOne({ username }).select('+password');
if (!user || !(expect user.correctPassword(password, user.password))) {
return next(new AppError('Incorrect username or password', 401));
}
createUserToken(user, 200, req, res); }); //check if user is logged in exports.checkUser = catchAsync(async(req, res, next) => {
permit currentUser;
if (req.cookies.jwt) {
const token = req.cookies.jwt;
const decoded = expect promisify(jwt.verify)(token, process.env.JWT_SECRET);
currentUser = await User.findById(decoded.id);
} else {
currentUser = null;
}
res.status(200).send({ currentUser });
});
//log user out exports.logoutUser = catchAsync(async (req, res) => {
res.cookie('jwt', 'loggedout', {
expires: new Appointment(Date.now() + x * thousand),
httpOnly: true
});
res.status(200).ship('user is logged out');
});

That's enough boilerplate—over again, the entire server tin exist found in the codebase. This guide does not embrace how JWT works, just in that location will be more resources at the bottom.

Step 1: Create context to shop the user.

Store the user in context allows their data to be accessed across the entire application.

Nosotros need to store this user in our app's global context using React's createContext and useContext hooks. First, we have to create an context instance using createContext:

src > hooks > UserContext.js

          import { createContext } from 'react';          consign const UserContext = createContext(nix);        

This is our entire UserContext. Ready the initial value of UserContext to null, because there is initially no user. Note: context instances are named with a uppercase letter of the alphabet (similar a component), not a lowercase letter (like a claw). I store context instances in my hooks folder because they role like a hook.

Step 2a: Cookie a user and store them in context on log in or registration.

When a user logs in or registers, four things happen:

  1. A cookie with their JWT token and a one-month life expectancy is ready in their browser.
  2. The user's JWT token is read by the browser.
  3. The decoded user is set in the application'south global context.
  4. The newly authenticated user is pushed to their homepage.

We already accept ii dissimilar components to handle Login and Registration. Each one will utilise two custom hooks: ane. useForm, which handles the form inputs and state, and 2. useAuth, which handles the hallmark. We volition focus on useAuth. As an example, I will show registration, only login works the same mode.

This is my Registration course

This the lawmaking for the page above:

src > pages > Register.js

          import React from 'react';
import { Link } from 'react-router-dom';
import FormInput from './../components/FormInput';
import CTA from './../components/CTA';
import Prompt from './../components/Prompt';
import ConfirmPasswordInput from './../components/ConfirmPasswordInput';
import Error from './../components/Fault';
import useForm from './../hooks/useForm';
import useAuth from './../hooks/useAuth';
export default part Register() {
const { values, handleChange} = useForm({
initialValues: {
email: '',
username: '',
password: '',
passwordConfirm: ''
}
});
const { registerUser, mistake } = useAuth(); const handleRegister = async (e) => {
eastward.preventDefault();
await registerUser(values);
}
return(
<div className="page" way={{justifyContent:'center'}}>
<div className="inlineForm">
<h3>Annals</h3>
<div className="inlineForm__notif">
{error && <Mistake error={fault.messages}/>}
</div>
<course onSubmit={handleRegister}>
<FormInput blazon={"text"}
placeholder={"Email"}
proper name={"email"}
value={values.email}
handleChange={handleChange} />
<FormInput type={"text"}
placeholder={"Username"}
name={"username"}
value={values.username}
handleChange={handleChange} />
<ConfirmPasswordInput type={"password"}
placeholder={"Password"}
name={"password"}
value={values.username}
handleChange={handleChange} />
<div className="inlineForm__submit">
<Link to='/login'>
<Prompt prompt={"Existing account? Log in."}/>
</Link>
<CTA proper noun={"annals"} type={"submit"}/>
</div>
</course>
</div>
</div>
)
}

On submit of the form, an async office is chosen in the folio that prevents the default behavior from occurring (the page refreshing on submit) and calls two functions in the useAuth custom hook. And so far, that hook has these functions:

src > hooks > useAuth

          import { useState, useContext } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
import { UserContext } from './UserContext';
consign default function useAuth() {
let history = useHistory();
const { setUser } = useContext(UserContext);
const [error, setError] = useState(null);
//set user in context and push them home
const setUserContext = async () => {
render wait axios.get('/user').so(res => {
setUser(res.information.currentUser);
history.button('/home');
}).catch((err) => {
setError(err.response.data);
})
}
//annals user
const registerUser = async (data) => {
const { username, email, password, passwordConfirm } = data;
return axios.post(`auth/register`, {
username, email, password, passwordConfirm
}).then(async () => {
await setUserContext();
}).take hold of((err) => {
setError(err.response.data);
})
};
render { registerUser,
error
}
}

This hook is doing several things with these two functions.

  • registerUser: a Mail request is fabricated /auth/register endpoint with the user information in the request body. The user is created in the user database. They are likewise cookied with a first-party cookie fix for a 30-day expiration. (See the API code in the Pre-requisite step for the backend part.)
  • setUserContext: a GET request is made to check if at that place is a session cookie. If it exists, the user returned from the JWT is stored in the context and the user is pushed /home using the useHistory hook from React Router.
  • Error handling: if these functions can't be executed the catch cake sets an error bulletin. See here for more about fault handling.

Note: information technology is possible to set the user returned from the POST request as the user stored in context. Information technology may seem gratuitous to split this into two calls: a POST to create the user, and a Become to fetch that same user so shop them. Notwithstanding, we will always shop users from a Become asking when they make it on the site afterward authenticating. For a consistent user experience, information technology'due south important to always store the aforementioned user object, which is accomplished with the Become request here.

The login process is identical to registration on the Login component, and we add the login function to our useAuth hook.

src > hooks > useAuth

          import { useState, useContext } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
import { UserContext } from './UserContext';
export default function useAuth() {
let history = useHistory();
const { setUser } = useContext(UserContext);
const [error, setError] = useState(null);
//set up user in context and push them home
const setUserContext = async () => {
return wait axios.become('/user').then(res => {
setUser(res.data.currentUser);
history.button('/dwelling');
}).catch((err) => {
setError(err.response.data);
})
}
//register user
const registerUser = async (data) => {
const { username, email, password, passwordConfirm } = data;
return axios.post(`auth/register`, {
username, email, password, passwordConfirm
}).grab((err) => {
setError(err.response.data);
})
};
//login user
const loginUser = async (information) => {
const { username, countersign } = information;
return axios.post(`auth/login`, {
username, password
}).and then(async () => {
await setUserContext();
}).grab((err) => {
setError(err.response.information);
}
render { registerUser,
loginUser,
error
}
}

Step 2b: Create a custom claw to check if a user has a session cookie when they go far on the site.

Users don't always log in or register when they're arriving to a site. Nearly of the time, the user's authentication status is read by the cookie created after they authenticated. Without being able to read a user's cookie, they would be logged out on every difficult refresh. So, the awarding must e'er check if a user is already authenticated as before long as they go far on the site. This tin can be accomplished by running our useFindUser() custom hook as before long as our awarding is first rendered.

src > hooks > useFindUser.js

          import { useState, useEffect } from 'react';
import axios from 'axios';
consign default function useFindUser() {
const [user, setUser] = useState(null);
const [isLoading, setLoading] = useState(truthful);
useEffect(() => {
async function findUser() {
expect axios.get('/user')
.then(res => {
setUser(res.data.currentUser);
setLoading(fake);
}). catch(err => {
setLoading(false);
});
}
findUser();
}, []);
render {
user,
isLoading
}
}

This call uses two pieces of country: the user, set to null when there is no user, and isLoading, which is truthful before the call to check the user has completed. This role is the nigh of import for hallmark. The useEffect hook will run information technology as soon as the user arrives on the site. If there is a user, the promise will be resolved and the user volition be gear up in the user state. If there is no user, the promise will be rejected, and the user will correctly remain null. Either mode, isLoading will exist false.

When this axios call is made, the backend will use the checkUser() function at the /user route to decode and read the JWT token from the cookie in the user's browser, as it did on login and reg.

At present, let's call our useFindUser() custom hook and store the returned value in the global context at the top-level of our application. In this case (and well-nigh cases) this is the App.js file.

src > App.js

          const { user, setUser, isLoading } = useFindUser();        

This line is added to App.js earlier the return statement. The unabridged App.js code will be beneath.

Step 3: Store the returned user in the app's global context.

Whichever way the user is stored in context—either the useAuth hook or the useFindUser hook—their data needs to be attainable over the entire application. We wrap our entire app in the context provider, which makes the information bachelor to whatsoever component inside information technology:

src > App.js

          import './App.css';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { UserContext } from './hooks/UserContext';
import Register from './pages/Register';
import Login from './pages/Login';
import Landing from './pages/Landing';
import NotFound from './pages/NotFound';
import useFindUser from './hooks/useFindUser';
office App() { const { user, setUser, isLoading } = useFindUser(); return (
<Router>
<UserContext.Provider value={{ user, setUser, isLoading }}>
<Switch>
<Route verbal path="/" component={Landing}/>
<Road path="/register" component={Register}/>
<Route path="/login" component={Login}/>
<Route component={NotFound}/>
</Switch>
</UserContext.Provider>
</Router>
);
}
export default App;

The data fix as the value property is now attainable in any component above.

If a console.log(user), I will run across this object in the console:

This is now accessible similar whatever typical object, and I can return user.username or user.email to get private properties.

Note: never store raw user passwords in context. Hash the password (as I did above) or remove the countersign field!

Pace 4: Create a private route component for authenticated users.

In applications with authentication, there are "protected routes" that only those logged in users can admission. To prevent non-authenticated users from accessing sure routes, nosotros tin create a PrivateRoute component that "screens" users for authentication status and responds accordingly.

If a user is authenticated, they can proceed to the route, which is inside the PrivateRoute component. If a user is not authenticated, nosotros handle them by directing them a generic, public road.

src > pages > PrivateRoute.js

          import React, { useContext } from 'react';
import { Route, Redirect } from 'react-router-dom';
import { UserContext } from './../hooks/UserContext';
import Loading from './../components/Loading';
consign default role PrivateRoute(props) { const { user, isLoading } = useContext(UserContext);
const { component: Component, ...remainder } = props;
if(isLoading) {
return <Loading/>
}
if(user){
return ( <Route {...rest} return={(props) =>
(<Component {...props}/>)
}
/>
)}
//redirect if in that location is no user
return <Redirect to='/login' />
}

This is the PrivateRoute component, where the user is directed when they try to access a protected route. The React component the authenticated user will see is passed to this component every bit a prop. So if the Habitation component is protected, it will be passed as the <Component/> hither.

Notation: It'due south important to return the component inside a <Route/> , and not simply return <Component/> . If just the component is returned, the user volition be directed correctly, but they won't exist able to access the props from React Router, like useHistory, useParams, or state from the Link component.

The PrivateRoute has iii possible outcomes: 1. loading, in which example a loading screen is shown (or you can return null if you don't want to prove a loading screen), 2. not loading and a user is found, in which case they are routed to the component, and 3. not loading and a user is not constitute, in which example they are directed to the login page.

Calculation a PrivateRoute component to our App.js is simple:

src > App.js

          import './App.css';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { UserContext } from './hooks/UserContext';
import PrivateRoute from './pages/PrivateRoute';
import Register from './pages/Register';
import Login from './pages/Login';
import Landing from './pages/Landing';
import Home from './pages/Home';
import NotFound from './pages/NotFound';
import useFindUser from './hooks/useFindUser';
role App() { const { user, setUser, isLoading } = useFindUser(); return (
<Router>
<UserContext.Provider value={{ user, setUser, isLoading }}>
<Switch>
<Route verbal path="/" component={Landing}/>
<Road path="/register" component={Register}/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/home" component={Home}/>
<Route component={NotFound}/>
</Switch>
</UserContext.Provider>
</Router>
);
}
export default App;

Now, when I register with a new user, they are immediately taken here:

This is only accessible for logged in users

This component is the Home returned from Private Route.

Step 5: Redirect users and conditionally return components past authentication status.

Some components are accessible to all users, simply should change based on authentication status. This can be handled with redirects and conditional rendering.

Redirect authenticated users

If nosotros have a public landing page for unauthenticated users, we may non desire authenticated users to meet information technology. Authenticated users should be redirected to their personalized, individual homepage.

          import React, { useContext } from 'react';
import Header from '../sections/Header';
import { Redirect } from 'react-router-dom';
import { UserContext } from '../hooks/UserContext';
export default function Landing() {
const { user } = useContext(UserContext);

if(user) {
<Redirect to='/home'/>
}

return (
<div className="page">
<Header/>
<h3>This is the public landing page</h3>
</div>
)
}

If there is a user, they volition immediately be redirected to the private /home road before the return statement shows them the generic, public landing folio.

Provisional rendering

Different versions of the aforementioned component may be needed for public vs. individual routes. The typical usecase for conditional rendering is a phone call to action button: unknown users should encounter a Login push, while known users should see a Logout push button. Nevertheless, there are many cases where the same component will change for known vs. unknown users.

An example of this in my application is the Header component. If a user is stored, the header reflects that I'k logged in: the Logout button is presented instead of Login, and my username (retrieved from context) is dynamically populated.

src > sections > Header.js

          import React, { useContext } from 'react';
import InlineButton from './../components/InlineButton';
import { UserContext } from '../hooks/UserContext';
import useLogout from './../hooks/useLogout';
export default function Header() {
const { user } = useContext(UserContext);
const { logoutUser } = useLogout();
return(
<header>
{user
?

<>
Hello, {user.username}
<InlineButton name={'logout'} handleClick={logoutUser} />
</>
:
<div className='btnGroup'>
<Link to = "/login">
<InlineButton name={"login"}/>
</Link>
<Link to = "/register">
<InlineButton name={"register"}/>
</Link>
</div>
}
</header>
)
}

This is my basic Header component that uses a ternary operator to conditionally render two dissimilar versions of the header if there is or isn't a user. As you can see, when there is a user, I use user.username to personalize the page with my username.

Conclusion

That's about it for a basic template! There is much more to authentication, but I promise this template provides a sizable foundation. If someone has a more than elegant pattern, I would appreciate any relevant resources. I establish it hard to discover many comprehensive hallmark guides. In sum, the pieces covered hither are:

  • Storing authenticated users in global context.
  • Checking if a user is logged in based on their HTTP-merely cookie/JWT token.
  • Protecting routes for authenticated users only.
  • Redirecting users and conditional rendering based on their hallmark status.

Project repo

Some resources I used & institute helpful:

  • Token-based authentication and how it works.
  • Storing JWT in a cookie.
  • useContext hooks and context provider docs.
  • Ben Awad's video tutorial on retrieving a user from JWT.
  • Ben Awad's video tutorial on storing the user in global context.
  • Set up a PrivateRoute — by far the best guide I found, others didn't include the <Road/> component.

gayleefor1941.blogspot.com

Source: https://levelup.gitconnected.com/react-template-for-jwt-authentication-with-private-routes-and-redirects-f77c488bfb85

0 Response to "Can I Hook Comcast Directly to a Tv"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel