Skip to content

Instantly share code, notes, and snippets.

@jermsam
Last active May 10, 2018 12:21
Show Gist options
  • Save jermsam/d45e0ef41391d80d1d79458cc3834997 to your computer and use it in GitHub Desktop.
Save jermsam/d45e0ef41391d80d1d79458cc3834997 to your computer and use it in GitHub Desktop.
Cross-domain issue For the case where you are using feathers-reduxify-authentication and feathers-authentication-management
// CLIENT
// src/App.js
// other imports
import VerifySocialPage from './pages/VerifySocialPage'
function App () {
return (
<Switch>
// .... other routes
<Route path="/verify-social" exact component={VerifySocialPage} />
</Switch>
);
}
export default App;
/* path to our successRedirect defined in the server*/
// SERVER
// src/authentication.js
// Bring in the oauth-handler
const makeHandler = require('./oauth-handler')
module.exports = function (app) {
const config = app.get('authentication');
// Create a handler by passing the `app` object.
const handler = makeHandler(app);
// ....
app.configure(oauth2(Object.assign({
name: 'google',
Strategy: GoogleStrategy,
Verifier: EmailFirstOAuth2Verifier,
// Provide the handler to the Google auth setup.
// The successRedirect should point to the handle-oauth-login.html hosted on the web server.
handler: handler(config.google.successRedirect),
emailField: 'email'
}, config.google)));
}
/* Now we are good to look at the client side (React + Redux) code*/
// SERVER CODE
//config/default.json
// ..... note that json may not allow these comments depending on your eslint configuration, you may want to remove these
"google": {
"clientID": "paste your google plus app id",
"clientSecret": "paste your google plus app secret",
"successRedirect": "http://localhost:3000/verify-social",
"scope": [
"profile openid email"
]
},
/*
we just set the successRedirect path (the path to redirect too when all is good)
to the component that will read the jwt in our client domain, store it in the browser's local storage
and then use it as we describe later.
for now we'l first specify our provider's handler property in the provider's configuration in authentication.js
as below
*/
// CLIENT
//services/index.js
import reduxifyServices ,{ getServicesStatus as getStatus } from 'feathers-redux';
import reduxifyAuthentication from 'feathers-reduxify-authentication';
// well Configured and authenticated feathers-client
import app from './app'
async function checkIfUserIsVerified() {
try {
const token = localStorage.getItem('feathers-jwt');
// Get our initialized service so that we can register hooks and filters
const service = app.service('users');
console.log('token: ',token)
const payload = await app.passport.verifyJWT(token);
console.log('payload: ',payload)
const user = await service.get(payload.userId);
console.log('we got: ',user)
return user.isVerified ;
} catch (err) {
return {};
}
}
// Reduxify feathers-client.authentication
const session = reduxifyAuthentication(app,
checkIfUserIsVerified().then(t=> ({isUserAuthorized: t}) ) // WE INSIST USER IS 'verified' TO AUTHENTICATE
);
/**
// Sign in with the JWT currently in localStorage
if (localStorage['feathers-jwt']) {
store.dispatch(signin.authenticate()).catch(err => { ... });
}
// Sign in with credentials
store.dispatch(signin.authenticate({ type: 'local', email, password }))
.then(() => { ... )
.catch(err => { ... });
*/
const services = reduxifyServices(app, ['users','authManagement']);
/**
* // email addr verification with long token
// Feathers is now 100% compatible with Redux. Use just like [Feathers method calls.](#methods)
store.dispatch(services.authManagement.create({ action: 'verifySignupLong',
value: verifyToken,
}, {})
);
*/
export{
app,
session,
services,
getStatus
}
// SERVER CODE
// src/oauth-handler.js
/*define an oauth handler */
module.exports = function (app) {
return function (url) {
const config = app.get('authentication');
const options = {
jwt: config.jwt,
secret: config.secret
};
return function (req, res, next) {
if (req.feathers && req.feathers.payload) {
app.passport.createJWT(req.feathers.payload, options).then(token => {
res.redirect(`${url}?token=${token}`);
})
.catch(error => {
next(error);
});
}
};
};
};
/* as seen here this purposes on getting the set url (for the configured successRedirect) and attach a token parameter
whose value is our generated jwt. thus next we have to configure this (successRedirect) property for our specific provider
Will use google plus
*/
// CLIENT
// pages/VerifySocialPage.js
import React,{Component} from 'react'
import queryString from 'query-string'
import PropTypes from 'prop-types'
import { compose } from 'recompose'
import {connect} from 'react-redux'
import {
withRouter,
} from 'react-router-dom';
import {app,session} from '../services';
/* eslint-disable no-console */
// store jwt to localStorage
async function setJwtTocken(token) {
try {
console.log('token: ',token)
// remove cause it was for testing purposes
// localStorage.removeItem('feathers-jwt');
return await window.localStorage.setItem('feathers-jwt', token);
} catch (err) {
return {};
}
}
async function getUserFromJwtTocken(token) {
try {
// Get our initialized service so that we can register hooks and filters
const service = app.service('users');
console.log('token: ',token)
const payload = await app.passport.verifyJWT(token);
console.log('payload: ',payload)
const user = await service.get(payload.userId);
console.log('we got: ',user)
return user;
} catch (err) {
return {};
}
}
class VerifySocialPageUI extends Component{
componentDidMount(){
const {location,history:{push}} = this.props;
// get token from url
const verifyToken = queryString.parse(location.search).token;
if(verifyToken) {
getUserFromJwtTocken(verifyToken)
.then(user=>{
// if user account is new (not yet verified so has verifyToken) redirect to the page that handles auth management
// (wont be included in this gist though)
if(user.verifyToken) {push(`verify?token=${user.verifyToken}`)}
else{
// since this is an old user (with a null verifyToken field and an isVerified flield set to True) store the
// token locally
setJwtTocken(verifyToken).then(()=>console.log('Set JWT in LocalStorage'))
.then(()=>console.log('Now You can login from LocalStorage'))
.then(()=>this.props.onLogin())
.then(()=>console.log('Now You can redirect User to protected area where they can select their role and provide missing details'))
.then(()=>push('/users'))
}
})
}
}
render(){
return <div/>
}
}
VerifySocialPageUI.propTypes={
location:PropTypes.shape({}).isRequired,
onLogin:PropTypes.func.isRequired,
history:PropTypes.shape({
push:PropTypes.func.isRequired
}).isRequired,
}
const mapDispatchToProps = (dispatch) => ({
// Sign in with credentials
onLogin: () =>dispatch(session.authenticate())
});
const VerifySocialPage=compose(
withRouter,
connect(
null,
mapDispatchToProps
),
)(VerifySocialPageUI)
export default VerifySocialPage;
/** One more important thing to look at is how we ensure that a user is verified before authenticating them
for that reason we look at our services description last
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment