Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

authentication flow #59

Merged
merged 11 commits into from
Jul 3, 2017
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ npm-debug.log*

dev.sqlite3

/config/development*
/config/production*

/browserify-cache.json
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ Anything that a developer working on Cobuy should know about.

TODO organize all the miscy mushy magic

### How to get private development config

Our development config is stored in a private repository:

```shell
cd ../
git clone git@github.com:Enspiral-Pods-Swarm/cobuy-config
cp cobuy-config/*.js cobuy/config
```

### After deploy: migrate on heroku!

```shell
Expand Down
7 changes: 4 additions & 3 deletions agents/components/LogOut.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import React from 'react'
import FlatButton from 'material-ui/FlatButton'
import { connect as connectFela } from 'react-fela'
import { flow } from 'lodash'
import { pipe } from 'ramda'

import styles from '../styles/LogOut'

function LogOut (props) {
const { styles } = props
const { styles, actions } = props
return (
<FlatButton
className={styles.container}
backgroundColor='#ddd'
onClick={actions.authentication.logOut}
>
Log Out
</FlatButton>
)
}

export default flow(
export default pipe(
connectFela(styles)
)(LogOut)
66 changes: 25 additions & 41 deletions agents/components/Register.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,20 @@ import PropTypes from 'prop-types'
import React from 'react'
import { connect as connectFela } from 'react-fela'
import { Field, reduxForm as connectForm } from 'redux-form'
import { map, flow } from 'lodash'
import { mapObjIndexed, pipe } from 'ramda'
import { TextField } from 'redux-form-material-ui'
import FlatButton from 'material-ui/FlatButton'
import RaisedButton from 'material-ui/RaisedButton'
import FontIcon from 'material-ui/FontIcon'
import { required, email, length, confirmation } from 'redux-form-validators'

import styles from '../styles/Register'
import RemoteAuthenticationMethods from './RemoteAuthenticationButtons'

// https://blog.codinghorror.com/the-god-login/

const remoteAuthenticationMethods = [
{
label: 'Google',
icon: 'fa fa-google',
backgroundColor: '#ffffff'
},
{
label: 'Facebook',
icon: 'fa fa-facebook',
backgroundColor: '#3b5998'
},
{
label: 'Twitter',
icon: 'fa fa-twitter',
backgroundColor: '#00bced'
},
{
label: 'GitHub',
icon: 'fa fa-github',
backgroundColor: '#6d6d6d'
}
]

function LocalAuthenticationForm (props) {
const { styles, handleSubmit } = props
const { styles, handleSubmit, navigateToSignIn } = props

return (
<form onSubmit={handleSubmit} className={styles.form}>
Expand Down Expand Up @@ -74,52 +52,58 @@ function LocalAuthenticationForm (props) {
/>
<div className={styles.actions}>
<RaisedButton
type='submit'
label='Create new account'
primary={true}
className={styles.registerAction}
/>
<FlatButton
label='Sign In'
className={styles.signInAction}
onClick={navigateToSignIn}
/>
</div>
</form>
)
}

LocalAuthenticationForm = flow(
LocalAuthenticationForm = pipe(
connectForm({
form: 'localAuthenticationForm'
})
)(LocalAuthenticationForm)


function Register (props) {
const { styles } = props
const { styles, error, actions } = props

return (
<div className={styles.container}>
<p className={styles.intro}>
Hey, welcome to Cobuy!
</p>
<ul className={styles.remotes}>
{map(remoteAuthenticationMethods, method => (
<li
className={styles.remote}
>
<RaisedButton
label={method.label}
icon={<FontIcon className={method.icon} />}
backgroundColor={method.backgroundColor}
hoverColor={method.hoverColor}
fullWidth={true}
/>
</li>
))}
<RemoteAuthenticationMethods
styles={styles}
signIn={actions.authentication.signIn}
/>
</ul>
{error && (
<div className={styles.error}>
{error.message}
</div>
)}
<LocalAuthenticationForm
styles={styles}
onSubmit={actions.authentication.register}
navigateToSignIn={navigateToSignIn}
/>
</div>
)

function navigateToSignIn () {
actions.router.push('/sign-in')
}
}

Register.propTypes = {
Expand All @@ -128,6 +112,6 @@ Register.propTypes = {
Register.defaultProps = {
}

export default flow(
export default pipe(
connectFela(styles)
)(Register)
79 changes: 79 additions & 0 deletions agents/components/RemoteAuthenticationButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react'
import RaisedButton from 'material-ui/RaisedButton'
import FontIcon from 'material-ui/FontIcon'

function RemoteAuthenticationMethod (props) {
const {
name,
label,
icon,
backgroundColor,
hoverColor,
signIn
} = props

return (
<RaisedButton
label={label}
icon={<FontIcon className={icon} />}
backgroundColor={backgroundColor}
hoverColor={hoverColor}
fullWidth={true}
onClick={handleClick}
/>
)

function handleClick (ev) {
const url = `/auth/${name}`
const title = `Cobuy sign in with ${name}`

listenSignInPopup(openSignInPopup({ url, title }))
}

function listenSignInPopup (popup) {
if (popup.closed) {
console.log('cancel!')
// cancel()
} else {
let token
try {
token = popup.token
} catch (err) {}
if (token && token.accessToken) {
const { accessToken } = token
signIn({ strategy: 'jwt', accessToken })
popup.close()
} else {
setTimeout(() => listenSignInPopup(popup), 0)
}
}
}
}

export default RemoteAuthenticationMethod

/*
* A helper function that opens the provided URL in a centered popup.
* Accepts an `options` object with `width` and `height` number properties.
*/
// from https://github.com/feathersjs/feathers-authentication-popups/blob/master/src/feathers-authentication-popups.js
function openSignInPopup (options = {}) {
const { url, title } = options
let width = options.width || 1024
let height = options.height || 640
let {top, left} = getCenterCoordinates(window, width, height)
let params = `width=${width}, height=${height}, top=${top}, left=${left}`
return window.open(url, title, params)
}

/*
* Returns the coordinates to center a popup window in the viewport with
* the provided width and height args.
*/
// from https://github.com/feathersjs/feathers-authentication-popups/blob/master/src/feathers-authentication-popups.js
function getCenterCoordinates (window, width, height) {
return {
left: window.screenX + ((window.outerWidth - width) / 2),
top: window.screenY + ((window.outerHeight - height) / 2)
}
}
32 changes: 32 additions & 0 deletions agents/components/RemoteAuthenticationButtons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react'
import { pipe, map, mapObjIndexed, values, mergeAll } from 'ramda'

import RemoteAuthenticationButton from './RemoteAuthenticationButton'

// TODO get this config from the redux store
const remoteAuthenticationMethods = window.config.authentication.remote

function RemoteAuthenticationButtons (allProps) {
const { styles } = allProps

const buttons = pipe(
mapObjIndexed((method, name) => {
const methodProps = mergeAll([method, { name }, allProps])
return <RemoteAuthenticationButton {...methodProps} />
}),
values
)(remoteAuthenticationMethods)

const buttonItems = map(button => (
<li className={styles.remote}>{button}</li>
))

return (
<ul className={styles.remotes}>
{buttonItems(buttons)}
</ul>
)
}


export default RemoteAuthenticationButtons
Loading