fixing setup authentication
This commit is contained in:
Родитель
5527dff430
Коммит
ae050c0fcc
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"main.css": "static/css/main.b2105435.css",
|
||||
"main.css.map": "static/css/main.b2105435.css.map",
|
||||
"main.js": "static/js/main.164150ed.js",
|
||||
"main.js.map": "static/js/main.164150ed.js.map"
|
||||
"main.js": "static/js/main.0fd897f0.js",
|
||||
"main.js.map": "static/js/main.0fd897f0.js.map"
|
||||
}
|
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="shortcut icon" href="/favicon.ico"><link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"/><title>React App</title><link href="/static/css/main.b2105435.css" rel="stylesheet"></head><body><div id="root"></div><script type="text/javascript" src="/static/js/main.164150ed.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="shortcut icon" href="/favicon.ico"><link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"/><title>React App</title><link href="/static/css/main.b2105435.css" rel="stylesheet"></head><body><div id="root"></div><script type="text/javascript" src="/static/js/main.0fd897f0.js"></script></body></html>
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -12,6 +12,13 @@ const cosmosDBRouter = require('./routes/cosmos-db');
|
|||
const azureRouter = require('./routes/azure');
|
||||
|
||||
const app = express();
|
||||
|
||||
|
||||
app.use((req, res, next) => {
|
||||
console.log(`Request URL: ${req.url}`);
|
||||
return next();
|
||||
});
|
||||
|
||||
app.use(cookieParser());
|
||||
app.use(expressSession({ secret: 'keyboard cat', resave: true, saveUninitialized: false }));
|
||||
app.use(bodyParser.json());
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"identityMetadata": "https://login.microsoftonline.com/common/.well-known/openid-configuration",
|
||||
"validateIssuer": false,
|
||||
"skipUserProfile": true,
|
||||
"validateIssuer": true,
|
||||
"skipUserProfile": false,
|
||||
"responseType": "id_token code",
|
||||
"responseMode": "query"
|
||||
}
|
||||
|
|
|
@ -279,8 +279,6 @@ router.post('/setup', (req, res) => {
|
|||
}
|
||||
|
||||
var content = (req.body && req.body.json) || '';
|
||||
console.dir(content);
|
||||
|
||||
fs.writeFile(path.join(__dirname, '..', 'config', 'setup.private.json'), content, err => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
|
|
|
@ -21,8 +21,9 @@ if (fs.existsSync(path.join(__dirname, '..', 'config', 'setup.private.json'))) {
|
|||
let users = [];
|
||||
function findByEmail(email, fn) {
|
||||
for (var i = 0, len = users.length; i < len; i++) {
|
||||
var user = users[i];
|
||||
console.info('we are using user: ', user);
|
||||
let user = users[i];
|
||||
|
||||
console.info(`Current logged in user: ${user}`);
|
||||
if (user.email === email) {
|
||||
return fn(null, user);
|
||||
}
|
||||
|
@ -66,15 +67,18 @@ function initializePassport() {
|
|||
skipUserProfile: configAuth.skipUserProfile,
|
||||
responseType: configAuth.responseType,
|
||||
responseMode: configAuth.responseMode,
|
||||
validateIssuer: configAuth.validateIssuer
|
||||
validateIssuer: configAuth.validateIssuer,
|
||||
issuer: configSetup.issuer
|
||||
},
|
||||
function(iss, sub, profile, accessToken, refreshToken, done) {
|
||||
console.log(`passport arguments: ${arguments}`);
|
||||
|
||||
profile.email = profile.email || profile.upn;
|
||||
|
||||
if (!profile.email) {
|
||||
return done(new Error("No email found"), null);
|
||||
}
|
||||
|
||||
// asynchronous verification, for effect...
|
||||
process.nextTick(function () {
|
||||
findByEmail(profile.email, function(err, user) {
|
||||
|
@ -141,7 +145,11 @@ router.get('/account', (req, res) => {
|
|||
function addAuthRoutes() {
|
||||
|
||||
router.get('/login',
|
||||
passport.authenticate('azuread-openidconnect', { failureRedirect: '/login' }),
|
||||
(req, res, next) => {
|
||||
console.log('aaa');
|
||||
return next();
|
||||
},
|
||||
passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/login' }),
|
||||
function(req, res) {
|
||||
console.info('Login was called in the Sample');
|
||||
res.redirect('/');
|
||||
|
@ -154,7 +162,7 @@ function addAuthRoutes() {
|
|||
// provider will redirect the user back to this application at
|
||||
// /auth/openid/return
|
||||
router.get('/openid',
|
||||
passport.authenticate('azuread-openidconnect', { failureRedirect: '/login' }),
|
||||
passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/login' }),
|
||||
function(req, res) {
|
||||
console.info('Authentication was called in the Sample');
|
||||
res.redirect('/');
|
||||
|
@ -166,7 +174,7 @@ function addAuthRoutes() {
|
|||
// login page. Otherwise, the primary route function function will be called,
|
||||
// which, in this example, will redirect the user to the home page.
|
||||
router.get('/openid/return',
|
||||
passport.authenticate('azuread-openidconnect', { failureRedirect: '/login' }),
|
||||
passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/login' }),
|
||||
function(req, res) {
|
||||
console.info('We received a return from AzureAD.');
|
||||
res.redirect(redirectPath || '/');
|
||||
|
@ -179,7 +187,7 @@ function addAuthRoutes() {
|
|||
// login page. Otherwise, the primary route function function will be called,
|
||||
// which, in this example, will redirect the user to the home page.
|
||||
router.post('/openid/return',
|
||||
passport.authenticate('azuread-openidconnect', { failureRedirect: '/login' }),
|
||||
passport.authenticate('azuread-openidconnect', { failureRedirect: '/auth/login' }),
|
||||
function(req, res) {
|
||||
console.info('We received a return from AzureAD.');
|
||||
res.redirect(redirectPath || '/');
|
||||
|
@ -191,7 +199,10 @@ function addAuthRoutes() {
|
|||
res.redirect('/');
|
||||
});
|
||||
}
|
||||
if (authEnabled) { addAuthRoutes(); }
|
||||
if (authEnabled) {
|
||||
console.log(`Registering auth routes`);
|
||||
addAuthRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding all authentication middleware on process start.
|
||||
|
|
|
@ -46,6 +46,7 @@ export default class Home extends React.Component<any, IHomeState> {
|
|||
redirectUrl: '',
|
||||
clientID: '',
|
||||
clientSecret: '',
|
||||
issuer: '',
|
||||
loaded: false,
|
||||
|
||||
templates: [],
|
||||
|
|
|
@ -6,11 +6,13 @@ import Button from 'react-md/lib/Buttons/Button';
|
|||
import Switch from 'react-md/lib/SelectionControls/Switch';
|
||||
|
||||
import InfoDrawer from '../common/InfoDrawer';
|
||||
import { ToastActions } from '../Toast';
|
||||
|
||||
import SetupActions from '../../actions/SetupActions';
|
||||
import SetupStore from '../../stores/SetupStore';
|
||||
import SetupStore, { ISetupStoreState } from '../../stores/SetupStore';
|
||||
|
||||
interface ISetupState extends ISetupConfig {
|
||||
editedEmail?: string;
|
||||
validEmail?: boolean;
|
||||
loaded?: boolean;
|
||||
}
|
||||
|
@ -21,33 +23,44 @@ export default class Setup extends React.Component<any, ISetupState> {
|
|||
admins: null,
|
||||
stage: 'none',
|
||||
enableAuthentication: false,
|
||||
editedEmail: '',
|
||||
validEmail: true,
|
||||
allowHttp: false,
|
||||
redirectUrl: '',
|
||||
clientID: '',
|
||||
clientSecret: '',
|
||||
loaded: false
|
||||
loaded: false,
|
||||
issuer: ''
|
||||
};
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.checkKeyValue = this.checkKeyValue.bind(this);
|
||||
this.updateSetupState = this.updateSetupState.bind(this);
|
||||
this.checkEmailValue = this.checkEmailValue.bind(this);
|
||||
this.onSave = this.onSave.bind(this);
|
||||
this.onCancel = this.onCancel.bind(this);
|
||||
this.onRemoveAdmin = this.onRemoveAdmin.bind(this);
|
||||
this.onSwitchAllowHttp = this.onSwitchAllowHttp.bind(this);
|
||||
this.onSwitchAuthenticationEnables = this.onSwitchAuthenticationEnables.bind(this);
|
||||
this.onFieldChange = this.onFieldChange.bind(this);
|
||||
this.getAdminArray = this.getAdminArray.bind(this);
|
||||
}
|
||||
|
||||
updateSetupState(state: ISetupStoreState) {
|
||||
this.setState(state);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
this.setState(SetupStore.getState());
|
||||
this.updateSetupState(SetupStore.getState());
|
||||
|
||||
SetupActions.load();
|
||||
SetupStore.listen(state => {
|
||||
this.setState(state);
|
||||
});
|
||||
SetupStore.listen(this.updateSetupState);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
SetupStore.unlisten(this.updateSetupState);
|
||||
}
|
||||
|
||||
validateEmail(email: string): boolean {
|
||||
|
@ -56,7 +69,10 @@ export default class Setup extends React.Component<any, ISetupState> {
|
|||
return re.test(email);
|
||||
}
|
||||
|
||||
checkKeyValue(e: any) {
|
||||
checkEmailValue(e: any) {
|
||||
|
||||
this.setState({ editedEmail: e.target.value })
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
|
||||
let email = e.target.value;
|
||||
|
@ -75,15 +91,60 @@ export default class Setup extends React.Component<any, ISetupState> {
|
|||
return true;
|
||||
}
|
||||
|
||||
onSave () {
|
||||
fixRedirectUrl(redirectUrl: string): string {
|
||||
if (redirectUrl) { return redirectUrl; }
|
||||
|
||||
let host = window.location.host;
|
||||
|
||||
// On localhost, authentication requests go directly to port 4000
|
||||
if (host === 'localhost:3000') { host = 'localhost:4000'; }
|
||||
|
||||
return window.location.protocol + '//' + host + '/auth/openid/return';
|
||||
}
|
||||
|
||||
getAdminArray(): string[] {
|
||||
let admins = this.state.admins || [];
|
||||
if (this.state.editedEmail) {
|
||||
admins.push(this.state.editedEmail);
|
||||
}
|
||||
return admins;
|
||||
}
|
||||
|
||||
onSave(): any {
|
||||
|
||||
let admins = this.getAdminArray();
|
||||
let redirectUrl = this.fixRedirectUrl(this.state.redirectUrl);
|
||||
|
||||
if (this.state.enableAuthentication) {
|
||||
if (!admins || !admins.length) {
|
||||
return ToastActions.addToast({ text: 'Fill in at least one admin', action: null });
|
||||
}
|
||||
if (!redirectUrl) {
|
||||
return ToastActions.addToast({ text: 'Fill in redirect url', action: null });
|
||||
}
|
||||
if (!this.state.issuer) {
|
||||
return ToastActions.addToast({ text: 'Fill in issuer', action: null });
|
||||
}
|
||||
if (!this.state.clientID) {
|
||||
return ToastActions.addToast({ text: 'Fill in client ID', action: null });
|
||||
}
|
||||
if (!this.state.clientSecret) {
|
||||
return ToastActions.addToast({ text: 'Fill in client secret', action: null });
|
||||
}
|
||||
if (!this.state.allowHttp && redirectUrl.startsWith('http:')) {
|
||||
return ToastActions.addToast({ text: 'Redirect url should start with https or enable http redirects', action: null });
|
||||
}
|
||||
}
|
||||
|
||||
var setupConfig = {
|
||||
admins: this.state.admins,
|
||||
admins: admins,
|
||||
stage: this.state.stage,
|
||||
enableAuthentication: this.state.enableAuthentication,
|
||||
allowHttp: this.state.allowHttp,
|
||||
redirectUrl: this.state.redirectUrl,
|
||||
redirectUrl: redirectUrl,
|
||||
clientID: this.state.clientID,
|
||||
clientSecret: this.state.clientSecret
|
||||
clientSecret: this.state.clientSecret,
|
||||
issuer: this.state.issuer
|
||||
};
|
||||
SetupActions.save(setupConfig, () => { window.location.replace('/'); });
|
||||
}
|
||||
|
@ -118,11 +179,10 @@ export default class Setup extends React.Component<any, ISetupState> {
|
|||
|
||||
render() {
|
||||
|
||||
let { admins, loaded, validEmail, enableAuthentication, redirectUrl, clientID, clientSecret } = this.state;
|
||||
let { admins, loaded, validEmail, enableAuthentication, redirectUrl, clientID, clientSecret, issuer } = this.state;
|
||||
|
||||
if (!redirectUrl) {
|
||||
redirectUrl = window.location.protocol + '//' + window.location.host + '/auth/openid/return';
|
||||
}
|
||||
// Setting default redirect parameter
|
||||
redirectUrl = this.fixRedirectUrl(redirectUrl);
|
||||
|
||||
if (!loaded) {
|
||||
return null;
|
||||
|
@ -143,6 +203,7 @@ export default class Setup extends React.Component<any, ISetupState> {
|
|||
<div style={{ width: '100%' }}>
|
||||
<Switch
|
||||
id="enableAuthentication"
|
||||
name="enableAuthentication"
|
||||
label="Enable Authentication"
|
||||
checked={enableAuthentication}
|
||||
onChange={this.onSwitchAuthenticationEnables}
|
||||
|
@ -171,6 +232,7 @@ export default class Setup extends React.Component<any, ISetupState> {
|
|||
<div>
|
||||
<Switch
|
||||
id="allowHttp"
|
||||
name="allowHttp"
|
||||
label="Allow http in authentication responses"
|
||||
checked={this.state.allowHttp}
|
||||
onChange={this.onSwitchAllowHttp}
|
||||
|
@ -182,11 +244,11 @@ export default class Setup extends React.Component<any, ISetupState> {
|
|||
id="adminEmail"
|
||||
label="Administrator Email"
|
||||
error={!validEmail}
|
||||
errorText={!validEmail && 'Please enter a valid email address'}
|
||||
errorText={(!validEmail && 'Please enter a valid email address') || ''}
|
||||
lineDirection="center"
|
||||
placeholder="Enter an additional administrator email"
|
||||
className="md-cell md-cell--bottom"
|
||||
onKeyDown={this.checkKeyValue}
|
||||
onKeyDown={this.checkEmailValue}
|
||||
/>
|
||||
<TextField
|
||||
id="redirectUrl"
|
||||
|
@ -216,6 +278,15 @@ export default class Setup extends React.Component<any, ISetupState> {
|
|||
defaultValue={clientSecret}
|
||||
onChange={this.onFieldChange}
|
||||
/>
|
||||
<TextField
|
||||
id="issuer"
|
||||
label="Issuer: https://sts.windows.net/{Tenant-ID}/"
|
||||
lineDirection="center"
|
||||
placeholder="https://sts.windows.net/{Tenant-ID}/"
|
||||
className="md-cell md-cell--bottom"
|
||||
defaultValue={issuer}
|
||||
onChange={this.onFieldChange}
|
||||
/>
|
||||
</div>)
|
||||
}
|
||||
<Button flat primary label="Save & Apply" onClick={this.onSave}>save</Button>
|
||||
|
|
|
@ -5,7 +5,7 @@ import connections from '../data-sources/connections';
|
|||
import { DataSourceConnector, IDataSourceDictionary } from '../data-sources';
|
||||
import SetupActions from '../actions/SetupActions';
|
||||
|
||||
interface ISetupStoreState extends ISetupConfig {
|
||||
export interface ISetupStoreState extends ISetupConfig {
|
||||
loaded: boolean;
|
||||
saveSuccess: boolean;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ class SetupStore extends AbstractStoreModel<ISetupStoreState> implements ISetupS
|
|||
redirectUrl: string;
|
||||
clientID: string;
|
||||
clientSecret: string;
|
||||
issuer: string;
|
||||
loaded: boolean;
|
||||
saveSuccess: boolean;
|
||||
|
||||
|
@ -34,6 +35,7 @@ class SetupStore extends AbstractStoreModel<ISetupStoreState> implements ISetupS
|
|||
this.clientSecret = '';
|
||||
this.loaded = false;
|
||||
this.saveSuccess = false;
|
||||
this.issuer = '';
|
||||
|
||||
this.bindListeners({
|
||||
load: SetupActions.load
|
||||
|
@ -48,6 +50,7 @@ class SetupStore extends AbstractStoreModel<ISetupStoreState> implements ISetupS
|
|||
this.redirectUrl = setupConfig.redirectUrl;
|
||||
this.clientID = setupConfig.clientID;
|
||||
this.clientSecret = setupConfig.clientSecret;
|
||||
this.issuer = setupConfig.issuer;
|
||||
this.loaded = true;
|
||||
this.saveSuccess = true;
|
||||
|
||||
|
|
|
@ -155,4 +155,5 @@ interface ISetupConfig {
|
|||
redirectUrl: string;
|
||||
clientID: string;
|
||||
clientSecret: string;
|
||||
issuer: string;
|
||||
}
|
Загрузка…
Ссылка в новой задаче