Merge pull request #354 from majd-asab/feature/add-github-auth

added login page with github authentication option
This commit is contained in:
Stefan Ålund
2020-03-23 08:23:42 +01:00
committed by GitHub
4 changed files with 201 additions and 0 deletions
@@ -18,6 +18,7 @@ import React, { FC, useContext } from 'react';
import PropTypes from 'prop-types';
import { Link, makeStyles, Typography } from '@material-ui/core';
import HomeIcon from '@material-ui/icons/Home';
import AccountCircle from '@material-ui/icons/AccountCircle';
import {
Sidebar,
SidebarPage,
@@ -77,6 +78,7 @@ const Root: FC<{}> = ({ children }) => (
<SidebarSpacer />
<SidebarDivider />
<SidebarItem icon={HomeIcon} to="/" text="Home" />
<SidebarItem icon={AccountCircle} to="/login" text="Login" />
<SidebarDivider />
<SidebarSpace />
</Sidebar>
+5
View File
@@ -25,6 +25,7 @@ import {
SystemIconKey,
defaultSystemIcons,
} from '../../icons';
import LoginPage from './LoginPage';
class AppImpl implements App {
constructor(private readonly systemIcons: SystemIcons) {}
@@ -86,6 +87,10 @@ export default class AppBuilder {
}
}
routes.push(
<Route key="login" path="/login" component={LoginPage} exact />,
);
return () => (
<AppContextProvider app={app}>
<Switch>
@@ -0,0 +1,177 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { FC, useState } from 'react';
import { GitHub as GitHubIcon } from '@material-ui/icons';
import Page from '../../../layout/Page';
import Header from '../../../layout/Header';
import Content from '../../../layout/Content/Content';
import ContentHeader from '../../../layout/ContentHeader/ContentHeader';
import {
Grid,
Typography,
Button,
TextField,
List,
ListItem,
Link
} from '@material-ui/core';
import InfoCard from '../../../layout/InfoCard/InfoCard';
enum AuthType {
GitHub,
}
const LoginPage: FC<{}> = () => {
const [githubUsername, setGithubUsername] = useState(String);
const [githubPersonalAuthToken, setGithubPersonalAuthToken] = useState(String);
const [loginDetails, setLoginDetails] = useState(Object);
const saveGithubInfo = (info: {}) => {
localStorage.setItem('githubLoginDetails', JSON.stringify(info));
setLoginDetails(info);
};
const deleteGithubInfo = () => {
localStorage.removeItem('githubLoginDetails');
setLoginDetails(undefined);
};
const handleTokenRegistration = (event: any) => {
switch (event.target.name) {
case 'github-username-tf':
setGithubUsername(event.target.value);
break;
case 'github-auth-tf':
setGithubPersonalAuthToken(event.target.value);
break;
default:
break;
}
};
const fetchGitHubToken = (username: String, token: String) => {
fetch('https://api.github.com/user', {
headers: new Headers({
Authorization: `Basic ${btoa(`${username}:${token}`)}`,
'Content-Type': 'application/x-www-form-urlencoded',
}),
})
.then((response) => {
if (response.status === 200) return response.json();
throw Error(`${response.status} ${response.statusText}`);
})
.then((data) => {
const info = {
username: username,
token: token,
name: data.name || data.login,
};
saveGithubInfo(info);
})
.catch(() => {});
};
const validateUsernameAndToken = (username: String, token: String) => {
if (username === undefined || username === null || username === '')
return false;
if (token === undefined || token === null || token === '') return false;
return true;
};
const authenticate = (type: AuthType) => {
switch (type) {
case AuthType.GitHub:
{
const username = githubUsername;
const token = githubPersonalAuthToken;
if (validateUsernameAndToken(username, token))
fetchGitHubToken(username, token);
}
break;
default:
break;
}
};
const LoginIndicator = () => {
const ls = localStorage.getItem('githubLoginDetails');
if (ls !== null) {
const obj = ls || loginDetails ? JSON.parse(ls) : loginDetails;
return (
<Typography variant="h6" component="h2">
{`Welcome, ${obj.name}!`}
<br />
<Link onClick={deleteGithubInfo}>Logout</Link>
</Typography>
);
}
return (
<Typography variant="h6" component="h2">
Welcome, guest!
</Typography>
);
};
return (
<Page>
<Header title="Login">
<LoginIndicator />
</Header>
<Content>
<ContentHeader title="Choose a method to authenticate" />
<Grid container>
<Grid item>
<InfoCard>
<Typography variant="h6">
<GitHubIcon /> GitHub
</Typography>
<List>
<ListItem>
<TextField
name="github-username-tf"
label="Username"
onChange={handleTokenRegistration}
/>
</ListItem>
<ListItem>
<TextField
name="github-auth-tf"
label="Token"
onChange={handleTokenRegistration}
/>
</ListItem>
<ListItem>
<Button
variant="outlined"
color="primary"
onClick={() => authenticate(AuthType.GitHub)}
>
Authenticate
</Button>
</ListItem>
</List>
</InfoCard>
</Grid>
</Grid>
</Content>
</Page>
);
};
export default LoginPage;
@@ -0,0 +1,17 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { default } from './LoginPage';