Replace material-UI by material-components-web.

pull/330/head
Clement Michaud 2019-01-26 15:29:12 +01:00
parent 7c6023fee4
commit 317611b099
58 changed files with 881 additions and 768 deletions

View File

@ -0,0 +1,2 @@
REACT_APP_CSP_CONTENT="default-src 'unsafe-inline'; script-src * 'unsafe-inline'; img-src * data:; style-src 'unsafe-inline'; connect-src * 'unsafe-inline' extensions:"

View File

@ -0,0 +1,2 @@
REACT_APP_CSP_CONTENT="default-src 'self'; style-src 'self'; script-src 'self';"

View File

@ -8,6 +8,7 @@ services:
- ./client/tsconfig.json:/usr/app/client/tsconfig.json
- ./client/public:/usr/app/client/public
- ./client/src:/usr/app/client/src
- ./client/.env.development:/usr/app/client/.env.development
networks:
example-network:
aliases:

216
client/package-lock.json generated
View File

@ -1,5 +1,5 @@
{
"name": "client-react",
"name": "authelia-portal",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
@ -890,6 +890,215 @@
"react-is": "^16.6.3"
}
},
"@material/animation": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/animation/-/animation-0.41.0.tgz",
"integrity": "sha512-yYAwJbX3Q2AFd4dr6IYOsWLQy2HN8zWOFVl9AbUXunjzTfJCa/ecfXCriaT6qkmoNoHeTdJHRrsQJZC5GsPvzA=="
},
"@material/base": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/base/-/base-0.41.0.tgz",
"integrity": "sha512-tEyzwBRu3d1H120SfKsDVYZHcqT5lKohh/7cWKR93aAaPDkSvjpKJIjyu2yuSkjpDduVZGzVocYbOvhUKhhzXQ=="
},
"@material/button": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/button/-/button-0.41.0.tgz",
"integrity": "sha512-9mA/7P8yD3YPJ8ijwu0oOiT65OCa8Km3M9OF6VAsBE+XJS9Wo5hWDMgkv16raeOFeXj+1ALsjvuTz31JdcSkgQ==",
"requires": {
"@material/elevation": "^0.41.0",
"@material/ripple": "^0.41.0",
"@material/rtl": "^0.40.1",
"@material/shape": "^0.41.0",
"@material/theme": "^0.41.0",
"@material/typography": "^0.41.0"
}
},
"@material/checkbox": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-0.41.0.tgz",
"integrity": "sha512-Zz6e5WRpziO7Z+4rbEs8GHNNBf1UuttniLp6/RvwPSQRaD8G04sdg4HcP/aDCY1KGMwivkuDPc2Bsgs6j+rD7Q==",
"requires": {
"@material/animation": "^0.41.0",
"@material/base": "^0.41.0",
"@material/ripple": "^0.41.0",
"@material/rtl": "^0.40.1",
"@material/selection-control": "^0.41.0",
"@material/theme": "^0.41.0"
}
},
"@material/elevation": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-0.41.0.tgz",
"integrity": "sha512-ZtZS8z5ie9c7Cx5PVudgSorGYa0C3lu3dA+Nn6qJdhGUokl01msh54NfNuwk+EZsk65bNRRqw1Td/63TCbKIzg==",
"requires": {
"@material/animation": "^0.41.0",
"@material/theme": "^0.41.0"
}
},
"@material/floating-label": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-0.41.0.tgz",
"integrity": "sha512-qI6f1nZU3crXxWAI9fw3U5fHw2qOzEor49EvskbcaV5KSRW5qO+jtfUQ3ib/Vhki7lqhgwNHB/0n7KYhvhjRHQ==",
"requires": {
"@material/animation": "^0.41.0",
"@material/base": "^0.41.0",
"@material/rtl": "^0.40.1",
"@material/theme": "^0.41.0",
"@material/typography": "^0.41.0"
}
},
"@material/line-ripple": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-0.41.0.tgz",
"integrity": "sha512-5DDIoC3d78fCLhNgle7DRFojT3D2SF+XVpUd3g6yLZmybHB7832p4bgl/qGpbIXwk1wAQA1dkUgKH5foxorjNQ==",
"requires": {
"@material/animation": "^0.41.0",
"@material/base": "^0.41.0",
"@material/theme": "^0.41.0"
}
},
"@material/notched-outline": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-0.41.0.tgz",
"integrity": "sha512-nQBkOXvkd5G9FeJ9UuecZh88WRgTsnGVvfj7UFJZEkvkzZwLBGUiJS6fF9FYraih3ZFgmphdbJxXEd9af3cqyQ==",
"requires": {
"@material/animation": "^0.41.0",
"@material/base": "^0.41.0",
"@material/rtl": "^0.40.1",
"@material/shape": "^0.41.0",
"@material/theme": "^0.41.0"
}
},
"@material/react-button": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@material/react-button/-/react-button-0.8.0.tgz",
"integrity": "sha512-j/wk/HK2A3/mb+yFdYCbkWFi6hYvvq4fc4t1M3aRLgdeddMNn/+FHOu014dyKeFW93RrBn1WAdQTwCc31KivuQ==",
"requires": {
"@material/button": "^0.41.0",
"@material/react-ripple": "^0.8.0",
"classnames": "^2.2.5",
"react": "^16.4.2"
}
},
"@material/react-checkbox": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@material/react-checkbox/-/react-checkbox-0.8.0.tgz",
"integrity": "sha512-/7wQcFovUa04/T0MnJOnrbGn21CgxSSWjqkbMPFh/RjET8vya/kqAjhkLw51Mk4q6si+nfHljM6YSFvvXbUBBw==",
"requires": {
"@material/checkbox": "^0.41.0",
"@material/react-ripple": "^0.8.0",
"classnames": "^2.2.5",
"react": "^16.3.2"
}
},
"@material/react-floating-label": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@material/react-floating-label/-/react-floating-label-0.8.0.tgz",
"integrity": "sha512-Bfa+/4nLFu6YE0pAB1wG+vxwYiFvHcOKIq7uAB/IY0/N/ONKM2Jh74/KKqLvNJI608YpZqLAqmZu87R3K15rNQ==",
"requires": {
"@material/floating-label": "^0.41.0",
"classnames": "^2.2.5",
"react": "^16.4.2"
}
},
"@material/react-line-ripple": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@material/react-line-ripple/-/react-line-ripple-0.8.0.tgz",
"integrity": "sha512-s921awUm8taB/kbETGN/vEEZV1WUwtTcsNkQ//HsK2fJW7lKuyVWrObc8ZhMFWljVEUj54ANmRvCke7M4/LSmA==",
"requires": {
"@material/line-ripple": "^0.41.0",
"classnames": "^2.2.5",
"react": "^16.4.2"
}
},
"@material/react-notched-outline": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@material/react-notched-outline/-/react-notched-outline-0.8.0.tgz",
"integrity": "sha512-fQ8hHGOQ3dpk9AuuQqXbCAN2UTuRr5LCztkDxdCwvaPC+oR5meTd8RW7RkJgx/MeiSLe/1/2EMz+9lP+V0rx2w==",
"requires": {
"@material/notched-outline": "^0.41.0",
"classnames": "^2.2.5",
"react": "^16.4.2"
}
},
"@material/react-ripple": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@material/react-ripple/-/react-ripple-0.8.0.tgz",
"integrity": "sha512-ThH9Px42poicRXgFBTfLjbAsVIUi6wmX4RszDPwiSTVMQYc785n240VAD8MfVCR27ykIxCrys/YibqxvREvjUQ==",
"requires": {
"@material/ripple": "^0.41.0",
"classnames": "^2.2.5",
"react": "^16.4.2"
}
},
"@material/react-text-field": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@material/react-text-field/-/react-text-field-0.8.0.tgz",
"integrity": "sha512-eISr4YIBmsIarQQU/LhAPfB9M8srY8Tzagqw6E2z5JyHux8eHlsMWSuYD6eh4FiQekdZoIgoJjlFIGhOvNV59Q==",
"requires": {
"@material/react-floating-label": "^0.8.0",
"@material/react-line-ripple": "^0.8.0",
"@material/react-notched-outline": "^0.8.0",
"@material/textfield": "^0.41.0",
"classnames": "^2.2.5",
"react": "^16.4.2"
}
},
"@material/ripple": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-0.41.0.tgz",
"integrity": "sha512-rxEUVWM4AByDlTCH0kkthZQmUuY6eeN0X6cOHBoioFN2vUDk0D0Nfzz/N9FF2AlAf8C2lDDLrTuqnJPVIn+NHA==",
"requires": {
"@material/animation": "^0.41.0",
"@material/base": "^0.41.0",
"@material/theme": "^0.41.0"
}
},
"@material/rtl": {
"version": "0.40.1",
"resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-0.40.1.tgz",
"integrity": "sha512-Pk6Iw1/KrhWZoZtkDsPMDUW0bm7Z1zeXb3MTQRCFmjf1wU5cRxgOTtuoZLcJqlcKGppLAzJL/TJV3E7KEiuL0A=="
},
"@material/selection-control": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/selection-control/-/selection-control-0.41.0.tgz",
"integrity": "sha512-rRHGiZVPoP4nxAAoeqsgTsxz9GwInGs7HIlEhPfMFygmSZVUHHsuOJXSTpOKYi8GCoKHpB0RKZsAtxM0BYAelw==",
"requires": {
"@material/ripple": "^0.41.0"
}
},
"@material/shape": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/shape/-/shape-0.41.0.tgz",
"integrity": "sha512-k1K3CjOVxclfb/0r28cOa2oJpP7QMA2fP0SOS3Vh8ale5Q2jkaHTL60KX8VkHvV6rMqShpXW+60gPjoz5XRbDQ=="
},
"@material/textfield": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-0.41.0.tgz",
"integrity": "sha512-kJ52W2gxOS2xfpreVhvHQ1u3UkiDl58duw9HkhEkK5Oi1bSDOtbnlWy0pGTOiAma5ZQgetPNgoa+T0zMBptfnw==",
"requires": {
"@material/animation": "^0.41.0",
"@material/base": "^0.41.0",
"@material/floating-label": "^0.41.0",
"@material/line-ripple": "^0.41.0",
"@material/notched-outline": "^0.41.0",
"@material/ripple": "^0.41.0",
"@material/rtl": "^0.40.1",
"@material/shape": "^0.41.0",
"@material/theme": "^0.41.0",
"@material/typography": "^0.41.0"
}
},
"@material/theme": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/theme/-/theme-0.41.0.tgz",
"integrity": "sha512-ohW2JxObKOWvP34EkIIcrEVtL3g0Gs/T3/MdOsM36euyshY8Jwl1f6fjVUQvVjSpixUtSb30/+ulblF8fTOwBg=="
},
"@material/typography": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@material/typography/-/typography-0.41.0.tgz",
"integrity": "sha512-15dlqSU+9uGcWdg4KXXcmDzTKJPb7/5Z9kmooONb2Laot1uiuntDXQS0yL+U2FYLW5Ros+WVMosDBKFruWx68A=="
},
"@mrmlnc/readdir-enhanced": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@ -16990,6 +17199,11 @@
"resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
"integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw="
},
"utility-types": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.4.1.tgz",
"integrity": "sha512-GyH068kJf7c4vhfqj4aCM5X+TeV8jG2iPFLimpGpnQ7dvKfogZOHfiYxMww/nGEfMm/bEmnVbeZGO1see1OVKg=="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",

