Skip to content

Commit

Permalink
Merge pull request #234 from olinlibrary/osteele/oauth
Browse files Browse the repository at this point in the history
Enable OAuth sign-in
  • Loading branch information
osteele committed May 15, 2018
2 parents 241caa7 + 5000360 commit 32c651b
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 12 deletions.
19 changes: 19 additions & 0 deletions src/__test__/auth.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { removeOauthParams } from '../data/auth';

describe('removeOauthParams', () => {
test('removes parameters', () => {
expect(removeOauthParams('http://localhost/path')).toEqual('http://localhost/path');
expect(removeOauthParams('http://localhost/path?access_token=value')).toEqual('http://localhost/path');
expect(removeOauthParams('http://localhost/path?expires_in=value')).toEqual('http://localhost/path');
expect(removeOauthParams('http://localhost/path?state=value')).toEqual('http://localhost/path');
expect(removeOauthParams('http://localhost/path?token_type=value')).toEqual('http://localhost/path');
expect(removeOauthParams('http://localhost/path?other_param=value')).toEqual('http://localhost/path?other_param=value');
expect(removeOauthParams('http://localhost/path?access_token=value&state=1')).toEqual('http://localhost/path');
expect(removeOauthParams('http://localhost/path?access_token=value&token_type=value')).toEqual('http://localhost/path');
expect(removeOauthParams('http://localhost/path?access_token=value&other_param=1&token_type=value')).toEqual('http://localhost/path?other_param=1');
expect(removeOauthParams('http://localhost/path?access_token=value&other_param=1')).toEqual('http://localhost/path?other_param=1');
expect(removeOauthParams('http://localhost/path?other_param=1&access_token=value')).toEqual('http://localhost/path?other_param=1');
expect(removeOauthParams('http://localhost/path?access_token=value&other_param=1&state=2')).toEqual('http://localhost/path?other_param=1');
expect(removeOauthParams('http://localhost/path?access_token=value&state=2&other_param=1')).toEqual('http://localhost/path?other_param=1');
});
});
3 changes: 3 additions & 0 deletions src/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Route, Router, Switch } from 'react-router';
import { initializeAccessToken } from './data/auth';
import AddEditContainer from './containers/add-edit-container';
import CalendarContainer from './containers/calendar-container';
import ViewEventContainer from './containers/event-details-container';
Expand All @@ -26,6 +27,8 @@ const history = createHistory();
// Set up the Redux store
const store = setupStore(history);

initializeAccessToken();

// Fetch the labels
store.dispatch(fetchAccount());
store.dispatch(fetchLabels());
Expand Down
17 changes: 11 additions & 6 deletions src/data/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import axios from 'axios';
import moment from 'moment';
import ReactGA from 'react-ga';
import { push } from 'react-router-redux';
import { setAccessTokenFromResponse } from './auth';
import { decodeEvent } from './encoding';

export const ActionTypes = {
Expand Down Expand Up @@ -228,8 +229,10 @@ export function setViewMode(mode) {
*/
export function fetchAccount() {
return dispatch =>
fetch(`${window.abe_url}/account`)
.then(response => response.json(), error => dispatch(displayError(error)))
axios
.get(`${window.abe_url}/account/`)
.then(setAccessTokenFromResponse)
.then(response => response.data, error => dispatch(displayError(error)))
.then(account => dispatch(setAccount(account)));
}

Expand Down Expand Up @@ -297,8 +300,9 @@ export function refreshEvents(start, end) {
return (dispatch) => {
const startString = `${start.year()}-${start.month() + 1}-${start.date()}`;
const endString = `${end.year()}-${end.month() + 1}-${end.date()}`;
return fetch(`${window.abe_url}/events/?start=${startString}&end=${endString}`)
.then(response => response.json(), error => dispatch(displayError(error)))
return axios
.get(`${window.abe_url}/events/?start=${startString}&end=${endString}`)
.then(response => response.data, error => dispatch(displayError(error)))
.then((data) => {
const events = data.map(decodeEvent);
dispatch(setEvents(events));
Expand Down Expand Up @@ -495,8 +499,9 @@ export function refreshLabelsIfNeeded() {
*/
export function fetchLabels() {
return dispatch =>
fetch(`${window.abe_url}/labels/`)
.then(response => response.json(), error => dispatch(displayError(error)))
axios
.get(`${window.abe_url}/labels/`)
.then(response => response.data, error => dispatch(displayError(error)))
.then(labels => dispatch(setLabels(labels)));
}

Expand Down
47 changes: 47 additions & 0 deletions src/data/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import axios from 'axios';

const ACCESS_TOKEN_KEY = 'abeAccessToken';

// axios.interceptors.request.use((config) => {
// if (accessToken) {
// // eslint-disable-next-line no-param-reassign
// config.headers.common.Authorization = `Bearer ${accessToken}`;
// }
// return config;
// }, Promise.reject);

export function setAccessToken(accessToken) {
localStorage[ACCESS_TOKEN_KEY] = accessToken;
axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
}

export function clearAccessToken() {
localStorage.removeItem(ACCESS_TOKEN_KEY);
axios.defaults.headers.common.Authorization = null;
}

export function removeOauthParams(url) {
return url
.replace(/([?&])(access_token|expires_in|state|token_type)=[^&]*/g, '$1')
.replace(/([?&])&+/, '$1')
.replace(/[?&]$/, '');
}

export function initializeAccessToken() {
const token = localStorage[ACCESS_TOKEN_KEY];
const match = document.location.search.match(/[&?]access_token=([^&]+)/);
if (match) {
setAccessToken(decodeURIComponent(match[1]));
window.history.replaceState({}, document.title, removeOauthParams(window.location.href));
} else if (token) {
setAccessToken(token);
}
}

export function setAccessTokenFromResponse(response) {
const token = response.headers['access-token'];
if (token) {
setAccessToken(token);
}
return response;
}
17 changes: 11 additions & 6 deletions src/sidebar/sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ const Sidebar = (props) => {
<p>You are viewing the public calendar.</p>
<p>
<a
href="https://github.com/olinlibrary/abe-web/wiki/Calendar-Access#view-olin-community-events"
target="_black"
href={`${window.abe_url}/oauth/authorize?redirect_uri=${encodeURIComponent(window.location.href)}`}
>
How do I see Olin Community events?
</a>
Sign in
</a>{' '}
to view and add Olin Community events.
</p>
</div>
)}
Expand All @@ -45,7 +45,9 @@ const Sidebar = (props) => {
)}

{mode.EVENT_ACTIONS &&
permissions.has('edit_events') && <EventActionsPane key="event-actions" className="sidebar-item" {...props} />}
permissions.has('edit_events') && (
<EventActionsPane key="event-actions" className="sidebar-item" {...props} />
)}

{mode.EVENT_LABELS_PANE && (
<SidebarItemContainer key="event-labels" header="Labels">
Expand Down Expand Up @@ -83,7 +85,10 @@ const Sidebar = (props) => {
return (
<div className={sidebarClasses}>
<div className="sidebar-container">
<SidebarHeader homeClicked={props.homeClicked} toggleSidebarCollapsed={props.toggleSidebarCollapsed} />
<SidebarHeader
homeClicked={props.homeClicked}
toggleSidebarCollapsed={props.toggleSidebarCollapsed}
/>
<div className="sidebar-content">{content}</div>
<Footer class="sidebar-footer" />
</div>
Expand Down

0 comments on commit 32c651b

Please sign in to comment.