!! For the latest version see https://github.com/syntithenai/lambda-oauth-login-system
This package provides an easy way to add user registration and login to a React web application.
This package is an example application, that shows how to use the two related packages
It integrates a complete oauth2 server implementation and uses that for local authentication and token generation so passwords are never given to the web clients.
The delegated authentication provided by the oauth2 server is useful to allow third party web sites granular access to your application data.
For example, a public facing oauth server is required when developing apps for Google Home or Amazon Alexa that require user identification and account linking.
It also integrates passport.js to enable login using Google, Twitter, Facebook and Github. Passport includes solutions for many more authentication providers.
Features
Example web application.
The demo assumes there is a mongodb server running on localhost. See .env file and src/config.js for details.
To see the suite in action
git clone https://github.com/syntithenai/react-express-oauth-login-system.git
cd react-express-oauth-login-system/
npm i
npm start
Open https://localhost:3000/
npm i express-oauth-login-system-routes
npm i react-express-oauth-login-system-components
let config = require('./config');
var loginSystem = require('express-oauth-login-system-server')
var login = loginSystem(config)
const loginRouter = login.router
const authenticate = login.authenticate
const csrf = login.csrf
const app = express();
app.use('/',function(req,res,next) {
csrf.setToken(req,res,next)
});
app.use(bodyParser.json());
app.use(cookieParser());
// session required for twitter login
app.use(session({ secret: config.sessionSalt ? config.sessionSalt : 'board GOAT boring do boat'}));
app.use('/api/login',loginRouter);
// endpoint requiring authentication
app.use('/protected',authenticate,otherRoutesNeedingProtection);
app.listen(port, () => {
console.log(`Login system example listening at http://localhost:${port}`)
})
Use LoginSystem component on the login react dom route (eg /login) in your React application.
Note that the LoginSystemContext exposes the current user and a bunch of helper functions to it’s child renderer.
import {LoginSystem,LoginSystemContext, getAxiosClient,getMediaQueryString,getCsrfQueryString} from 'react-express-oauth-login-system-components'
<LoginSystemContext
authServer={process.env.REACT_APP_authServer}
authServerHostname={process.env.REACT_APP_authServerHostname}
>
{(user,setUser,getAxiosClient,getMediaQueryString,getCsrfQueryString, isLoggedIn, loadUser, useRefreshToken, logout, authServer, authServerHostname) => {
return <Router>
<div style=>
<img style= src={csrfMediaImage} alt='csrf' />
<img style= src={protectedMediaImage} alt='Not logged in' />
{!isLoggedIn() && <a href="/login/login"><button className='btn btn-primary'>Login</button></a>}
{isLoggedIn() && <a href="/login/profile"><button className='btn btn-primary'>Profile</button></a>}
<hr style=/>
<Route path='/login' render={
(props) => <LoginSystem
match={props.match}
location={props.location}
history={props.history}
authServer={authServer}
// also need external link to auth server (combind authServerHostname + authServer) for google, github, .. login buttons
authServerHostname={authServerHostname}
// update for login api location, use package.json proxy config to map other host/port to local link
loginButtons={process.env.REACT_APP_loginButtons?process.env.REACT_APP_loginButtons.split(","):[]}
// optional callbacks
logoutRedirect={'/'}
user={user} setUser={setUser} isLoggedIn={isLoggedIn} logout={logout} saveUser={saveUser} startWaiting={that.startWaiting} stopWaiting={that.stopWaiting}
/>}
/>
</div>
</Router>
}
}
</LoginSystemContext>
The email templates for registration and forgot password can be set in config.
To make layout changes, extend the LoginSystem class and override render.
All requests to your secured API endpoints must include an Authorization header including a bearer token
fetch(that.props.authServer+'/saveuser', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer '+user.token.access_token
},
body: JSON.stringify(user)
})
The module exports a number of helper functions to React including getAxiosClient that adds authentications and csrf headers to ajax requests automatically.
import {getCookie,getAxiosClient} from './helpers'
this.axiosClient = getAxiosClient(bearerToken);
that.axiosClient( {
url: that.props.authServer+'/protectedurl',
method: 'post',
})
.then(function(res) {
return res.data;
})
.then(function(user) {})
To enable login using external services you will need a key and secret from each of the services. Obtaining these keys may include filling a number of forms to justify your use of their API.
Keys are added to the config.js file.
To request keys visit the following links.
https://github.com/settings/applications
https://developer.twitter.com/
https://developers.facebook.com/apps/
https://console.developers.google.com/
https://developer.amazon.com/settings/console/securityprofile/overview.html
External services can use the oauth routes to obtain a token to access your API directly. In developing skills for Alexa(https://developer.amazon.com/) or Google Actions (https://console.actions.google.com/), the project administration website allows for entering
The authorization and token urls are immediately under the path that you located the loginsystem express routes. For example
[Some services also allow a choice between implicit and authorization code flows. Use authorization code.]
In the mongo database, create an entry in the oauthclients collection for each external service that can authenticate.
Client entries can also include fields to be used on the authorization page that is loaded when the external service redirects to your website to ask the user permission to grant access.
mongo browserexample
> db.users.insert({"_id" : ObjectId("5c859a7a64997a72a107065b"), "clientId" : "newclient", "clientSecret" : "testpass", "name" : "New Client", "website_url" : "https://client.com", "privacy_url" : "https://client.com/privacy", "grants" : [ "authorization_code", "password", "refresh_token", "client_credentials" ], "redirectUris" : []})
The server creates an initial client based on configuration settings for local authentication purposes. Examine that item for details.
mongo browserexample
> db.oauthclients.find()
{ "_id" : ObjectId("5c859a7a64997a72a107065b"), "clientId" : "test", "clientSecret" : "testpass", "name" : "Test Client", "website_url" : "https://localhost", "privacy_url" : "https://localhost/privacy", "grants" : [ "authorization_code", "password", "refresh_token", "client_credentials" ], "redirectUris" : [ ], "__v" : 0 }
The example provides code to protect against Cross Site Request Forgery by