Skip to content

Commit

Permalink
feat: add authentication (#2)
Browse files Browse the repository at this point in the history
* feat: begin working on authentication

* chore: finish authentication (for now)

* chore: Switch to querystring because it can be uglified
  • Loading branch information
DSchau committed Apr 18, 2018
1 parent d767421 commit 39538dc
Show file tree
Hide file tree
Showing 13 changed files with 263 additions and 81 deletions.
2 changes: 2 additions & 0 deletions .env.development
@@ -0,0 +1,2 @@
CLIENT_ID='fb701c554c046357a5d8'
CLIENT_SECRET='254558185a4ae5a5083f2d76f069fd1572ed11fe'
2 changes: 2 additions & 0 deletions .env.production
@@ -0,0 +1,2 @@
CLIENT_ID='6cd76d335dc3bf8ae896'
CLIENT_SECRET='a345acffc61ef90ce98b00fc911764e051d9135e'
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -5,6 +5,5 @@ node_modules
yarn-error.log
.env


# Build directory
/public
6 changes: 4 additions & 2 deletions package.json
Expand Up @@ -24,11 +24,13 @@
"gatsby-transformer-yaml": "~1.5.16",
"graphql": "0.11.7",
"graphql-tag": "~2.8.0",
"isomorphic-fetch": "~2.2.1",
"node-fetch": "~2.1.2",
"normalize.css": "~8.0.0",
"react": "~16.2.0",
"querystring": "~0.2.0",
"react": "~16.3.2",
"react-apollo": "~2.1.1",
"react-dom": "~16.2.0",
"react-dom": "~16.3.2",
"react-emotion": "~9.1.0",
"react-helmet": "^5.2.0",
"react-tippy": "~1.2.2",
Expand Down
107 changes: 107 additions & 0 deletions src/components/AuthenticationProvider/AuthenticationProvider.js
@@ -0,0 +1,107 @@
import React, { Component } from 'react';
import qs from 'querystring';
import fetch from 'isomorphic-fetch';

import { getItem, setItem } from '../../util/local-storage';

const AuthenticationContext = React.createContext('authentication');

export class AuthenticationProvider extends Component {
static AUTHENTICATION_KEY = '__SPEAKER_SIGNUP_AUTHENTICATION__';

state = {
authenticated: false,
token: '',
};

componentDidMount() {
const { code } = qs.parse(location.search);
if (code) {
const {
redirectURI,
clientId,
clientSecret,
scope,
} = this.getAuthenticationParams();
const params = qs.stringify({
client_id: clientId,
client_secret: clientSecret,
code,
redirect_uri: redirectURI,
});
/*
* TODO: Fix this is dog shit
*/
fetch(
`https://cors-anywhere.herokuapp.com/https://github.com/login/oauth/access_token?${params}`,
{
method: 'POST',
headers: {
Accept: 'application/json',
},
}
)
.then(response => response.json())
.then(json => {
const { access_token } = json;
if (access_token) {
history.replaceState('', '', location.href.replace(/\?.*/, ''));
this.setState(
{
authenticated: true,
token: access_token,
},
() => {
setItem(AuthenticationProvider.AUTHENTICATION_KEY, this.state);
}
);
}
});
} else {
const stored = getItem(AuthenticationProvider.AUTHENTICATION_KEY);
if (stored.token) {
this.setState({
...stored,
});
}
}
}

getAuthenticationParams = () => {
const redirectURI = location.href;
const isLocalHost = redirectURI.includes('localhost');
const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;
const scope = ['repo'].join(' ');

return {
redirectURI,
clientId,
clientSecret,
scope,
};
};

authenticate = () => {
const { redirectURI, clientId, scope } = this.getAuthenticationParams();
location.replace(
`https://github.com/login/oauth/authorize?client_id=${clientId}&redirect-uri=${redirectURI}&scope=${scope}`
);
};

render() {
const { children } = this.props;
return (
<AuthenticationContext.Provider
value={{
...this.state,
authenticate: this.authenticate,
}}
>
{children}
</AuthenticationContext.Provider>
);
}
}

export const Authentication = AuthenticationContext.Consumer;
1 change: 1 addition & 0 deletions src/components/AuthenticationProvider/index.js
@@ -0,0 +1 @@
export * from './AuthenticationProvider';
18 changes: 9 additions & 9 deletions src/components/Emoji/Emoji.js
@@ -1,7 +1,7 @@
import React from 'react';
import styled from 'react-emotion';

const Container = styled.button(
const EmojiContainer = styled.span(
{
fontSize: 24,
padding: '0.5rem',
Expand All @@ -11,14 +11,6 @@ const Container = styled.button(
outline: 'none',
},
({ interactive, small }) => ({
...(interactive
? {
cursor: 'pointer',
':hover': {
transform: 'scale(1.2)',
},
}
: {}),
...(small
? {
fontSize: 20,
Expand All @@ -27,6 +19,13 @@ const Container = styled.button(
})
);

const EmojiButton = styled(EmojiContainer)({
cursor: 'pointer',
':hover': {
transform: 'scale(1.2)',
},
}).withComponent('button');

const EMOJI_MAP = {
CONFUSED: '😕',
HEART: '❤️',
Expand All @@ -37,6 +36,7 @@ const EMOJI_MAP = {
};

export function Emoji({ ariaLabel, className, name, ...rest }) {
const Container = rest.interactive ? EmojiButton : EmojiContainer;
return (
<Container aria-label={ariaLabel} className={className} {...rest}>
{EMOJI_MAP[name]}
Expand Down
61 changes: 43 additions & 18 deletions src/components/Reaction/Reaction.js
Expand Up @@ -3,6 +3,7 @@ import styled from 'react-emotion';
import { Tooltip } from 'react-tippy';
import 'react-tippy/dist/tippy.css';

import { Authentication } from '..';
import { Trigger } from './Trigger';
import { Emoji } from '..';

Expand All @@ -23,32 +24,56 @@ const ReactionContainer = styled.div({
display: 'flex',
});

const Reactions = ({ list }) => (
const Reactions = ({ list, onReaction }) => (
<ReactionContainer>
{list.map(name => (
<Emoji ariaLabel="Emoji reaction" key={name} name={name} interactive />
<Emoji
ariaLabel="Emoji reaction"
key={name}
name={name}
interactive
onClick={() => onReaction(name)}
/>
))}
</ReactionContainer>
);

export function Reaction({ children, list }) {
return (
<Container>
{children}
<Tooltip
position="bottom"
trigger="click"
html={<Reactions list={list} />}
animation="scale"
duration={150}
theme="light"
interactive
arrow
arrowSize="big"
>
<Trigger />
</Tooltip>
</Container>
<Authentication>
{({ authenticate, authenticated, token }) => (
<Container>
{children}
<Tooltip
position="bottom"
trigger="click"
html={
<Reactions
list={list}
onReaction={reaction => {
if (!authenticated) {
const shouldAuthenticate = confirm(
'Adding a reaction requires logging in. Log in?'
);
if (shouldAuthenticate) {
authenticate();
}
}
}}
/>
}
animation="scale"
duration={150}
theme="light"
interactive
arrow
arrowSize="big"
>
<Trigger />
</Tooltip>
</Container>
)}
</Authentication>
);
}

Expand Down
1 change: 1 addition & 0 deletions src/components/index.js
@@ -1,3 +1,4 @@
export * from './AuthenticationProvider';
export * from './Block';
export * from './Emoji';
export * from './Header';
Expand Down
4 changes: 2 additions & 2 deletions src/layouts/index.js
@@ -1,7 +1,7 @@
import React from 'react';
import Helmet from 'react-helmet';

import { Header } from '../components';
import { AuthenticationProvider, Header } from '../components';
import '../style/global';
import 'normalize.css';

Expand Down Expand Up @@ -30,7 +30,7 @@ export default function IndexLayout({ children, data }) {
paddingTop: 0,
}}
>
{children()}
<AuthenticationProvider>{children()}</AuthenticationProvider>
</div>
</div>
);
Expand Down

0 comments on commit 39538dc

Please sign in to comment.