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

Enable oauth2 authorization #733

Merged
merged 4 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config-overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
}
},
{
urlPattern: /s3.amazonaws.com\/mapbox\/real-changesets\/production\//,
urlPattern: /real-changesets.s3.us-west-2.amazonaws.com\//,
Fixed Show fixed Hide fixed
handler: 'cacheFirst',
options: {
cache: {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "osmcha-frontend",
"version": "0.87.5",
"version": "0.88.0",
"license": "ISC",
"engines": {
"node": ">=7.0"
Expand Down Expand Up @@ -70,7 +70,7 @@
"scripts": {
"precommit": "lint-staged",
"flow": "flow",
"start": "HTTPS=true react-scripts start",
"start": "react-scripts start",
"lint": "eslint src",
"test": "npm run lint && react-scripts test --env=jsdom",
"coverage": "react-scripts test --env=jsdom --coverage",
Expand Down
9 changes: 0 additions & 9 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,6 @@
<link href="https://api.mapbox.com/mapbox-assembly/v0.21.0/assembly.min.css" rel="stylesheet">
<script async defer src="https://api.mapbox.com/mapbox-assembly/v0.21.0/assembly.js"></script>
<script type="text/javascript">
// Redirect to HTTPS
if (location.protocol == 'http:') {
location.href = location.href.replace(/^http:/, 'https:');
}
// Redirect to non-WWW
if (window.location.hostname.indexOf("www") == 0) {
window.location = window.location.href.replace("www.", "");
}

// Single Page Apps for GitHub Pages
// https://github.com/rafrex/spa-github-pages
// Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
Expand Down
42 changes: 0 additions & 42 deletions public/local-landing.html

This file was deleted.

13 changes: 0 additions & 13 deletions public/oauth-landing.html

This file was deleted.

2 changes: 2 additions & 0 deletions src/AppDesktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { CMap } from './views/map';
import { NavbarChangeset } from './views/navbar_changeset';
import { NavbarSidebar } from './views/navbar_sidebar';
import { Home } from './views/home';
import { Authorized } from './views/authorized';
import { Modal } from './views/modal';
import { User } from './views/user';
import { SavedFilters } from './views/saved_filters';
Expand Down Expand Up @@ -60,6 +61,7 @@ export const AppDesktop = () => {
<Route path="/saved-filters" component={SavedFilters} />
<Route path="/trusted-users" component={TrustedUsers} />
<Route path="/watchlist" component={Watchlist} />
<Route path="/authorized" component={Authorized} />
</CSSTransitionGroup>
)}
/>
Expand Down
3 changes: 2 additions & 1 deletion src/AppMobile.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ChangesetsList } from './views/changesets_list';
import { CMap } from './views/map';
import { NavbarChangeset } from './views/navbar_changeset';
import { NavbarSidebar } from './views/navbar_sidebar';
// import { Home } from './views/home';
import { Authorized } from './views/authorized';
import { Modal } from './views/modal';
import { User } from './views/user';
import { SavedFilters } from './views/saved_filters';
Expand Down Expand Up @@ -41,6 +41,7 @@ export const AppMobile = () => {
<Route path="/saved-filters" component={SavedFilters} />
<Route path="/trusted-users" component={TrustedUsers} />
<Route path="/watchlist" component={Watchlist} />
<Route path="/authorized" component={Authorized} />
<Route exact path="/teams" component={MappingTeams} />
<Route path={'/teams/:id'} component={EditMappingTeam} />
</div>
Expand Down
43 changes: 8 additions & 35 deletions src/components/changeset/sign_in_button.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,19 @@
import React from 'react';
import { connect } from 'react-redux';

import { createPopup } from '../../utils/create_popup';
import { handlePopupCallback } from '../../utils/handle_popup_callback';
import { osmAuthUrl } from '../../config/constants';
import { isDev, isLocal } from '../../config';
import type { RootStateType } from '../../store';
import { getFinalToken } from '../../store/auth_actions';
import { getChangesetsPage } from '../../store/changesets_page_actions';
import { getChangeset } from '../../store/changeset_actions';
import { getAuthUrl } from '../../network/auth';

class SignInButton extends React.PureComponent {
props: {
text: string,
oAuthToken: ?string,
pageIndex: number,
getFinalToken: string => mixed,
getChangeset: string => mixed,
getChangesetsPage: string => mixed
pageIndex: number
};

handleLoginClick = () => {
var oAuthToken = this.props.oAuthToken;
if (!oAuthToken) return;

let url;
if (isDev || isLocal) {
url = `/local-landing.html#${oAuthToken}`;
} else {
url = `${osmAuthUrl}?oauth_token=${oAuthToken}`;
}

createPopup('oauth_popup', url);
handlePopupCallback().then(oAuthObj => {
this.props.getFinalToken(oAuthObj.oauth_verifier);
getAuthUrl().then(res => {
window.location.assign(res.auth_url);
});
};
render() {
Expand All @@ -54,16 +34,9 @@ class SignInButton extends React.PureComponent {
}
}

SignInButton = connect(
(state: RootStateType, props) => ({
oAuthToken: state.auth.get('oAuthToken'),
pageIndex: state.changesetsPage.get('pageIndex') || 0
}),
{
getFinalToken,
getChangeset,
getChangesetsPage
}
)(SignInButton);
SignInButton = connect((state: RootStateType, props) => ({
oAuthToken: state.auth.get('oAuthToken'),
pageIndex: state.changesetsPage.get('pageIndex') || 0
}))(SignInButton);

export { SignInButton };
1 change: 0 additions & 1 deletion src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export const osmchaUrl = API_URL.replace('api/v1', '');

export const osmUrl =
process.env.REACT_APP_OSM_URL || 'https://www.openstreetmap.org';
export const osmAuthUrl = `${osmUrl}/oauth/authorize`;
export const isOfficialOSM = osmUrl === 'https://www.openstreetmap.org';
export const apiOSM =
process.env.REACT_APP_OSM_API || 'https://api.openstreetmap.org/api/0.6';
Expand Down
27 changes: 11 additions & 16 deletions src/network/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,11 @@ import { osmchaSocialTokenUrl } from '../config/constants';
import { API_URL } from '../config';
import { handleErrors } from './aoi';

export function postFinalTokensOSMCha(
oauth_token: string,
oauth_token_secret: string,
oauth_verifier: string
) {
export function postFinalTokensOSMCha(code: string) {
return request
.post(osmchaSocialTokenUrl)
.type('form')
.send({ oauth_token: oauth_token })
.send({ oauth_verifier: oauth_verifier })
.send({ oauth_token_secret: oauth_token_secret })
.send({ code: code })
.then(r => {
return r.body;
})
Expand All @@ -23,14 +17,15 @@ export function postFinalTokensOSMCha(
return Promise.reject(e);
});
}
export function postTokensOSMCha() {
return request
.post(osmchaSocialTokenUrl)
.type('form')
.then(r => r.body)
.catch(e => {
console.error(e);
return Promise.reject(e);

export function getAuthUrl() {
return fetch(`${API_URL}/social-auth/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
.then(handleErrors)
.then(res => {
return res.json();
});
}

Expand Down
44 changes: 21 additions & 23 deletions src/store/auth_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { push } from 'react-router-redux';
import { fromJS } from 'immutable';

import {
postTokensOSMCha,
postFinalTokensOSMCha,
fetchUserDetails,
updateUserDetails
Expand All @@ -24,7 +23,6 @@ import type { RootStateType } from './';

export const AUTH = {
postSocialToken: 'AUTH_POST_SOCIAL_TOKEN',
saveOAuth: 'AUTH_SAVE_OAUTH_OBJ',
getFinalToken: 'AUTH_GET_FINAL_TOKEN',
saveToken: 'AUTH_SAVE_TOKEN',
logout: 'AUTH_LOGOUT',
Expand All @@ -43,8 +41,8 @@ export function action(type: string, payload: ?Object) {
// starting point for react component to start fetch
export const getOAuthToken = () => action(AUTH.postSocialToken);

export const getFinalToken = (oauth_verifier: string) =>
action(AUTH.getFinalToken, { oauth_verifier });
export const getFinalToken = (code: string) =>
action(AUTH.getFinalToken, { code });

export const logUserOut = () => action(AUTH.logout);

Expand Down Expand Up @@ -150,8 +148,6 @@ export function* watchAuth(): any {

export function* logoutFlow(): any {
yield call(removeItem, 'token');
yield call(removeItem, 'oauth_token');
yield call(removeItem, 'oauth_token_secret');
yield put(action(AUTH.clearSession));
yield put(action(TRUSTEDLIST.clear));
// get CHANGESET_PAGE without user metadata
Expand All @@ -170,34 +166,36 @@ export function* logoutFlow(): any {
}

export function* authTokenFlow(): any {
const { oauth_token, oauth_token_secret } = yield call(postTokensOSMCha);
yield put(
action(AUTH.saveOAuth, {
oauth_token,
oauth_token_secret
})
);
console.log('Authorization flow started');
// yield take(ACTION) waits for the particular action
// to emit and resume the flow. next in action would
// be to wait for the action `GET_FINAL_TOKEN`
// and resume the flow
const { oauth_verifier } = yield take(AUTH.getFinalToken);
const { token } = yield call(
postFinalTokensOSMCha,
oauth_token,
oauth_token_secret,
oauth_verifier
const { code } = yield take(AUTH.getFinalToken);
yield put(
modal({
kind: 'warning',
title: 'Login in progress',
description: 'Please, wait. We are logging you in.',
autoDismiss: 1
})
);
const { token } = yield call(postFinalTokensOSMCha, code);
if (!token || token === '') {
throw new Error('invalid token');
}
yield put(
modal({
kind: 'success',
title: 'Successful authentication',
description: 'You are now logged in!',
autoDismiss: 2
})
);
yield call(setItem, 'token', token);
yield call(setItem, 'oauth_token', oauth_token);
yield call(setItem, 'oauth_token_secret', oauth_token_secret);
yield put(
action(AUTH.saveToken, {
token,
oauth_verifier
token
})
);
return token;
Expand Down
6 changes: 0 additions & 6 deletions src/store/auth_reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ export function authReducer(
action: Object
): AuthType {
switch (action.type) {
case AUTH.saveOAuth: {
return state
.set('oAuthToken', action.oauth_token)
.set('oAuthTokenSecret', action.oauth_token_secret)
.set('error', null);
}
case AUTH.saveToken: {
return state.set('token', action.token).set('error', null);
}
Expand Down
20 changes: 12 additions & 8 deletions src/store/changesets_page_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,25 @@ export function* fetchChangesetsPageSaga({
})
);
} catch (error) {
const token = yield select(tokenSelector);
yield put(
action(CHANGESETS_PAGE.error, {
pageIndex: oldPageIndex,
error
})
);
error.name = `Failed to load page ${pageIndex}`;
yield put(
modal({
error,
callback: action,
callbackLabel: 'Retry',
callbackArgs: [CHANGESETS_PAGE.fetch, { pageIndex }]
})
);
if (token) {
yield put(
modal({
error,
callback: action,
callbackLabel: 'Retry',
callbackArgs: [CHANGESETS_PAGE.fetch, { pageIndex }],
autoDismiss: 1
})
);
}
}
}

Expand Down
Loading
Loading