View File

@ -1,10 +1,11 @@
{
"name": "client-react",
"name": "authelia-portal",
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "3.7.1",
"@material-ui/icons": "^3.0.1",
"@material/react-button": "^0.8.0",
"@material/react-checkbox": "^0.8.0",
"@material/react-text-field": "^0.8.0",
"@types/classnames": "^2.2.7",
"@types/jss": "^9.5.7",
"@types/node": "^10.12.2",
@ -31,11 +32,12 @@
"redux-thunk": "^2.3.0",
"typesafe-actions": "^3.0.0",
"typescript": "^3.1.6",
"u2f-api": "^1.0.10"
"u2f-api": "^1.0.10",
"utility-types": "^3.4.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"start": "SASS_PATH=./node_modules react-scripts start",
"build": "SASS_PATH=./node_modules react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},

View File

@ -5,7 +5,8 @@
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="noindex, nofollow, nosnippet, noarchive">
<meta name="theme-color" content="#000000">
<meta http-equiv="Content-Security-Policy" content="%REACT_APP_CSP_CONTENT%">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/

View File

@ -9,7 +9,5 @@
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
"display": "standalone"
}

View File

@ -0,0 +1,3 @@
@import "@material/react-button/index.scss";
@import "@material/react-checkbox/index.scss";
@import "@material/react-text-field/index.scss";

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import './App.css';
import './App.scss';
import { Router, Route, Switch } from "react-router-dom";
import { Route, Switch } from "react-router-dom";
import { routes } from './routes/index';
import { createBrowserHistory } from 'history';
import { createStore, applyMiddleware, compose } from 'redux';

View File

@ -1,34 +0,0 @@
import { createStyles, Theme } from "@material-ui/core";
const styles = createStyles((theme: Theme) => ({
container: {
textAlign: 'center',
},
messageContainer: {
fontSize: theme.typography.fontSize,
marginTop: theme.spacing.unit * 2,
marginBottom: theme.spacing.unit * 2,
color: 'green',
display: 'inline-block',
marginLeft: theme.spacing.unit * 2,
textAlign: 'left',
},
successContainer: {
verticalAlign: 'middle',
paddingTop: theme.spacing.unit * 2,
paddingBottom: theme.spacing.unit * 2,
marginTop: theme.spacing.unit * 2,
marginBottom: theme.spacing.unit * 3,
border: '1px solid #8ae48a',
borderRadius: '100px',
},
successLogoContainer: {
display: 'inline-block',
},
logoutButtonContainer: {
marginTop: theme.spacing.unit * 2,
},
}));
export default styles;

View File

@ -1,36 +0,0 @@
import { createStyles, Theme } from "@material-ui/core";
const styles = createStyles((theme: Theme) => ({
fields: {
marginTop: theme.spacing.unit * 2,
},
field: {
paddingBottom: theme.spacing.unit * 2,
},
input: {
width: '100%',
},
buttons: {
'& button': {
width: '100%',
},
},
controls: {
display: 'inline-block',
width: '100%',
fontSize: '0.875rem',
},
rememberMe: {
float: 'left',
fontSize: theme.typography.fontSize * 0.8,
},
resetPassword: {
padding: '12px 0px',
float: 'right',
'& a': {
color: 'black',
},
},
}));
export default styles;

View File

@ -1,20 +0,0 @@
import { createStyles, Theme } from "@material-ui/core";
const styles = createStyles((theme: Theme) => ({
messageOuter: {
position: 'relative',
},
messageInner: {
width: '100%',
},
messageContainer: {
color: 'white',
fontSize: theme.typography.fontSize,
padding: theme.spacing.unit * 2,
border: '1px solid red',
borderRadius: '5px',
backgroundColor: '#ff8d8d',
},
}));
export default styles;

View File

@ -1,69 +0,0 @@
import { createStyles, Theme } from "@material-ui/core";
import { isAbsolute } from "path";
const styles = createStyles((theme: Theme) => ({
container: {
position: 'relative',
},
hello: {},
logout: {},
header: {
fontSize: theme.typography.fontSize * 1.5,
marginBottom: theme.spacing.unit,
position: 'relative',
'& $hello': {
display: 'inline-block',
},
'& $logout': {
position: 'absolute',
bottom: '0px',
right: '0px',
fontSize: theme.typography.fontSize * 0.9,
},
},
body: {
paddingTop: theme.spacing.unit * 2,
paddingBottom: theme.spacing.unit * 2,
paddingLeft: theme.spacing.unit * 2,
paddingRight: theme.spacing.unit * 2,
border: '1px solid #e0e0e0',
borderRadius: '2px',
},
methodName: {
fontSize: theme.typography.fontSize * 1.2,
fontWeight: 'bold',
marginBottom: theme.spacing.unit,
},
methodU2f: {
borderBottom: '1px solid #e0e0e0',
padding: theme.spacing.unit,
},
methodTotp: {
padding: theme.spacing.unit,
paddingTop: theme.spacing.unit * 2,
},
image: {
width: '120px',
},
imageContainer: {
textAlign: 'center',
marginTop: theme.spacing.unit * 2,
marginBottom: theme.spacing.unit * 2,
},
registerDeviceContainer: {
textAlign: 'right',
fontSize: theme.typography.fontSize * 0.8,
},
registerDevice: {},
totpField: {
marginTop: theme.spacing.unit * 2,
width: '100%',
},
totpButton: {
marginTop: theme.spacing.unit * 2,
width: '100%',
}
}));
export default styles;

View File

@ -1,41 +0,0 @@
import { createStyles, Theme } from "@material-ui/core";
const styles = createStyles((theme: Theme) => ({
mainContent: {
width: '440px',
margin: '0 auto',
padding: '50px 0px',
},
frame: {
boxShadow: 'rgba(0,0,0,0.14902) 0px 1px 1px 0px,rgba(0,0,0,0.09804) 0px 1px 2px 0px',
backgroundColor: 'white',
borderRadius: '5px',
padding: '30px 40px',
},
innerFrame: {
width: '100%',
},
title: {
fontSize: '1.4em',
fontWeight: 'bold',
borderBottom: '5px solid ' + theme.palette.primary.main,
display: 'inline-block',
paddingRight: '10px',
paddingBottom: '5px',
},
content: {
paddingTop: theme.spacing.unit * 2,
paddingBottom: theme.spacing.unit,
},
footer: {
marginTop: '10px',
textAlign: 'center',
fontSize: '0.65em',
color: 'grey',
'& a': {
color: 'grey',
}
},
}));
export default styles;

