WIP login page and authentication code
This commit is contained in:
34
interface/src/authentication/AuthenticatedRoute.js
Normal file
34
interface/src/authentication/AuthenticatedRoute.js
Normal file
@ -0,0 +1,34 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Redirect, Route
|
||||
} from "react-router-dom";
|
||||
|
||||
import { withAuthenticationContext } from './Context.js';
|
||||
import * as Authentication from './Authentication';
|
||||
import { withNotifier } from '../components/SnackbarNotification';
|
||||
|
||||
export class AuthenticatedRoute extends React.Component {
|
||||
|
||||
render() {
|
||||
const { raiseNotification, authenticationContext, component: Component, ...rest } = this.props;
|
||||
const { location } = this.props;
|
||||
const renderComponent = (props) => {
|
||||
if (authenticationContext.jwt) {
|
||||
return (
|
||||
<Component {...props} />
|
||||
);
|
||||
}
|
||||
Authentication.storeLoginRedirect(location);
|
||||
raiseNotification("Please log in to continue.");
|
||||
return (
|
||||
<Redirect to='/' />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Route {...rest} render={renderComponent} />
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withNotifier(withAuthenticationContext(AuthenticatedRoute));
|
56
interface/src/authentication/Authentication.js
Normal file
56
interface/src/authentication/Authentication.js
Normal file
@ -0,0 +1,56 @@
|
||||
import history from '../history';
|
||||
|
||||
export const ACCESS_TOKEN = 'access_token';
|
||||
export const LOGIN_PATHNAME = 'loginPathname';
|
||||
export const LOGIN_SEARCH = 'loginSearch';
|
||||
|
||||
export function storeLoginRedirect(location) {
|
||||
if (location) {
|
||||
localStorage.setItem(LOGIN_PATHNAME, location.pathname);
|
||||
localStorage.setItem(LOGIN_SEARCH, location.search);
|
||||
}
|
||||
}
|
||||
|
||||
export function clearLoginRedirect() {
|
||||
localStorage.removeItem(LOGIN_PATHNAME);
|
||||
localStorage.removeItem(LOGIN_SEARCH);
|
||||
}
|
||||
|
||||
export function fetchLoginRedirect() {
|
||||
const loginPathname = localStorage.getItem(LOGIN_PATHNAME);
|
||||
const loginSearch = localStorage.getItem(LOGIN_SEARCH);
|
||||
clearLoginRedirect();
|
||||
return {
|
||||
pathname: loginPathname || "/",
|
||||
search: (loginPathname && loginSearch) || undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the normal fetch routene with one with provides the access token if present.
|
||||
*/
|
||||
export function secureFetch(url, params) {
|
||||
if (localStorage.getItem(ACCESS_TOKEN)) {
|
||||
params = params || {};
|
||||
params.headers = params.headers || new Headers();
|
||||
params.headers.Authorization = 'Bearer ' + localStorage.getItem(ACCESS_TOKEN)
|
||||
}
|
||||
return fetch(url, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the normal fetch routene which redirects on 401 response.
|
||||
*/
|
||||
export function redirectingSecureFetch(url, params) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
secureFetch(url, params).then(response => {
|
||||
if (response.status === 401) {
|
||||
history.go("/");
|
||||
} else {
|
||||
resolve(response);
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
91
interface/src/authentication/AuthenticationWrapper.js
Normal file
91
interface/src/authentication/AuthenticationWrapper.js
Normal file
@ -0,0 +1,91 @@
|
||||
import * as React from 'react';
|
||||
import history from '../history'
|
||||
import { withNotifier } from '../components/SnackbarNotification';
|
||||
|
||||
import { ACCESS_TOKEN } from './Authentication';
|
||||
import { AuthenticationContext } from './Context';
|
||||
import jwtDecode from 'jwt-decode';
|
||||
|
||||
class AuthenticationWrapper extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.refresh = this.refresh.bind(this);
|
||||
this.signIn = this.signIn.bind(this);
|
||||
this.signOut = this.signOut.bind(this);
|
||||
this.state = {
|
||||
context: {
|
||||
refresh: this.refresh,
|
||||
signIn: this.signIn,
|
||||
signOut: this.signOut
|
||||
},
|
||||
initialized: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.state.initialized ? this.renderContent() : this.renderContentLoading()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
return (
|
||||
<AuthenticationContext.Provider value={this.state.context}>
|
||||
{this.props.children}
|
||||
</AuthenticationContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
renderContentLoading() {
|
||||
return (
|
||||
<div>THIS IS WHERE THE LOADING MESSAGE GOES</div>
|
||||
);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
var accessToken = localStorage.getItem(ACCESS_TOKEN);
|
||||
if (accessToken) {
|
||||
try {
|
||||
this.setState({ initialized: true, context: { ...this.state.context, jwt: jwtDecode(accessToken) } });
|
||||
} catch (err) {
|
||||
localStorage.removeItem(ACCESS_TOKEN);
|
||||
this.props.raiseNotification("Please log in again.");
|
||||
history.push('/');
|
||||
}
|
||||
} else {
|
||||
this.setState({ initialized: true });
|
||||
}
|
||||
}
|
||||
|
||||
signIn(accessToken) {
|
||||
try {
|
||||
this.setState({ context: { ...this.state.context, jwt: jwtDecode(accessToken) } });
|
||||
localStorage.setItem(ACCESS_TOKEN, accessToken);
|
||||
} catch (err) {
|
||||
this.props.raiseNotification("JWT did not parse.");
|
||||
history.push('/');
|
||||
}
|
||||
}
|
||||
|
||||
signOut() {
|
||||
localStorage.removeItem(ACCESS_TOKEN);
|
||||
this.setState({
|
||||
context: {
|
||||
...this.state.context,
|
||||
me: undefined
|
||||
}
|
||||
});
|
||||
this.props.raiseNotification("You have signed out.");
|
||||
history.push('/');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withNotifier(AuthenticationWrapper)
|
15
interface/src/authentication/Context.js
Normal file
15
interface/src/authentication/Context.js
Normal file
@ -0,0 +1,15 @@
|
||||
import * as React from "react";
|
||||
|
||||
export const AuthenticationContext = React.createContext(
|
||||
{}
|
||||
);
|
||||
|
||||
export function withAuthenticationContext(Component) {
|
||||
return function AuthenticationContextComponent(props) {
|
||||
return (
|
||||
<AuthenticationContext.Consumer>
|
||||
{authenticationContext => <Component {...props} authenticationContext={authenticationContext} />}
|
||||
</AuthenticationContext.Consumer>
|
||||
);
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user