View File

@ -1,33 +0,0 @@
import { createStyles, Theme } from "@material-ui/core";
const styles = createStyles((theme: Theme) => ({
form: {
paddingTop: theme.spacing.unit * 2,
},
field: {
width: '100%',
},
buttonsContainer: {
marginTop: theme.spacing.unit * 2,
width: '100%',
},
buttonContainer: {
width: '50%',
display: 'inline-block',
boxSizing: 'border-box',
},
buttonConfirmContainer: {
paddingRight: theme.spacing.unit / 2,
},
buttonConfirm: {
width: '100%',
},
buttonCancelContainer: {
paddingLeft: theme.spacing.unit / 2,
},
buttonCancel: {
width: '100%',
},
}));
export default styles;

View File

@ -1,55 +0,0 @@
import { createStyles, Theme } from "@material-ui/core";
const borderColor = '#e0e0e0';
const styles = createStyles((theme: Theme) => ({
secretContainer: {
width: '100%',
border: '1px solid ' + borderColor,
marginTop: theme.spacing.unit * 2,
marginBottom: theme.spacing.unit * 2,
},
qrcodeContainer: {
textAlign: 'center',
padding: theme.spacing.unit * 2,
},
base32Container: {
textAlign: 'center',
borderTop: '1px solid ' + borderColor,
padding: theme.spacing.unit,
wordWrap: 'break-word',
},
text: {
textAlign: 'center',
},
needGoogleAuthenticator: {
textAlign: 'center',
marginTop: theme.spacing.unit * 2,
},
needGoogleAuthenticatorText: {
fontSize: theme.typography.fontSize * 0.8,
},
store: {
width: '100px',
marginTop: theme.spacing.unit * 0.5,
marginLeft: theme.spacing.unit * 0.5,
marginRight: theme.spacing.unit * 0.5,
},
buttonContainer: {
textAlign: 'center',
paddingTop: theme.spacing.unit * 2,
},
progressContainer: {
textAlign: 'center',
paddingTop: theme.spacing.unit * 2,
},
button: {
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit,
},
loginButtonContainer: {
textAlign: 'center',
},
}));
export default styles;

View File

@ -1,30 +0,0 @@
import { createStyles, Theme } from "@material-ui/core";
const styles = createStyles((theme: Theme) => ({
form: {
marginTop: theme.spacing.unit * 2,
},
field: {
width: '100%',
marginBottom: theme.spacing.unit * 2,
},
buttonsContainer: {
width: '100%',
},
buttonContainer: {
width: '50%',
boxSizing: 'border-box',
display: 'inline-block',
},
buttonResetContainer: {
paddingRight: theme.spacing.unit / 2,
},
buttonCancelContainer: {
paddingLeft: theme.spacing.unit / 2,
},
button: {
width: '100%',
}
}));
export default styles;

View File

@ -1,19 +0,0 @@
import { createStyles, Theme } from "@material-ui/core";
const styles = createStyles((theme: Theme) => ({
infoContainer: {
marginBottom: theme.spacing.unit * 2,
},
imageContainer: {
textAlign: 'center',
'& img': {
width: '120px',
},
},
retryButtonContainer: {
textAlign: 'center',
paddingTop: theme.spacing.unit * 2,
},
}));
export default styles;

View File

@ -0,0 +1,2 @@
$theme-spacing: 10px;

View File

@ -0,0 +1,24 @@
@import '../../variables.scss';
.container {
text-align: center;
}
.messageContainer {
color: green;
.username {
display: block;
font-size: 1.6em;
font-weight: bold;
}
}
.statusIcon {
margin-top: ($theme-spacing) * 2;
margin-bottom: ($theme-spacing) * 2;
}
.logoutButtonContainer {
margin-top: ($theme-spacing) * 2,
}

View File

@ -0,0 +1,43 @@
@import '../../variables.scss';
.notification {
margin-bottom: ($theme-spacing) * 2;
}
.field {
padding-bottom: ($theme-spacing) * 2;
}
.input {
width: 100%;
}
.buttons button{
width: 100%;
}
.controls {
display: inline-block;
width: 100%;
font-size: 0.875rem;
}
.rememberMe {
float: left;
font-size: 0.8em;
display: flex;
align-items: center;
margin-left: -11px;
}
.resetPassword {
height: 40px;
float: right;
display: flex;
align-items: center;
}
.resetPassword a {
color: black;
font-size: 0.8em;
}

View File

@ -0,0 +1,10 @@
@import '../../variables.scss';
.container {
color: white;
font-size: 0.8em;
padding: ($theme-spacing) * 2;
border: 1px solid red;
border-radius: 5px;
background-color: #ff8d8d;
}

View File

@ -0,0 +1,75 @@
@import '../../variables.scss';
.container {
position: relative,
}
.header {
font-size: 1.5em;
margin-bottom: ($theme-spacing);
position: relative;
.hello {
display: inline-block;
}
.logout {
position: absolute;
bottom: 0px;
right: 0px;
font-size: 0.6em,
}
}
.body {
padding-top: ($theme-spacing) * 2;
padding-bottom: ($theme-spacing) * 2;
padding-left: ($theme-spacing) * 2;
padding-right: ($theme-spacing) * 2;
border: 1px solid #e0e0e0;
border-radius: 2px;
}
.methodName {
font-size: 1.2em;
font-weight: bold;
margin-bottom: ($theme-spacing);
}
.methodU2f {
border-bottom: 1px solid #e0e0e0;
padding: ($theme-spacing);
}
.methodTotp {
padding: ($theme-spacing);
padding-top: ($theme-spacing) * 2;
}
.image {
width: '120px';
}
.imageContainer {
text-align: center;
margin-top: ($theme-spacing) * 2;
margin-bottom: ($theme-spacing) * 2;
}
.registerDeviceContainer {
text-align: right;
font-size: 0.7em;
}
.totpField {
margin-top: ($theme-spacing) * 2;
width: 100%;
}
.totpButton {
margin-top: ($theme-spacing);
button {
width: 100%;
}
}

View File

@ -0,0 +1,40 @@
@import '../../variables.scss';
@import "@material/theme/mdc-theme";
.mainContent {
width: 440px;
margin: auto;
margin-top: 80px;
}
.title {
font-size: 1.4em;
font-weight: bold;
display: inline-block;
padding-bottom: ($theme-spacing);
}
.frame {
box-shadow:
rgba(0,0,0,0.14902) 0px 1px 1px 0px,
rgba(0,0,0,0.09804) 0px 1px 2px 0px;
background-color: white;
border-radius: 5px;
padding: 40px;
border-top: 6px solid ($mdc-theme-primary);
}
.innerFrame {
width: 100%;
}
.footer {
margin-top: 10px;
text-align: center;
font-size: 0.65em;
color: grey;
a:visited {
color: grey;
}
}

View File

@ -0,0 +1,36 @@
@import '../../variables.scss';
.form {
padding-top: ($theme-spacing) * 2;
}
.field {
width: 100%;
}
.buttonsContainer {
margin-top: ($theme-spacing) * 2;
width: 100%;
}
.buttonContainer {
width: 50%;
display: inline-block;
box-sizing: border-box;
}
.buttonConfirmContainer {
padding-right: ($theme-spacing) / 2;
}
.buttonConfirm {
width: 100%;
}
.buttonCancelContainer {
padding-left: ($theme-spacing) / 2;
}
.buttonCancel {
width: 100%;
}

View File

@ -0,0 +1,60 @@
@import '../../variables.scss';
.secretContainer {
width: 100%;
border: 1px solid #dcdcdc;
border-radius: 10px;
margin-top: ($theme-spacing) * 2;
margin-bottom: ($theme-spacing) * 2;
}
.qrcodeContainer {
text-align: center;
padding: ($theme-spacing) * 2;
}
.base32Container {
text-align: center;
border-top: 1px solid #dcdcdc;
padding: ($theme-spacing);
word-wrap: break-word;
}
.text {
text-align: center;
}
.needGoogleAuthenticator {
text-align: center;
margin-top: ($theme-spacing) * 2;
}
.needGoogleAuthenticatorText {
font-size: 0.8em;
}
.store {
width: 100px;
margin-top: ($theme-spacing) * 0.5;
margin-left: ($theme-spacing) * 0.5;
margin-right: ($theme-spacing) * 0.5;
}
.buttonContainer {
text-align: center;
padding-top: ($theme-spacing) * 2;
}
.progressContainer {
text-align: center;
padding-top: ($theme-spacing) * 2,
}
.button {
margin-left: ($theme-spacing);
margin-right: ($theme-spacing);
}
.loginButtonContainer {
text-align: center;
}

View File

@ -0,0 +1,32 @@
@import '../../variables.scss';
.form {
margin-top: ($theme-spacing) * 2,
}
.field {
width: 100%;
margin-bottom: ($theme-spacing) * 2;
}
.buttonsContainer {
width: 100%;
}
.buttonContainer {
width: 50%;
box-sizing: border-box;
display: inline-block;
}
.buttonResetContainer {
padding-right: ($theme-spacing) / 2;
}
.buttonCancelContainer {
padding-left: ($theme-spacing) / 2;
}
.button {
width: 100%;
}

View File

@ -0,0 +1,18 @@
@import '../../variables.scss';
.infoContainer {
margin-bottom: ($theme-spacing) * 2;
}
.imageContainer {
text-align: center;
img {
width: 120px;
}
}
.retryButtonContainer {
text-align: center;
padding-top: ($theme-spacing) * 2;
}

View File

@ -1,7 +1,7 @@
import React, { Component } from "react";
import styles from '../../assets/jss/components/AlreadyAuthenticated/AlreadyAuthenticated';
import { WithStyles, withStyles, Button } from "@material-ui/core";
import styles from '../../assets/scss/components/AlreadyAuthenticated/AlreadyAuthenticated.module.scss';
import Button from "@material/react-button";
import CircleLoader, { Status } from "../CircleLoader/CircleLoader";
export interface OwnProps {
@ -12,26 +12,23 @@ export interface DispatchProps {
onLogoutClicked: () => void;
}
export type Props = OwnProps & DispatchProps & WithStyles;
export type Props = OwnProps & DispatchProps;
class AlreadyAuthenticated extends Component<Props> {
render() {
const { classes } = this.props;
return (
<div className={classes.container}>
<div className={classes.successContainer}>
<CircleLoader status={Status.SUCCESSFUL} />
<span className={classes.messageContainer}>
<b>{this.props.username}</b><br/>
<div className={styles.container}>
<div className={styles.successContainer}>
<div className={styles.messageContainer}>
<span className={styles.username}>{this.props.username}</span>
you are authenticated
</span>
</div>
<div className={styles.statusIcon}><CircleLoader status={Status.SUCCESSFUL} /></div>
</div>
<div>Close this tab or logout</div>
<div className={classes.logoutButtonContainer}>
<div className={styles.logoutButtonContainer}>
<Button
onClick={this.props.onLogoutClicked}
variant="contained"
color="primary">
color="red">
Logout
</Button>
</div>
@ -40,4 +37,4 @@ class AlreadyAuthenticated extends Component<Props> {
}
}
export default withStyles(styles)(AlreadyAuthenticated);
export default AlreadyAuthenticated;

View File

@ -1,7 +1,7 @@
import React, { Component } from "react";
import classnames from 'classnames';
import styles from './CircleLoader.module.scss';
import styles from '../../assets/scss/components/CircleLoader/CircleLoader.module.scss';
export enum Status {
LOADING,

View File

@ -1,19 +1,14 @@
import React, { Component, KeyboardEvent, ChangeEvent } from "react";
import React, { Component, KeyboardEvent, FormEvent } from "react";
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import TextField, {Input} from '@material/react-text-field';
import Button from '@material/react-button';
import Checkbox from '@material/react-checkbox';
import { Link } from "react-router-dom";
import { WithStyles, withStyles } from "@material-ui/core";
import styles from '../../assets/jss/components/FirstFactorForm/FirstFactorForm';
import FormNotification from "../../components/FormNotification/FormNotification";
import Notification from "../../components/Notification/Notification";
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'
import CheckBoxIcon from '@material-ui/icons/CheckBox';
import styles from '../../assets/scss/components/FirstFactorForm/FirstFactorForm.module.scss';
export interface StateProps {
formDisabled: boolean;
@ -24,7 +19,7 @@ export interface DispatchProps {
onAuthenticationRequested(username: string, password: string): void;
}
export type Props = StateProps & DispatchProps & WithStyles;
export type Props = StateProps & DispatchProps;
interface State {
username: string;
@ -48,12 +43,14 @@ class FirstFactorForm extends Component<Props, State> {
})
}
onUsernameChanged = (e: ChangeEvent<HTMLInputElement>) => {
this.setState({username: e.target.value});
onUsernameChanged = (e: FormEvent<HTMLElement>) => {
const val = (e.target as HTMLInputElement).value;
this.setState({username: val});
}
onPasswordChanged = (e: ChangeEvent<HTMLInputElement>) => {
this.setState({password: e.target.value});
onPasswordChanged = (e: FormEvent<HTMLElement>) => {
const val = (e.target as HTMLInputElement).value;
this.setState({password: val});
}
onLoginClicked = () => {
@ -67,63 +64,61 @@ class FirstFactorForm extends Component<Props, State> {
}
render() {
const { classes } = this.props;
return (
<div>
<FormNotification
show={this.props.error != null}>
<Notification
show={this.props.error != null}
className={styles.notification}>
{this.props.error || ''}
</FormNotification>
<div className={classes.fields}>
<div className={classes.field}>
</Notification>
<div className={styles.fields}>
<div className={styles.field}>
<TextField
className={classes.input}
variant="outlined"
id="username"
className={styles.input}
label="Username"
disabled={this.props.formDisabled}
onChange={this.onUsernameChanged}>
outlined={true}>
<Input
id="username"
onChange={this.onUsernameChanged}
disabled={this.props.formDisabled}
value={this.state.username}/>
</TextField>
</div>
<div className={classes.field}>
<div className={styles.field}>
<TextField
className={classes.input}
id="password"
variant="outlined"
className={styles.input}
label="Password"
type="password"
disabled={this.props.formDisabled}
onChange={this.onPasswordChanged}
onKeyPress={this.onPasswordKeyPressed}>
outlined={true}>
<Input
id="password"
type="password"
disabled={this.props.formDisabled}
onChange={this.onPasswordChanged}
onKeyPress={this.onPasswordKeyPressed}
value={this.state.password} />
</TextField>
</div>
</div>
<div>
<div className={classes.buttons}>
<div className={styles.buttons}>
<Button
onClick={this.onLoginClicked}
variant="contained"
color="primary"
raised={true}
disabled={this.props.formDisabled}>
Login
</Button>
</div>
<div className={classes.controls}>
<div className={classes.rememberMe}>
<FormControlLabel
control={
<Checkbox
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
checkedIcon={<CheckBoxIcon fontSize="small" />}
checked={this.state.rememberMe}
onChange={this.toggleRememberMe}
color="primary"
/>
}
label="Remember me"
<div className={styles.controls}>
<div className={styles.rememberMe}>
<Checkbox
nativeControlId='remember-checkbox'
checked={this.state.rememberMe}
onChange={this.toggleRememberMe}
/>
<label htmlFor='remember-checkbox'>Remember me</label>
</div>
<div className={classes.resetPassword}>
<div className={styles.resetPassword}>
<Link to="/forgot-password">Forgot password?</Link>
</div>
</div>
@ -139,4 +134,4 @@ class FirstFactorForm extends Component<Props, State> {
}
}
export default withStyles(styles)(FirstFactorForm);
export default FirstFactorForm;

View File

@ -1,27 +0,0 @@
import React, { Component } from "react";
import { withStyles, WithStyles, Collapse } from "@material-ui/core";
import styles from '../../assets/jss/components/FormNotification/FormNotification';
interface Props extends WithStyles {
show: boolean;
}
class FormNotification extends Component<Props> {
render() {
const { classes } = this.props;
return (
<Collapse in={this.props.show}>
<div className={classes.messageOuter}>
<div className={classes.messageInner}>
<div className={classes.messageContainer}>
{this.props.children}
</div>
</div>
</div>
</Collapse>
)
}
}
export default withStyles(styles)(FormNotification);

View File

@ -0,0 +1,21 @@
import React, { Component } from "react";
import classnames from 'classnames';
import styles from '../../assets/scss/components/Notification/Notification.module.scss';
interface Props {
className?: string;
show: boolean;
}
class Notification extends Component<Props> {
render() {
return (this.props.show)
? (<div className={classnames(styles.container, this.props.className)}>
{this.props.children}
</div>)
: null;
}
}
export default Notification;

View File

@ -1,10 +1,11 @@
import React, { Component, KeyboardEvent, ChangeEvent } from 'react';
import React, { Component, KeyboardEvent, ChangeEvent, FormEvent } from 'react';
import { WithStyles, withStyles, Button, TextField } from '@material-ui/core';
import TextField, { Input } from '@material/react-text-field';
import Button from '@material/react-button';
import styles from '../../assets/jss/components/SecondFactorForm/SecondFactorForm';
import styles from '../../assets/scss/components/SecondFactorForm/SecondFactorForm.module.scss';
import CircleLoader, { Status } from '../../components/CircleLoader/CircleLoader';
import FormNotification from '../FormNotification/FormNotification';
import Notification from '../Notification/Notification';
export interface OwnProps {
username: string;
@ -29,7 +30,7 @@ export interface DispatchProps {
onOneTimePasswordValidationRequested: (token: string) => void;
}
export type Props = OwnProps & StateProps & DispatchProps & WithStyles;
export type Props = OwnProps & StateProps & DispatchProps;
interface State {
oneTimePassword: string;
@ -48,7 +49,6 @@ class SecondFactorView extends Component<Props, State> {
}
private renderU2f(n: number) {
const { classes } = this.props;
let u2fStatus = Status.LOADING;
if (this.props.securityKeyVerified) {
u2fStatus = Status.SUCCESSFUL;
@ -56,14 +56,14 @@ class SecondFactorView extends Component<Props, State> {
u2fStatus = Status.FAILURE;
}
return (
<div className={classes.methodU2f} key='u2f-method'>
<div className={classes.methodName}>Option {n} - Security Key</div>
<div className={styles.methodU2f} key='u2f-method'>
<div className={styles.methodName}>Option {n} - Security Key</div>
<div>Insert your security key into a USB port and touch the gold disk.</div>
<div className={classes.imageContainer}>
<div className={styles.imageContainer}>
<CircleLoader status={u2fStatus}></CircleLoader>
</div>
<div className={classes.registerDeviceContainer}>
<a className={classes.registerDevice} href="#"
<div className={styles.registerDeviceContainer}>
<a className={styles.registerDevice} href="#"
onClick={this.props.onRegisterSecurityKeyClicked}>
Register device
</a>
@ -72,8 +72,8 @@ class SecondFactorView extends Component<Props, State> {
)
}
private onOneTimePasswordChanged = (e: ChangeEvent<HTMLInputElement>) => {
this.setState({oneTimePassword: e.target.value});
private onOneTimePasswordChanged = (e: FormEvent<HTMLElement>) => {
this.setState({oneTimePassword: (e.target as HTMLInputElement).value});
}
private onTotpKeyPressed = (e: KeyboardEvent) => {
@ -88,42 +88,43 @@ class SecondFactorView extends Component<Props, State> {
}
private renderTotp(n: number) {
const { classes } = this.props;
return (
<div className={classes.methodTotp} key='totp-method'>
<div className={classes.methodName}>Option {n} - One-Time Password</div>
<FormNotification show={this.props.oneTimePasswordVerificationError !== null}>
<div className={styles.methodTotp} key='totp-method'>
<div className={styles.methodName}>Option {n} - One-Time Password</div>
<Notification show={this.props.oneTimePasswordVerificationError !== null}>
{this.props.oneTimePasswordVerificationError}
</FormNotification>
</Notification>
<TextField
className={classes.totpField}
name="totp-token"
id="totp-token"
variant="outlined"
className={styles.totpField}
label="One-Time Password"
onChange={this.onOneTimePasswordChanged}
onKeyPress={this.onTotpKeyPressed}>
outlined={true}>
<Input
name="totp-token"
id="totp-token"
onChange={this.onOneTimePasswordChanged as any}
onKeyPress={this.onTotpKeyPressed}
value={this.state.oneTimePassword} />
</TextField>
<div className={classes.registerDeviceContainer}>
<a className={classes.registerDevice} href="#"
<div className={styles.registerDeviceContainer}>
<a className={styles.registerDevice} href="#"
onClick={this.props.onRegisterOneTimePasswordClicked}>
Register device
</a>
</div>
<Button
className={classes.totpButton}
variant="contained"
color="primary"
onClick={this.onOneTimePasswordValidationRequested}
disabled={this.props.oneTimePasswordVerificationInProgress}>
OK
</Button>
<div className={styles.totpButton}>
<Button
color="primary"
raised={true}
onClick={this.onOneTimePasswordValidationRequested}
disabled={this.props.oneTimePasswordVerificationInProgress}>
OK
</Button>
</div>
</div>
)
}
private renderMode() {
const { classes } = this.props;
const methods = [];
let n = 1;
if (this.props.securityKeySupported) {
@ -133,23 +134,22 @@ class SecondFactorView extends Component<Props, State> {
methods.push(this.renderTotp(n));
return (
<div className={classes.methodsContainer}>
<div className={styles.methodsContainer}>
{methods}
</div>
);
}
render() {
const { classes } = this.props;
return (
<div className={classes.container}>
<div className={classes.header}>
<div className={classes.hello}>Hello <b>{this.props.username}</b></div>
<div className={classes.logout}>
<div className={styles.container}>
<div className={styles.header}>
<div className={styles.hello}>Hello <b>{this.props.username}</b></div>
<div className={styles.logout}>
<a onClick={this.props.onLogoutClicked} href="#">Logout</a>
</div>
</div>
<div className={classes.body}>
<div className={styles.body}>
{this.renderMode()}
</div>
</div>
@ -157,4 +157,4 @@ class SecondFactorView extends Component<Props, State> {
}
}
export default withStyles(styles)(SecondFactorView);
export default SecondFactorView;

View File

@ -32,11 +32,14 @@ function onAuthenticationRequested(dispatch: Dispatch) {
return;
}
const json = await res.json();
if ('error' in json) {
await dispatch(authenticateFailure(json['error']));
return;
if (res.status !== 204) {
const json = await res.json();
if ('error' in json) {
await dispatch(authenticateFailure(json['error']));
return;
}
}
dispatch(authenticateSuccess());
// fetch state

View File

@ -1,11 +1,7 @@
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background: #eee;
}

View File

@ -4,11 +4,10 @@ import { Route, Switch, Redirect, RouterProps, RouteProps } from "react-router";
import { routes } from '../../routes/routes';
import { AUTHELIA_GITHUB_URL } from "../../constants";
import { WithStyles, withStyles } from "@material-ui/core";
import styles from '../../assets/jss/layouts/PortalLayout/PortalLayout';
import styles from '../../assets/scss/layouts/PortalLayout/PortalLayout.module.scss';
interface Props extends RouterProps, RouteProps, WithStyles {}
interface Props extends RouterProps, RouteProps {}
class PortalLayout extends Component<Props> {
private renderTitle() {
@ -25,15 +24,14 @@ class PortalLayout extends Component<Props> {
render() {
const { classes } = this.props;
return (
<div className={classes.mainContent}>
<div className={classes.frame}>
<div className={classes.innerFrame}>
<div className={classes.title}>
{this.renderTitle()}
</div>
<div className={classes.content}>
<div className={styles.main}>
<div className={styles.mainContent}>
<div className={styles.title}>
{this.renderTitle()}
</div>
<div className={styles.frame}>
<div className={styles.innerFrame}>
<Switch>
{routes.map((r, key) => {
return <Route path={r.path} component={r.component} exact={true} key={key} />
@ -42,13 +40,13 @@ class PortalLayout extends Component<Props> {
</Switch>
</div>
</div>
</div>
<div className={classes.footer}>
<div>Powered by <a href={AUTHELIA_GITHUB_URL}>Authelia</a></div>
<div className={styles.footer}>
<div><a href={AUTHELIA_GITHUB_URL}>Powered by Authelia</a></div>
</div>
</div>
</div>
)
}
}
export default withStyles(styles)(PortalLayout);
export default PortalLayout;

View File

@ -7,7 +7,7 @@ import AuthenticationView from "../containers/views/AuthenticationView/Authentic
export const routes = [{
path: '/',
title: 'Login',
title: 'Sign in',
component: AuthenticationView,
}, {
path: '/confirmation-sent',
@ -15,11 +15,11 @@ export const routes = [{
component: ConfirmationSentView
}, {
path: '/one-time-password-registration',
title: 'One-time password registration',
title: 'One-time password',
component: OneTimePasswordRegistrationView,
}, {
path: '/security-key-registration',
title: 'Security key registration',
title: 'Security key',
component: SecurityKeyRegistrationView,
}, {
path: '/forgot-password',

View File

@ -3,7 +3,7 @@ import AlreadyAuthenticated from "../../containers/components/AlreadyAuthenticat
import FirstFactorForm from "../../containers/components/FirstFactorForm/FirstFactorForm";
import SecondFactorForm from "../../containers/components/SecondFactorForm/SecondFactorForm";
import RemoteState from "./RemoteState";
import { RouterProps, Redirect } from "react-router";
import { RouterProps } from "react-router";
import queryString from 'query-string';
export enum Stage {

View File

@ -3,8 +3,8 @@ import classnames from 'classnames';
import mail from '../../assets/images/mail.png';
import styles from './ConfirmationSentView.module.css';
import { Button } from "@material-ui/core";
import styles from '../../assets/scss/views/ConfirmationSentView/ConfirmationSentView.module.scss';
import Button from "@material/react-button";
import { RouterProps } from "react-router";
interface Props extends RouterProps {}
@ -22,7 +22,7 @@ class ConfirmationSentView extends Component<Props> {
<Button
onClick={() => this.props.history.goBack()}
className={styles.button}
variant="contained"
raised={true}
color="primary">
Back
</Button>

View File

@ -1,8 +1,9 @@
import React, { Component, ChangeEvent, KeyboardEvent } from "react";
import { TextField, WithStyles, withStyles, Button } from "@material-ui/core";
import React, { Component, KeyboardEvent, FormEvent } from "react";
import classnames from 'classnames';
import Button from "@material/react-button";
import TextField, { Input } from "@material/react-text-field";
import styles from '../../assets/jss/views/ForgotPasswordView/ForgotPasswordView';
import styles from '../../assets/scss/views/ForgotPasswordView/ForgotPasswordView.module.scss';
export interface StateProps {
disabled: boolean;
@ -13,7 +14,7 @@ export interface DispatchProps {
onCancelClicked: () => void;
}
export type Props = StateProps & DispatchProps & WithStyles;
export type Props = StateProps & DispatchProps;
interface State {
username: string;
@ -27,8 +28,8 @@ class ForgotPasswordView extends Component<Props, State> {
}
}
private onUsernameChanged = (e: ChangeEvent<HTMLInputElement>) => {
this.setState({username: e.target.value});
private onUsernameChanged = (e: FormEvent<HTMLElement>) => {
this.setState({username: (e.target as HTMLInputElement).value});
}
private onKeyPressed = (e: KeyboardEvent) => {
@ -43,38 +44,38 @@ class ForgotPasswordView extends Component<Props, State> {
}
render() {
const { classes } = this.props;
return (
<div>
<div>What's your username?</div>
<div className={classes.form}>
<div className={styles.form}>
<TextField
className={classes.field}
variant="outlined"
className={styles.field}
outlined={true}
id="username"
label="Username"
onChange={this.onUsernameChanged}
onKeyPress={this.onKeyPressed}
value={this.state.username}
disabled={this.props.disabled}>
label="Username">
<Input
onChange={this.onUsernameChanged}
onKeyPress={this.onKeyPressed}
value={this.state.username}
disabled={this.props.disabled} />
</TextField>
<div className={classes.buttonsContainer}>
<div className={classnames(classes.buttonContainer, classes.buttonConfirmContainer)}>
<div className={styles.buttonsContainer}>
<div className={classnames(styles.buttonContainer, styles.buttonConfirmContainer)}>
<Button
onClick={this.onPasswordResetRequested}
variant="contained"
color="primary"
className={classes.buttonConfirm}
raised={true}
className={styles.buttonConfirm}
disabled={this.props.disabled}>
Next
</Button>
</div>
<div className={classnames(classes.buttonContainer, classes.buttonCancelContainer)}>
<div className={classnames(styles.buttonContainer, styles.buttonCancelContainer)}>
<Button
onClick={this.props.onCancelClicked}
variant="contained"
color="primary"
className={classes.buttonCancel}>
raised={true}
className={styles.buttonCancel}>
Cancel
</Button>
</div>
@ -85,4 +86,4 @@ class ForgotPasswordView extends Component<Props, State> {
}
}
export default withStyles(styles)(ForgotPasswordView);
export default ForgotPasswordView;

View File

@ -1,19 +1,20 @@
import React, { Component } from "react";
import { WithStyles, withStyles, CircularProgress, Button } from "@material-ui/core";
import Button from "@material/react-button";
import styles from '../../assets/jss/views/OneTimePasswordRegistrationView/OneTimePasswordRegistrationView';
import styles from '../../assets/scss/views/OneTimePasswordRegistrationView/OneTimePasswordRegistrationView.module.scss';
import { RouteProps, RouterProps } from "react-router";
import QueryString from 'query-string';
import QRCode from 'qrcode.react';
import FormNotification from "../../components/FormNotification/FormNotification";
import Notification from "../../components/Notification/Notification";
import googleStoreImage from '../../assets/images/googleplay-badge.svg';
import appleStoreImage from '../../assets/images/applestore-badge.svg';
import { Secret } from "./Secret";
import CircleLoader, { Status } from "../../components/CircleLoader/CircleLoader";
export interface Props extends WithStyles, RouteProps, RouterProps {
export interface Props extends RouteProps, RouterProps {
secret: Secret | null;
error: string | null;
onInit: (token: string) => void;
@ -47,54 +48,52 @@ class OneTimePasswordRegistrationView extends Component<Props> {
}
private renderWithSecret(secret: Secret) {
const { classes } = this.props;
return (
<div>
<div className={classes.text}>
<div className={styles.text}>
Register your device by scanning the barcode or adding the key.
</div>
<div className={classes.secretContainer}>
<div className={classes.qrcodeContainer}>
<div className={styles.secretContainer}>
<div className={styles.qrcodeContainer}>
<QRCode value={secret.otpauth_url} size={180} level="Q"></QRCode>
</div>
<div className={classes.base32Container}>{secret.base32_secret}</div>
<div className={styles.base32Container}>{secret.base32_secret}</div>
</div>
<div className={classes.loginButtonContainer}>
<div className={styles.loginButtonContainer}>
<Button
color="primary"
variant="contained"
raised={true}
onClick={this.props.onLoginClicked}>
Login
</Button>
</div>
<div className={classes.needGoogleAuthenticator}>
<div className={classes.needGoogleAuthenticatorText}>Need Google Authenticator?</div>
<img src={appleStoreImage} className={classes.store} alt='Google Authenticator on Apple Store'/>
<img src={googleStoreImage} className={classes.store} alt='Google Authenticator on Google Store'/>
<div className={styles.needGoogleAuthenticator}>
<div className={styles.needGoogleAuthenticatorText}>Need Google Authenticator?</div>
<img src={appleStoreImage} className={styles.store} alt='Google Authenticator on Apple Store'/>
<img src={googleStoreImage} className={styles.store} alt='Google Authenticator on Google Store'/>
</div>
</div>
)
}
private renderError() {
const {classes} = this.props;
return (
<div>
<FormNotification show={true}>
<Notification show={true}>
<div>{this.props.error}</div>
</FormNotification>
<div className={classes.buttonContainer}>
</Notification>
<div className={styles.buttonContainer}>
<Button
variant="contained"
color="primary"
className={classes.button}
raised={true}
className={styles.button}
onClick={this.props.onRetryClicked}>
Retry
</Button>
<Button
variant="contained"
color="primary"
className={classes.button}
raised={true}
className={styles.button}
onClick={this.props.onCancelClicked}>
Cancel
</Button>
@ -110,11 +109,10 @@ class OneTimePasswordRegistrationView extends Component<Props> {
}
private renderLoading() {
const { classes } = this.props;
return (
<div>
<div>One-Time password secret is being generated...</div>
<div className={classes.progressContainer}><CircularProgress /></div>
<div className={styles.progressContainer}><CircleLoader status={Status.LOADING} /></div>
</div>
)
}
@ -126,4 +124,4 @@ class OneTimePasswordRegistrationView extends Component<Props> {
}
}
export default withStyles(styles)(OneTimePasswordRegistrationView);
export default OneTimePasswordRegistrationView;

View File

@ -1,11 +1,13 @@
import React, { Component, KeyboardEvent, ChangeEvent } from "react";
import { TextField, Button, WithStyles, withStyles } from "@material-ui/core";
import React, { Component, KeyboardEvent, FormEvent } from "react";
import { RouterProps } from "react-router";
import classnames from 'classnames';
import QueryString from 'query-string';
import styles from '../../assets/jss/views/ResetPasswordView/ResetPasswordView';
import FormNotification from "../../components/FormNotification/FormNotification";
import Button from "@material/react-button";
import TextField, { Input } from "@material/react-text-field";
import styles from '../../assets/scss/views/ResetPasswordView/ResetPasswordView.module.scss';
import Notification from "../../components/Notification/Notification";
export interface StateProps {
disabled: boolean;
@ -17,7 +19,7 @@ export interface DispatchProps {
onCancelClicked: () => void;
}
export type Props = StateProps & DispatchProps & RouterProps & WithStyles;
export type Props = StateProps & DispatchProps & RouterProps;
interface State {
password1: string;
@ -66,61 +68,64 @@ class ResetPasswordView extends Component<Props, State> {
this.onPasswordResetRequested();
}
private onPassword1Changed = (e: ChangeEvent<HTMLInputElement>) => {
this.setState({password1: e.target.value});
private onPassword1Changed = (e: FormEvent<HTMLElement>) => {
this.setState({password1: (e.target as HTMLInputElement).value});
}
private onPassword2Changed = (e: ChangeEvent<HTMLInputElement>) => {
this.setState({password2: e.target.value});
private onPassword2Changed = (e: FormEvent<HTMLElement>) => {
this.setState({password2: (e.target as HTMLInputElement).value});
}
render() {
const { classes } = this.props;
return (
<div>
<FormNotification show={this.state.error !== null}>
<Notification show={this.state.error !== null}>
{this.state.error}
</FormNotification>
</Notification>
<div>Enter your new password</div>
<div className={classes.form}>
<div className={styles.form}>
<TextField
className={classes.field}
variant="outlined"
type="password"
className={styles.field}
outlined={true}
id="password1"
value={this.state.password1}
onChange={this.onPassword1Changed}
disabled={this.props.disabled}
label="New password">
<Input
type="password"
key="password1"
value={this.state.password1}
onChange={this.onPassword1Changed}
disabled={this.props.disabled}/>
</TextField>
<TextField
className={classes.field}
variant="outlined"
type="password"
className={styles.field}
outlined={true}
id="password2"
value={this.state.password2}
onKeyPress={this.onKeyPressed}
onChange={this.onPassword2Changed}
disabled={this.props.disabled}
label="Confirm password">
<Input
type="password"
key="password2"
value={this.state.password2}
onKeyPress={this.onKeyPressed}
onChange={this.onPassword2Changed}
disabled={this.props.disabled} />
</TextField>
<div className={classes.buttonsContainer}>
<div className={classnames(classes.buttonContainer, classes.buttonResetContainer)}>
<div className={styles.buttonsContainer}>
<div className={classnames(styles.buttonContainer, styles.buttonResetContainer)}>
<Button
onClick={this.onResetClicked}
variant="contained"
color="primary"
raised={true}
disabled={this.props.disabled}
className={classnames(classes.button, classes.buttonReset)}>
className={classnames(styles.button, styles.buttonReset)}>
Reset
</Button>
</div>
<div className={classnames(classes.buttonContainer, classes.buttonCancelContainer)}>
<div className={classnames(styles.buttonContainer, styles.buttonCancelContainer)}>
<Button
onClick={this.props.onCancelClicked}
variant="contained"
color="primary"
className={classnames(classes.button, classes.buttonCancel)}>
raised={true}
className={classnames(styles.button, styles.buttonCancel)}>
Cancel
</Button>
</div>
@ -131,4 +136,4 @@ class ResetPasswordView extends Component<Props, State> {
}
}
export default withStyles(styles)(ResetPasswordView);
export default ResetPasswordView;

View File

@ -1,14 +1,14 @@
import React, { Component } from "react";
import { WithStyles, withStyles, Button } from "@material-ui/core";
import Button from "@material/react-button";
import styles from '../../assets/jss/views/SecurityKeyRegistrationView/SecurityKeyRegistrationView';
import styles from '../../assets/scss/views/SecurityKeyRegistrationView/SecurityKeyRegistrationView.module.scss';
import { RouteProps, RouterProps } from "react-router";
import QueryString from 'query-string';
import FormNotification from "../../components/FormNotification/FormNotification";
import Notification from "../../components/Notification/Notification";
import CircleLoader, { Status } from "../../components/CircleLoader/CircleLoader";
export interface Props extends WithStyles, RouteProps, RouterProps {
export interface Props extends RouteProps, RouterProps {
deviceRegistered: boolean | null;
error: string | null;
onInit: (token: string) => void;
@ -32,16 +32,15 @@ class SecurityKeyRegistrationView extends Component<Props> {
}
private renderError() {
const { classes } = this.props;
return (
<div>
<FormNotification show={true}>
<Notification show={true}>
{this.props.error}
</FormNotification>
<div className={classes.retryButtonContainer}>
</Notification>
<div className={styles.retryButtonContainer}>
<Button
variant="contained"
color="primary"
raised={true}
onClick={this.props.onBackClicked}>
Back
</Button>
@ -51,7 +50,6 @@ class SecurityKeyRegistrationView extends Component<Props> {
}
private renderRegistering() {
const { classes } = this.props;
let status = Status.LOADING;
if (this.props.deviceRegistered === true) {
status = Status.SUCCESSFUL;
@ -60,8 +58,8 @@ class SecurityKeyRegistrationView extends Component<Props> {
}
return (
<div>
<div className={classes.infoContainer}>Press the gold disk to register your security key</div>
<div className={classes.imageContainer}>
<div className={styles.infoContainer}>Press the gold disk to register your security key</div>
<div className={styles.imageContainer}>
<CircleLoader status={status}></CircleLoader>
</div>
</div>
@ -77,4 +75,4 @@ class SecurityKeyRegistrationView extends Component<Props> {
}
}
export default withStyles(styles)(SecurityKeyRegistrationView);
export default SecurityKeyRegistrationView;

68
package-lock.json generated
View File

@ -285,7 +285,7 @@
},
"@types/events": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
"integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==",
"dev": true
},
@ -312,7 +312,7 @@
},
"@types/express-session": {
"version": "1.15.8",
"resolved": "http://registry.npmjs.org/@types/express-session/-/express-session-1.15.8.tgz",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.15.8.tgz",
"integrity": "sha512-Be5N9zul4C/IH1UjRDaVJ46wkG1jsBgJlihBdWlqJWfCaiqvaVmxcyqcLey7omSFGCTIUDgdHqf0vwNjEZOSVA==",
"dev": true,
"requires": {
@ -358,7 +358,7 @@
},
"@types/ldapjs": {
"version": "1.0.3",
"resolved": "http://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.3.tgz",
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.3.tgz",
"integrity": "sha512-FSj24s1WsFEfOy8taIKp2DokSZfFkjWYZb88AS5eDj3WTocZ+4DnHjhzrXEs048WQ5mfOLJXMOAnc0kSnHh5Lw==",
"dev": true,
"requires": {
@ -380,7 +380,7 @@
},
"@types/mockdate": {
"version": "2.0.0",
"resolved": "http://registry.npmjs.org/@types/mockdate/-/mockdate-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/@types/mockdate/-/mockdate-2.0.0.tgz",
"integrity": "sha1-qvOIoerTsPXtbcFhGVbqe0ClfTw=",
"dev": true
},
@ -598,7 +598,7 @@
},
"@types/yamljs": {
"version": "0.2.30",
"resolved": "http://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.30.tgz",
"resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.30.tgz",
"integrity": "sha1-0DTh0ynkbo0Pc3yajbl/aPgbU4I=",
"dev": true
},
@ -844,7 +844,7 @@
},
"array-equal": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
"dev": true
},
@ -1058,7 +1058,7 @@
},
"chalk": {
"version": "1.1.3",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@ -1536,7 +1536,7 @@
},
"browserify-aes": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"dev": true,
"requires": {
@ -1621,7 +1621,7 @@
},
"browserify-rsa": {
"version": "4.0.1",
"resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
"dev": true,
"requires": {
@ -1660,7 +1660,7 @@
},
"buffer": {
"version": "4.9.1",
"resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"dev": true,
"requires": {
@ -1753,7 +1753,7 @@
},
"camelcase-keys": {
"version": "2.1.0",
"resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
"integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
"dev": true,
"requires": {
@ -2158,7 +2158,7 @@
},
"create-hash": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"dev": true,
"requires": {
@ -2171,7 +2171,7 @@
},
"create-hmac": {
"version": "1.1.7",
"resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"dev": true,
"requires": {
@ -2564,7 +2564,7 @@
},
"diffie-hellman": {
"version": "5.0.3",
"resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"dev": true,
"requires": {
@ -2628,7 +2628,7 @@
},
"duplexer": {
"version": "0.1.1",
"resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
"integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
"dev": true
},
@ -2892,7 +2892,7 @@
},
"events": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
"dev": true
},
@ -3178,7 +3178,7 @@
},
"finalhandler": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"requires": {
"debug": "2.6.9",
@ -3866,7 +3866,7 @@
},
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
},
@ -4744,7 +4744,7 @@
},
"http-errors": {
"version": "1.6.3",
"resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
@ -4936,7 +4936,7 @@
},
"is-builtin-module": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
"integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
"dev": true,
"requires": {
@ -5740,7 +5740,7 @@
},
"load-json-file": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
"dev": true,
"requires": {
@ -5986,7 +5986,7 @@
},
"media-typer": {
"version": "0.3.0",
"resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"mem": {
@ -6000,7 +6000,7 @@
},
"meow": {
"version": "3.7.0",
"resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
"resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
"integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
"dev": true,
"requires": {
@ -6137,7 +6137,7 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
@ -6339,7 +6339,7 @@
},
"ncp": {
"version": "2.0.0",
"resolved": "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
"integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=",
"optional": true
},
@ -8222,7 +8222,7 @@
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true
},
@ -8309,7 +8309,7 @@
},
"parse-asn1": {
"version": "5.1.1",
"resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
"integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==",
"dev": true,
"requires": {
@ -8384,7 +8384,7 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"path-is-inside": {
@ -9062,7 +9062,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@ -9581,7 +9581,7 @@
},
"sha.js": {
"version": "2.4.11",
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"dev": true,
"requires": {
@ -10160,7 +10160,7 @@
},
"strip-eof": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
"dev": true
},
@ -10252,7 +10252,7 @@
},
"through": {
"version": "2.3.8",
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
@ -11312,7 +11312,7 @@
"dependencies": {
"async": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
"integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k="
}
}
@ -11333,7 +11333,7 @@
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"dev": true,
"requires": {

View File

@ -1,3 +0,0 @@
export const PRE_VALIDATION_TEMPLATE = "need-identity-validation";

View File

@ -1,3 +1,3 @@
import { default as Level } from '../../../../shared/AuthenticationLevel';
import { default as Level } from "../../../../shared/AuthenticationLevel";
export {Level};
export { Level };

View File

@ -8,7 +8,7 @@ import UserMessages = require("../../../../../shared/UserMessages");
import { ServerVariables } from "../../ServerVariables";
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
import { GroupsAndEmails } from "../../authentication/backends/GroupsAndEmails";
import {Level} from "../../authentication/Level";
import { Level } from "../../authentication/Level";
export default function (vars: ServerVariables) {
return function (req: express.Request, res: express.Response)
@ -65,4 +65,4 @@ export default function (vars: ServerVariables) {
})
.catch(ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.AUTHENTICATION_FAILED));
};
}
}

View File

@ -5,13 +5,10 @@ import objectPath = require("object-path");
import exceptions = require("../../../Exceptions");
import { Identity } from "../../../../../types/Identity";
import { IdentityValidable } from "../../../IdentityValidable";
import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate";
import Constants = require("../constants");
import { IRequestLogger } from "../../../logging/IRequestLogger";
import { IUsersDatabase } from "../../../authentication/backends/IUsersDatabase";
export const TEMPLATE_NAME = "password-reset-form";
export default class PasswordResetHandler implements IdentityValidable {
private logger: IRequestLogger;
private usersDatabase: IUsersDatabase;
@ -52,7 +49,8 @@ export default class PasswordResetHandler implements IdentityValidable {
}
preValidationResponse(req: express.Request, res: express.Response) {
res.render(PRE_VALIDATION_TEMPLATE);
res.status(204);
res.send();
}
postValidationInit(req: express.Request) {
@ -60,7 +58,8 @@ export default class PasswordResetHandler implements IdentityValidable {
}
postValidationResponse(req: express.Request, res: express.Response) {
res.render(TEMPLATE_NAME);
res.status(204);
res.send();
}
mailSubject(): string {

View File

@ -4,7 +4,6 @@ import BluebirdPromise = require("bluebird");
import { Identity } from "../../../../../../types/Identity";
import { IdentityValidable } from "../../../../IdentityValidable";
import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate";
import Constants = require("../constants");
import ErrorReplies = require("../../../../ErrorReplies");
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
@ -64,7 +63,8 @@ export default class RegistrationHandler implements IdentityValidable {
}
preValidationResponse(req: express.Request, res: express.Response) {
res.render(PRE_VALIDATION_TEMPLATE);
res.status(204);
res.send();
}
postValidationInit(req: express.Request) {

View File

@ -1,11 +1,9 @@
import BluebirdPromise = require("bluebird");
import express = require("express");
import objectPath = require("object-path");
import { IdentityValidable } from "../../../../IdentityValidable";
import { Identity } from "../../../../../../types/Identity";
import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate";
import FirstFactorValidator = require("../../../../FirstFactorValidator");
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
import { IRequestLogger } from "../../../../logging/IRequestLogger";
@ -13,8 +11,6 @@ import { IRequestLogger } from "../../../../logging/IRequestLogger";
const CHALLENGE = "u2f-register";
const MAIL_SUBJECT = "Register your security key with Authelia";
const POST_VALIDATION_TEMPLATE_NAME = "u2f-register";
export default class RegistrationHandler implements IdentityValidable {
private logger: IRequestLogger;
@ -55,7 +51,8 @@ export default class RegistrationHandler implements IdentityValidable {
}
preValidationResponse(req: express.Request, res: express.Response) {
res.render(PRE_VALIDATION_TEMPLATE);
res.status(204);
res.send();
}
postValidationInit(req: express.Request) {
@ -63,7 +60,8 @@ export default class RegistrationHandler implements IdentityValidable {
}
postValidationResponse(req: express.Request, res: express.Response) {
res.render(POST_VALIDATION_TEMPLATE_NAME);
res.status(204);
res.send();
}
mailSubject(): string {

View File

@ -1,7 +1,7 @@
import * as Express from 'express';
import * as Bluebird from 'bluebird';
import * as Express from "express";
import * as Bluebird from "bluebird";
import { ServerVariables } from "../../ServerVariables";
import { AuthenticationSessionHandler } from '../../AuthenticationSessionHandler';
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
export default function (vars: ServerVariables) {
return function (req: Express.Request, res: Express.Response): Bluebird<void> {
@ -10,7 +10,7 @@ export default function (vars: ServerVariables) {
res.json({
username: authSession.userid,
authentication_level: authSession.authentication_level
})
});
resolve();
});
};

View File

@ -1,92 +0,0 @@
import BluebirdPromise = require("bluebird");
import request = require("request");
import assert = require("assert");
import Endpoints = require("../../shared/api");
declare module "request" {
export interface RequestAPI<TRequest extends Request,
TOptions extends CoreOptions,
TUriUrlOptions> {
getAsync(uri: string, options?: RequiredUriUrl): BluebirdPromise<RequestResponse>;
getAsync(uri: string): BluebirdPromise<RequestResponse>;
getAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise<RequestResponse>;
postAsync(uri: string, options?: CoreOptions): BluebirdPromise<RequestResponse>;
postAsync(uri: string): BluebirdPromise<RequestResponse>;
postAsync(options: RequiredUriUrl & CoreOptions): BluebirdPromise<RequestResponse>;
}
}
const requestAsync: typeof request = BluebirdPromise.promisifyAll(request) as typeof request;
export = function (port: number) {
const PORT = port;
const BASE_URL = "http://localhost:" + PORT;
function execute_totp(jar: request.CookieJar, token: string) {
return requestAsync.postAsync({
url: BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST,
jar: jar,
form: {
token: token
}
});
}
function execute_u2f_authentication(jar: request.CookieJar) {
return requestAsync.getAsync({
url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET,
jar: jar
})
.then(function (res: request.RequestResponse) {
assert.equal(res.statusCode, 200);
return requestAsync.postAsync({
url: BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST,
jar: jar,
form: {
}
});
});
}
function execute_verification(jar: request.CookieJar) {
return requestAsync.getAsync({ url: BASE_URL + Endpoints.VERIFY_GET, jar: jar });
}
function execute_login(jar: request.CookieJar) {
return requestAsync.getAsync({ url: BASE_URL + Endpoints.FIRST_FACTOR_GET, jar: jar });
}
function execute_first_factor(jar: request.CookieJar) {
return requestAsync.postAsync({
url: BASE_URL + Endpoints.FIRST_FACTOR_POST,
jar: jar,
form: {
username: "test_ok",
password: "password"
}
});
}
function execute_failing_first_factor(jar: request.CookieJar) {
return requestAsync.postAsync({
url: BASE_URL + Endpoints.FIRST_FACTOR_POST,
jar: jar,
form: {
username: "test_nok",
password: "password"
}
});
}
return {
login: execute_login,
verify: execute_verification,
u2f_authentication: execute_u2f_authentication,
first_factor: execute_first_factor,
failing_first_factor: execute_failing_first_factor,
totp: execute_totp,
};
};

View File

@ -2,6 +2,6 @@ enum Level {
NOT_AUTHENTICATED = 0,
ONE_FACTOR = 1,
TWO_FACTOR = 2
};
}
export default Level;
export default Level;

View File

@ -244,10 +244,10 @@ export const FIRST_FACTOR_POST = "/api/firstfactor";
* @apiName State
* @apiGroup Authentication
* @apiVersion 1.0.0
*
*
* @apiSuccess (Success 200) A dict containing the username and the authentication
* level
*
*
* @apiDescription Get the authentication state of the user based on the cookie.
*/
export const STATE_GET = "/api/state";