Backport JS dependency updates (#50)
This commit is contained in:
Родитель
08a4e743cc
Коммит
bb161e0460
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -18,38 +18,38 @@
|
|||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~13.1.1",
|
||||
"@angular/common": "~13.1.1",
|
||||
"@angular/compiler": "~13.1.1",
|
||||
"@angular/core": "~13.1.1",
|
||||
"@angular/forms": "~13.1.1",
|
||||
"@angular/platform-browser": "~13.1.1",
|
||||
"@angular/platform-browser-dynamic": "~13.1.1",
|
||||
"@angular/platform-server": "~13.1.1",
|
||||
"@angular/router": "~13.1.1",
|
||||
"@angular/animations": "~13.3.5",
|
||||
"@angular/common": "~13.3.5",
|
||||
"@angular/compiler": "~13.3.5",
|
||||
"@angular/core": "~13.3.5",
|
||||
"@angular/forms": "~13.3.5",
|
||||
"@angular/platform-browser": "~13.3.5",
|
||||
"@angular/platform-browser-dynamic": "~13.3.5",
|
||||
"@angular/platform-server": "~13.3.5",
|
||||
"@angular/router": "~13.3.5",
|
||||
"bootstrap": "^5.1.3",
|
||||
"jquery": "^3.6.0",
|
||||
"oidc-client": "^1.11.5",
|
||||
"popper.js": "^1.16.0",
|
||||
"run-script-os": "^1.1.6",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.1.0",
|
||||
"zone.js": "~0.11.4"
|
||||
"rxjs": "~7.5.5",
|
||||
"tslib": "^2.4.0",
|
||||
"zone.js": "~0.11.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~13.1.2",
|
||||
"@angular/cli": "~13.1.2",
|
||||
"@angular/compiler-cli": "~13.1.1",
|
||||
"@types/jasmine": "~3.6.0",
|
||||
"@types/jasminewd2": "~2.0.8",
|
||||
"@types/node": "^12.11.1",
|
||||
"jasmine-core": "~3.8.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.0.3",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"typescript": "~4.4.4"
|
||||
"@angular-devkit/build-angular": "~13.3.4",
|
||||
"@angular/cli": "~13.3.4",
|
||||
"@angular/compiler-cli": "~13.3.5",
|
||||
"@types/jasmine": "~4.0.3",
|
||||
"@types/jasminewd2": "~2.0.10",
|
||||
"@types/node": "^17.0.29",
|
||||
"jasmine-core": "~4.1.0",
|
||||
"karma": "~6.3.19",
|
||||
"karma-chrome-launcher": "~3.1.1",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.7.0",
|
||||
"typescript": "~4.6.3"
|
||||
},
|
||||
"optionalDependencies": {}
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -4,42 +4,46 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.1.3",
|
||||
"http-proxy-middleware": "^0.19.1",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"jquery": "^3.6.0",
|
||||
"merge": "^2.1.1",
|
||||
"oidc-client": "^1.11.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-bootstrap": "^0.25.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "^5.0.0",
|
||||
"reactstrap": "^8.9.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"web-vitals": "^0.2.4",
|
||||
"workbox-background-sync": "^5.1.3",
|
||||
"workbox-broadcast-update": "^5.1.3",
|
||||
"workbox-cacheable-response": "^5.1.3",
|
||||
"workbox-core": "^5.1.3",
|
||||
"workbox-expiration": "^5.1.3",
|
||||
"workbox-google-analytics": "^5.1.3",
|
||||
"workbox-navigation-preload": "^5.1.3",
|
||||
"workbox-precaching": "^5.1.3",
|
||||
"workbox-range-requests": "^5.1.3",
|
||||
"workbox-routing": "^5.1.3",
|
||||
"workbox-strategies": "^5.1.3",
|
||||
"workbox-streams": "^5.1.3"
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-router-bootstrap": "^0.26.1",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-scripts": "^5.0.1",
|
||||
"reactstrap": "^9.0.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"web-vitals": "^2.1.4",
|
||||
"workbox-background-sync": "^6.5.3",
|
||||
"workbox-broadcast-update": "^6.5.3",
|
||||
"workbox-cacheable-response": "^6.5.3",
|
||||
"workbox-core": "^6.5.3",
|
||||
"workbox-expiration": "^6.5.3",
|
||||
"workbox-google-analytics": "^6.5.3",
|
||||
"workbox-navigation-preload": "^6.5.3",
|
||||
"workbox-precaching": "^6.5.3",
|
||||
"workbox-range-requests": "^6.5.3",
|
||||
"workbox-routing": "^6.5.3",
|
||||
"workbox-strategies": "^6.5.3",
|
||||
"workbox-streams": "^6.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ajv": "^6.9.1",
|
||||
"ajv": "^8.11.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^7.25.0",
|
||||
"eslint-config-react-app": "^6.0.0",
|
||||
"eslint-plugin-flowtype": "^5.7.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-react": "^7.23.2",
|
||||
"nan": "^2.14.2",
|
||||
"typescript": "^4.2.4"
|
||||
"eslint": "^8.14.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-flowtype": "^8.0.3",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-react": "^7.29.4",
|
||||
"nan": "^2.15.0",
|
||||
"typescript": "^4.6.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"css-what": "^5.0.1",
|
||||
"nth-check": "^3.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
//#if(RequiresHttps)
|
||||
|
|
|
@ -1,32 +1,29 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Route } from 'react-router';
|
||||
import { Layout } from './components/Layout';
|
||||
import { Home } from './components/Home';
|
||||
import { FetchData } from './components/FetchData';
|
||||
import { Counter } from './components/Counter';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import AppRoutes from './AppRoutes';
|
||||
////#if (IndividualLocalAuth)
|
||||
import AuthorizeRoute from './components/api-authorization/AuthorizeRoute';
|
||||
import ApiAuthorizationRoutes from './components/api-authorization/ApiAuthorizationRoutes';
|
||||
import { ApplicationPaths } from './components/api-authorization/ApiAuthorizationConstants';
|
||||
////#endif
|
||||
|
||||
import './custom.css'
|
||||
import { Layout } from './components/Layout';
|
||||
import './custom.css';
|
||||
|
||||
export default class App extends Component {
|
||||
static displayName = App.name;
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<Layout>
|
||||
<Route exact path='/' component={Home} />
|
||||
<Route path='/counter' component={Counter} />
|
||||
<Routes>
|
||||
{AppRoutes.map((route, index) => {
|
||||
////#if (!IndividualLocalAuth)
|
||||
<Route path='/fetch-data' component={FetchData} />
|
||||
////#endif
|
||||
////#if (IndividualLocalAuth)
|
||||
<AuthorizeRoute path='/fetch-data' component={FetchData} />
|
||||
<Route path={ApplicationPaths.ApiAuthorizationPrefix} component={ApiAuthorizationRoutes} />
|
||||
const { element, ...rest } = route;
|
||||
return <Route key={index} {...rest} element={element} />;
|
||||
////#else
|
||||
const { element, requireAuth, ...rest } = route;
|
||||
return <Route key={index} {...rest} element={requireAuth ? <AuthorizeRoute {...rest} element={element} /> : element} />;
|
||||
////#endif
|
||||
})}
|
||||
</Routes>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', async () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
const root = createRoot(div);
|
||||
root.render(
|
||||
<MemoryRouter>
|
||||
<App />
|
||||
</MemoryRouter>, div);
|
||||
</MemoryRouter>);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
});
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
////#if (IndividualLocalAuth)
|
||||
import ApiAuthorzationRoutes from './components/api-authorization/ApiAuthorizationRoutes';
|
||||
////#endif
|
||||
import { Counter } from "./components/Counter";
|
||||
import { FetchData } from "./components/FetchData";
|
||||
import { Home } from "./components/Home";
|
||||
|
||||
const AppRoutes = [
|
||||
{
|
||||
index: true,
|
||||
element: <Home />
|
||||
},
|
||||
{
|
||||
path: '/counter',
|
||||
element: <Counter />
|
||||
},
|
||||
////#if (!IndividualLocalAuth)
|
||||
{
|
||||
path: '/fetch-data',
|
||||
element: <FetchData />
|
||||
}
|
||||
////#else
|
||||
{
|
||||
path: '/fetch-data',
|
||||
requireAuth: true,
|
||||
element: <FetchData />
|
||||
},
|
||||
...ApiAuthorzationRoutes
|
||||
////#endif
|
||||
];
|
||||
|
||||
export default AppRoutes;
|
|
@ -3,7 +3,7 @@ import React, { Component } from 'react';
|
|||
export class Home extends Component {
|
||||
static displayName = Home.name;
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello, world!</h1>
|
||||
|
|
|
@ -5,7 +5,7 @@ import { NavMenu } from './NavMenu';
|
|||
export class Layout extends Component {
|
||||
static displayName = Layout.name;
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<NavMenu />
|
||||
|
|
|
@ -24,7 +24,7 @@ export class NavMenu extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<header>
|
||||
<Navbar className="navbar-expand-sm navbar-toggleable-sm ng-white border-bottom box-shadow mb-3" light>
|
||||
|
|
|
@ -1,30 +1,49 @@
|
|||
import React, { Component, Fragment } from 'react';
|
||||
import { Route } from 'react-router';
|
||||
import React from 'react';
|
||||
import { Login } from './Login'
|
||||
import { Logout } from './Logout'
|
||||
import { ApplicationPaths, LoginActions, LogoutActions } from './ApiAuthorizationConstants';
|
||||
|
||||
export default class ApiAuthorizationRoutes extends Component {
|
||||
|
||||
render () {
|
||||
return(
|
||||
<Fragment>
|
||||
<Route path={ApplicationPaths.Login} render={() => loginAction(LoginActions.Login)} />
|
||||
<Route path={ApplicationPaths.LoginFailed} render={() => loginAction(LoginActions.LoginFailed)} />
|
||||
<Route path={ApplicationPaths.LoginCallback} render={() => loginAction(LoginActions.LoginCallback)} />
|
||||
<Route path={ApplicationPaths.Profile} render={() => loginAction(LoginActions.Profile)} />
|
||||
<Route path={ApplicationPaths.Register} render={() => loginAction(LoginActions.Register)} />
|
||||
<Route path={ApplicationPaths.LogOut} render={() => logoutAction(LogoutActions.Logout)} />
|
||||
<Route path={ApplicationPaths.LogOutCallback} render={() => logoutAction(LogoutActions.LogoutCallback)} />
|
||||
<Route path={ApplicationPaths.LoggedOut} render={() => logoutAction(LogoutActions.LoggedOut)} />
|
||||
</Fragment>);
|
||||
const ApiAuthorizationRoutes = [
|
||||
{
|
||||
path: ApplicationPaths.Login,
|
||||
element: loginAction(LoginActions.Login)
|
||||
},
|
||||
{
|
||||
path: ApplicationPaths.LoginFailed,
|
||||
element: loginAction(LoginActions.LoginFailed)
|
||||
},
|
||||
{
|
||||
path: ApplicationPaths.LoginCallback,
|
||||
element: loginAction(LoginActions.LoginCallback)
|
||||
},
|
||||
{
|
||||
path: ApplicationPaths.Profile,
|
||||
element: loginAction(LoginActions.Profile)
|
||||
},
|
||||
{
|
||||
path: ApplicationPaths.Register,
|
||||
element: loginAction(LoginActions.Register)
|
||||
},
|
||||
{
|
||||
path: ApplicationPaths.LogOut,
|
||||
element: logoutAction(LogoutActions.Logout)
|
||||
},
|
||||
{
|
||||
path: ApplicationPaths.LogOutCallback,
|
||||
element: logoutAction(LogoutActions.LogoutCallback)
|
||||
},
|
||||
{
|
||||
path: ApplicationPaths.LoggedOut,
|
||||
element: logoutAction(LogoutActions.LoggedOut)
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function loginAction(name){
|
||||
return (<Login action={name}></Login>);
|
||||
return <Login action={name}></Login>;
|
||||
}
|
||||
|
||||
function logoutAction(name) {
|
||||
return (<Logout action={name}></Logout>);
|
||||
return <Logout action={name}></Logout>;
|
||||
}
|
||||
|
||||
export default ApiAuthorizationRoutes;
|
||||
|
|
|
@ -1,56 +1,49 @@
|
|||
import React from 'react'
|
||||
import { Component } from 'react'
|
||||
import { Route, Redirect } from 'react-router-dom'
|
||||
import { Navigate } from 'react-router-dom'
|
||||
import { ApplicationPaths, QueryParameterNames } from './ApiAuthorizationConstants'
|
||||
import authService from './AuthorizeService'
|
||||
|
||||
export default class AuthorizeRoute extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
ready: false,
|
||||
authenticated: false
|
||||
};
|
||||
}
|
||||
this.state = {
|
||||
ready: false,
|
||||
authenticated: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._subscription = authService.subscribe(() => this.authenticationChanged());
|
||||
this.populateAuthenticationState();
|
||||
}
|
||||
componentDidMount() {
|
||||
this._subscription = authService.subscribe(() => this.authenticationChanged());
|
||||
this.populateAuthenticationState();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
authService.unsubscribe(this._subscription);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
authService.unsubscribe(this._subscription);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { ready, authenticated } = this.state;
|
||||
var link = document.createElement("a");
|
||||
link.href = this.props.path;
|
||||
const returnUrl = `${link.protocol}//${link.host}${link.pathname}${link.search}${link.hash}`;
|
||||
const redirectUrl = `${ApplicationPaths.Login}?${QueryParameterNames.ReturnUrl}=${encodeURIComponent(returnUrl)}`
|
||||
if (!ready) {
|
||||
return <div></div>;
|
||||
} else {
|
||||
const { component: Component, ...rest } = this.props;
|
||||
return <Route {...rest}
|
||||
render={(props) => {
|
||||
if (authenticated) {
|
||||
return <Component {...props} />
|
||||
} else {
|
||||
return <Redirect to={redirectUrl} />
|
||||
}
|
||||
}} />
|
||||
}
|
||||
render() {
|
||||
const { ready, authenticated } = this.state;
|
||||
var link = document.createElement("a");
|
||||
link.href = this.props.path;
|
||||
const returnUrl = `${link.protocol}//${link.host}${link.pathname}${link.search}${link.hash}`;
|
||||
const redirectUrl = `${ApplicationPaths.Login}?${QueryParameterNames.ReturnUrl}=${encodeURIComponent(returnUrl)}`;
|
||||
if (!ready) {
|
||||
return <div></div>;
|
||||
} else {
|
||||
const { element } = this.props;
|
||||
return authenticated ? element : <Navigate replace to={redirectUrl} />;
|
||||
}
|
||||
}
|
||||
|
||||
async populateAuthenticationState() {
|
||||
const authenticated = await authService.isAuthenticated();
|
||||
this.setState({ ready: true, authenticated });
|
||||
}
|
||||
async populateAuthenticationState() {
|
||||
const authenticated = await authService.isAuthenticated();
|
||||
this.setState({ ready: true, authenticated });
|
||||
}
|
||||
|
||||
async authenticationChanged() {
|
||||
this.setState({ ready: false, authenticated: false });
|
||||
await this.populateAuthenticationState();
|
||||
}
|
||||
async authenticationChanged() {
|
||||
this.setState({ ready: false, authenticated: false });
|
||||
await this.populateAuthenticationState();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,204 +2,204 @@ import { UserManager, WebStorageStateStore } from 'oidc-client';
|
|||
import { ApplicationPaths, ApplicationName } from './ApiAuthorizationConstants';
|
||||
|
||||
export class AuthorizeService {
|
||||
_callbacks = [];
|
||||
_nextSubscriptionId = 0;
|
||||
_user = null;
|
||||
_isAuthenticated = false;
|
||||
_callbacks = [];
|
||||
_nextSubscriptionId = 0;
|
||||
_user = null;
|
||||
_isAuthenticated = false;
|
||||
|
||||
// By default pop ups are disabled because they don't work properly on Edge.
|
||||
// If you want to enable pop up authentication simply set this flag to false.
|
||||
_popUpDisabled = true;
|
||||
// By default pop ups are disabled because they don't work properly on Edge.
|
||||
// If you want to enable pop up authentication simply set this flag to false.
|
||||
_popUpDisabled = true;
|
||||
|
||||
async isAuthenticated() {
|
||||
const user = await this.getUser();
|
||||
return !!user;
|
||||
async isAuthenticated() {
|
||||
const user = await this.getUser();
|
||||
return !!user;
|
||||
}
|
||||
|
||||
async getUser() {
|
||||
if (this._user && this._user.profile) {
|
||||
return this._user.profile;
|
||||
}
|
||||
|
||||
async getUser() {
|
||||
if (this._user && this._user.profile) {
|
||||
return this._user.profile;
|
||||
await this.ensureUserManagerInitialized();
|
||||
const user = await this.userManager.getUser();
|
||||
return user && user.profile;
|
||||
}
|
||||
|
||||
async getAccessToken() {
|
||||
await this.ensureUserManagerInitialized();
|
||||
const user = await this.userManager.getUser();
|
||||
return user && user.access_token;
|
||||
}
|
||||
|
||||
// We try to authenticate the user in three different ways:
|
||||
// 1) We try to see if we can authenticate the user silently. This happens
|
||||
// when the user is already logged in on the IdP and is done using a hidden iframe
|
||||
// on the client.
|
||||
// 2) We try to authenticate the user using a PopUp Window. This might fail if there is a
|
||||
// Pop-Up blocker or the user has disabled PopUps.
|
||||
// 3) If the two methods above fail, we redirect the browser to the IdP to perform a traditional
|
||||
// redirect flow.
|
||||
async signIn(state) {
|
||||
await this.ensureUserManagerInitialized();
|
||||
try {
|
||||
const silentUser = await this.userManager.signinSilent(this.createArguments());
|
||||
this.updateState(silentUser);
|
||||
return this.success(state);
|
||||
} catch (silentError) {
|
||||
// User might not be authenticated, fallback to popup authentication
|
||||
console.log("Silent authentication error: ", silentError);
|
||||
|
||||
try {
|
||||
if (this._popUpDisabled) {
|
||||
throw new Error('Popup disabled. Change \'AuthorizeService.js:AuthorizeService._popupDisabled\' to false to enable it.')
|
||||
}
|
||||
|
||||
await this.ensureUserManagerInitialized();
|
||||
const user = await this.userManager.getUser();
|
||||
return user && user.profile;
|
||||
}
|
||||
const popUpUser = await this.userManager.signinPopup(this.createArguments());
|
||||
this.updateState(popUpUser);
|
||||
return this.success(state);
|
||||
} catch (popUpError) {
|
||||
if (popUpError.message === "Popup window closed") {
|
||||
// The user explicitly cancelled the login action by closing an opened popup.
|
||||
return this.error("The user closed the window.");
|
||||
} else if (!this._popUpDisabled) {
|
||||
console.log("Popup authentication error: ", popUpError);
|
||||
}
|
||||
|
||||
async getAccessToken() {
|
||||
await this.ensureUserManagerInitialized();
|
||||
const user = await this.userManager.getUser();
|
||||
return user && user.access_token;
|
||||
}
|
||||
|
||||
// We try to authenticate the user in three different ways:
|
||||
// 1) We try to see if we can authenticate the user silently. This happens
|
||||
// when the user is already logged in on the IdP and is done using a hidden iframe
|
||||
// on the client.
|
||||
// 2) We try to authenticate the user using a PopUp Window. This might fail if there is a
|
||||
// Pop-Up blocker or the user has disabled PopUps.
|
||||
// 3) If the two methods above fail, we redirect the browser to the IdP to perform a traditional
|
||||
// redirect flow.
|
||||
async signIn(state) {
|
||||
await this.ensureUserManagerInitialized();
|
||||
// PopUps might be blocked by the user, fallback to redirect
|
||||
try {
|
||||
const silentUser = await this.userManager.signinSilent(this.createArguments());
|
||||
this.updateState(silentUser);
|
||||
return this.success(state);
|
||||
} catch (silentError) {
|
||||
// User might not be authenticated, fallback to popup authentication
|
||||
console.log("Silent authentication error: ", silentError);
|
||||
|
||||
try {
|
||||
if (this._popUpDisabled) {
|
||||
throw new Error('Popup disabled. Change \'AuthorizeService.js:AuthorizeService._popupDisabled\' to false to enable it.')
|
||||
}
|
||||
|
||||
const popUpUser = await this.userManager.signinPopup(this.createArguments());
|
||||
this.updateState(popUpUser);
|
||||
return this.success(state);
|
||||
} catch (popUpError) {
|
||||
if (popUpError.message === "Popup window closed") {
|
||||
// The user explicitly cancelled the login action by closing an opened popup.
|
||||
return this.error("The user closed the window.");
|
||||
} else if (!this._popUpDisabled) {
|
||||
console.log("Popup authentication error: ", popUpError);
|
||||
}
|
||||
|
||||
// PopUps might be blocked by the user, fallback to redirect
|
||||
try {
|
||||
await this.userManager.signinRedirect(this.createArguments(state));
|
||||
return this.redirect();
|
||||
} catch (redirectError) {
|
||||
console.log("Redirect authentication error: ", redirectError);
|
||||
return this.error(redirectError);
|
||||
}
|
||||
}
|
||||
await this.userManager.signinRedirect(this.createArguments(state));
|
||||
return this.redirect();
|
||||
} catch (redirectError) {
|
||||
console.log("Redirect authentication error: ", redirectError);
|
||||
return this.error(redirectError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async completeSignIn(url) {
|
||||
try {
|
||||
await this.ensureUserManagerInitialized();
|
||||
const user = await this.userManager.signinCallback(url);
|
||||
this.updateState(user);
|
||||
return this.success(user && user.state);
|
||||
} catch (error) {
|
||||
console.log('There was an error signing in: ', error);
|
||||
return this.error('There was an error signing in.');
|
||||
}
|
||||
}
|
||||
|
||||
// We try to sign out the user in two different ways:
|
||||
// 1) We try to do a sign-out using a PopUp Window. This might fail if there is a
|
||||
// Pop-Up blocker or the user has disabled PopUps.
|
||||
// 2) If the method above fails, we redirect the browser to the IdP to perform a traditional
|
||||
// post logout redirect flow.
|
||||
async signOut(state) {
|
||||
await this.ensureUserManagerInitialized();
|
||||
try {
|
||||
if (this._popUpDisabled) {
|
||||
throw new Error('Popup disabled. Change \'AuthorizeService.js:AuthorizeService._popupDisabled\' to false to enable it.')
|
||||
}
|
||||
|
||||
await this.userManager.signoutPopup(this.createArguments());
|
||||
this.updateState(undefined);
|
||||
return this.success(state);
|
||||
} catch (popupSignOutError) {
|
||||
console.log("Popup signout error: ", popupSignOutError);
|
||||
try {
|
||||
await this.userManager.signoutRedirect(this.createArguments(state));
|
||||
return this.redirect();
|
||||
} catch (redirectSignOutError) {
|
||||
console.log("Redirect signout error: ", redirectSignOutError);
|
||||
return this.error(redirectSignOutError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async completeSignOut(url) {
|
||||
await this.ensureUserManagerInitialized();
|
||||
try {
|
||||
const response = await this.userManager.signoutCallback(url);
|
||||
this.updateState(null);
|
||||
return this.success(response && response.data);
|
||||
} catch (error) {
|
||||
console.log(`There was an error trying to log out '${error}'.`);
|
||||
return this.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
updateState(user) {
|
||||
this._user = user;
|
||||
this._isAuthenticated = !!this._user;
|
||||
this.notifySubscribers();
|
||||
}
|
||||
|
||||
subscribe(callback) {
|
||||
this._callbacks.push({ callback, subscription: this._nextSubscriptionId++ });
|
||||
return this._nextSubscriptionId - 1;
|
||||
}
|
||||
|
||||
unsubscribe(subscriptionId) {
|
||||
const subscriptionIndex = this._callbacks
|
||||
.map((element, index) => element.subscription === subscriptionId ? { found: true, index } : { found: false })
|
||||
.filter(element => element.found === true);
|
||||
if (subscriptionIndex.length !== 1) {
|
||||
throw new Error(`Found an invalid number of subscriptions ${subscriptionIndex.length}`);
|
||||
}
|
||||
|
||||
async completeSignIn(url) {
|
||||
try {
|
||||
await this.ensureUserManagerInitialized();
|
||||
const user = await this.userManager.signinCallback(url);
|
||||
this.updateState(user);
|
||||
return this.success(user && user.state);
|
||||
} catch (error) {
|
||||
console.log('There was an error signing in: ', error);
|
||||
return this.error('There was an error signing in.');
|
||||
}
|
||||
this._callbacks.splice(subscriptionIndex[0].index, 1);
|
||||
}
|
||||
|
||||
notifySubscribers() {
|
||||
for (let i = 0; i < this._callbacks.length; i++) {
|
||||
const callback = this._callbacks[i].callback;
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
createArguments(state) {
|
||||
return { useReplaceToNavigate: true, data: state };
|
||||
}
|
||||
|
||||
error(message) {
|
||||
return { status: AuthenticationResultStatus.Fail, message };
|
||||
}
|
||||
|
||||
success(state) {
|
||||
return { status: AuthenticationResultStatus.Success, state };
|
||||
}
|
||||
|
||||
redirect() {
|
||||
return { status: AuthenticationResultStatus.Redirect };
|
||||
}
|
||||
|
||||
async ensureUserManagerInitialized() {
|
||||
if (this.userManager !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We try to sign out the user in two different ways:
|
||||
// 1) We try to do a sign-out using a PopUp Window. This might fail if there is a
|
||||
// Pop-Up blocker or the user has disabled PopUps.
|
||||
// 2) If the method above fails, we redirect the browser to the IdP to perform a traditional
|
||||
// post logout redirect flow.
|
||||
async signOut(state) {
|
||||
await this.ensureUserManagerInitialized();
|
||||
try {
|
||||
if (this._popUpDisabled) {
|
||||
throw new Error('Popup disabled. Change \'AuthorizeService.js:AuthorizeService._popupDisabled\' to false to enable it.')
|
||||
}
|
||||
|
||||
await this.userManager.signoutPopup(this.createArguments());
|
||||
this.updateState(undefined);
|
||||
return this.success(state);
|
||||
} catch (popupSignOutError) {
|
||||
console.log("Popup signout error: ", popupSignOutError);
|
||||
try {
|
||||
await this.userManager.signoutRedirect(this.createArguments(state));
|
||||
return this.redirect();
|
||||
} catch (redirectSignOutError) {
|
||||
console.log("Redirect signout error: ", redirectSignOutError);
|
||||
return this.error(redirectSignOutError);
|
||||
}
|
||||
}
|
||||
let response = await fetch(ApplicationPaths.ApiAuthorizationClientConfigurationUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Could not load settings for '${ApplicationName}'`);
|
||||
}
|
||||
|
||||
async completeSignOut(url) {
|
||||
await this.ensureUserManagerInitialized();
|
||||
try {
|
||||
const response = await this.userManager.signoutCallback(url);
|
||||
this.updateState(null);
|
||||
return this.success(response && response.data);
|
||||
} catch (error) {
|
||||
console.log(`There was an error trying to log out '${error}'.`);
|
||||
return this.error(error);
|
||||
}
|
||||
}
|
||||
let settings = await response.json();
|
||||
settings.automaticSilentRenew = true;
|
||||
settings.includeIdTokenInSilentRenew = true;
|
||||
settings.userStore = new WebStorageStateStore({
|
||||
prefix: ApplicationName
|
||||
});
|
||||
|
||||
updateState(user) {
|
||||
this._user = user;
|
||||
this._isAuthenticated = !!this._user;
|
||||
this.notifySubscribers();
|
||||
}
|
||||
this.userManager = new UserManager(settings);
|
||||
|
||||
subscribe(callback) {
|
||||
this._callbacks.push({ callback, subscription: this._nextSubscriptionId++ });
|
||||
return this._nextSubscriptionId - 1;
|
||||
}
|
||||
this.userManager.events.addUserSignedOut(async () => {
|
||||
await this.userManager.removeUser();
|
||||
this.updateState(undefined);
|
||||
});
|
||||
}
|
||||
|
||||
unsubscribe(subscriptionId) {
|
||||
const subscriptionIndex = this._callbacks
|
||||
.map((element, index) => element.subscription === subscriptionId ? { found: true, index } : { found: false })
|
||||
.filter(element => element.found === true);
|
||||
if (subscriptionIndex.length !== 1) {
|
||||
throw new Error(`Found an invalid number of subscriptions ${subscriptionIndex.length}`);
|
||||
}
|
||||
|
||||
this._callbacks.splice(subscriptionIndex[0].index, 1);
|
||||
}
|
||||
|
||||
notifySubscribers() {
|
||||
for (let i = 0; i < this._callbacks.length; i++) {
|
||||
const callback = this._callbacks[i].callback;
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
createArguments(state) {
|
||||
return { useReplaceToNavigate: true, data: state };
|
||||
}
|
||||
|
||||
error(message) {
|
||||
return { status: AuthenticationResultStatus.Fail, message };
|
||||
}
|
||||
|
||||
success(state) {
|
||||
return { status: AuthenticationResultStatus.Success, state };
|
||||
}
|
||||
|
||||
redirect() {
|
||||
return { status: AuthenticationResultStatus.Redirect };
|
||||
}
|
||||
|
||||
async ensureUserManagerInitialized() {
|
||||
if (this.userManager !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let response = await fetch(ApplicationPaths.ApiAuthorizationClientConfigurationUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Could not load settings for '${ApplicationName}'`);
|
||||
}
|
||||
|
||||
let settings = await response.json();
|
||||
settings.automaticSilentRenew = true;
|
||||
settings.includeIdTokenInSilentRenew = true;
|
||||
settings.userStore = new WebStorageStateStore({
|
||||
prefix: ApplicationName
|
||||
});
|
||||
|
||||
this.userManager = new UserManager(settings);
|
||||
|
||||
this.userManager.events.addUserSignedOut(async () => {
|
||||
await this.userManager.removeUser();
|
||||
this.updateState(undefined);
|
||||
});
|
||||
}
|
||||
|
||||
static get instance() { return authService }
|
||||
static get instance() { return authService }
|
||||
}
|
||||
|
||||
const authService = new AuthorizeService();
|
||||
|
@ -207,7 +207,7 @@ const authService = new AuthorizeService();
|
|||
export default authService;
|
||||
|
||||
export const AuthenticationResultStatus = {
|
||||
Redirect: 'redirect',
|
||||
Success: 'success',
|
||||
Fail: 'fail'
|
||||
Redirect: 'redirect',
|
||||
Success: 'success',
|
||||
Fail: 'fail'
|
||||
};
|
||||
|
|
|
@ -9,125 +9,125 @@ import { LoginActions, QueryParameterNames, ApplicationPaths } from './ApiAuthor
|
|||
// a user can simply perform a redirect to this component with a returnUrl query parameter and
|
||||
// let the component perform the login and return back to the return url.
|
||||
export class Login extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
message: undefined
|
||||
};
|
||||
}
|
||||
this.state = {
|
||||
message: undefined
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const action = this.props.action;
|
||||
switch (action) {
|
||||
case LoginActions.Login:
|
||||
this.login(this.getReturnUrl());
|
||||
break;
|
||||
case LoginActions.LoginCallback:
|
||||
this.processLoginCallback();
|
||||
break;
|
||||
case LoginActions.LoginFailed:
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const error = params.get(QueryParameterNames.Message);
|
||||
this.setState({ message: error });
|
||||
break;
|
||||
case LoginActions.Profile:
|
||||
this.redirectToProfile();
|
||||
break;
|
||||
case LoginActions.Register:
|
||||
this.redirectToRegister();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const action = this.props.action;
|
||||
const { message } = this.state;
|
||||
|
||||
if (!!message) {
|
||||
return <div>{message}</div>
|
||||
} else {
|
||||
switch (action) {
|
||||
case LoginActions.Login:
|
||||
return (<div>Processing login</div>);
|
||||
case LoginActions.LoginCallback:
|
||||
return (<div>Processing login callback</div>);
|
||||
case LoginActions.Profile:
|
||||
case LoginActions.Register:
|
||||
return (<div></div>);
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async login(returnUrl) {
|
||||
const state = { returnUrl };
|
||||
const result = await authService.signIn(state);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
break;
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(returnUrl);
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.setState({ message: result.message });
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid status result ${result.status}.`);
|
||||
}
|
||||
}
|
||||
|
||||
async processLoginCallback() {
|
||||
const url = window.location.href;
|
||||
const result = await authService.completeSignIn(url);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
// There should not be any redirects as the only time completeSignIn finishes
|
||||
// is when we are doing a redirect sign in flow.
|
||||
throw new Error('Should not redirect.');
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(this.getReturnUrl(result.state));
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.setState({ message: result.message });
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid authentication result status '${result.status}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
getReturnUrl(state) {
|
||||
componentDidMount() {
|
||||
const action = this.props.action;
|
||||
switch (action) {
|
||||
case LoginActions.Login:
|
||||
this.login(this.getReturnUrl());
|
||||
break;
|
||||
case LoginActions.LoginCallback:
|
||||
this.processLoginCallback();
|
||||
break;
|
||||
case LoginActions.LoginFailed:
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const fromQuery = params.get(QueryParameterNames.ReturnUrl);
|
||||
if (fromQuery && !fromQuery.startsWith(`${window.location.origin}/`)) {
|
||||
// This is an extra check to prevent open redirects.
|
||||
throw new Error("Invalid return url. The return url needs to have the same origin as the current page.")
|
||||
}
|
||||
return (state && state.returnUrl) || fromQuery || `${window.location.origin}/`;
|
||||
const error = params.get(QueryParameterNames.Message);
|
||||
this.setState({ message: error });
|
||||
break;
|
||||
case LoginActions.Profile:
|
||||
this.redirectToProfile();
|
||||
break;
|
||||
case LoginActions.Register:
|
||||
this.redirectToRegister();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
}
|
||||
|
||||
redirectToRegister() {
|
||||
this.redirectToApiAuthorizationPath(`${ApplicationPaths.IdentityRegisterPath}?${QueryParameterNames.ReturnUrl}=${encodeURI(ApplicationPaths.Login)}`);
|
||||
}
|
||||
render() {
|
||||
const action = this.props.action;
|
||||
const { message } = this.state;
|
||||
|
||||
redirectToProfile() {
|
||||
this.redirectToApiAuthorizationPath(ApplicationPaths.IdentityManagePath);
|
||||
if (!!message) {
|
||||
return <div>{message}</div>
|
||||
} else {
|
||||
switch (action) {
|
||||
case LoginActions.Login:
|
||||
return (<div>Processing login</div>);
|
||||
case LoginActions.LoginCallback:
|
||||
return (<div>Processing login callback</div>);
|
||||
case LoginActions.Profile:
|
||||
case LoginActions.Register:
|
||||
return (<div></div>);
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redirectToApiAuthorizationPath(apiAuthorizationPath) {
|
||||
const redirectUrl = `${window.location.origin}/${apiAuthorizationPath}`;
|
||||
// It's important that we do a replace here so that when the user hits the back arrow on the
|
||||
// browser they get sent back to where it was on the app instead of to an endpoint on this
|
||||
// component.
|
||||
window.location.replace(redirectUrl);
|
||||
async login(returnUrl) {
|
||||
const state = { returnUrl };
|
||||
const result = await authService.signIn(state);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
break;
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(returnUrl);
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.setState({ message: result.message });
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid status result ${result.status}.`);
|
||||
}
|
||||
}
|
||||
|
||||
navigateToReturnUrl(returnUrl) {
|
||||
// It's important that we do a replace here so that we remove the callback uri with the
|
||||
// fragment containing the tokens from the browser history.
|
||||
window.location.replace(returnUrl);
|
||||
async processLoginCallback() {
|
||||
const url = window.location.href;
|
||||
const result = await authService.completeSignIn(url);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
// There should not be any redirects as the only time completeSignIn finishes
|
||||
// is when we are doing a redirect sign in flow.
|
||||
throw new Error('Should not redirect.');
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(this.getReturnUrl(result.state));
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.setState({ message: result.message });
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid authentication result status '${result.status}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
getReturnUrl(state) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const fromQuery = params.get(QueryParameterNames.ReturnUrl);
|
||||
if (fromQuery && !fromQuery.startsWith(`${window.location.origin}/`)) {
|
||||
// This is an extra check to prevent open redirects.
|
||||
throw new Error("Invalid return url. The return url needs to have the same origin as the current page.")
|
||||
}
|
||||
return (state && state.returnUrl) || fromQuery || `${window.location.origin}/`;
|
||||
}
|
||||
|
||||
redirectToRegister() {
|
||||
this.redirectToApiAuthorizationPath(`${ApplicationPaths.IdentityRegisterPath}?${QueryParameterNames.ReturnUrl}=${encodeURI(ApplicationPaths.Login)}`);
|
||||
}
|
||||
|
||||
redirectToProfile() {
|
||||
this.redirectToApiAuthorizationPath(ApplicationPaths.IdentityManagePath);
|
||||
}
|
||||
|
||||
redirectToApiAuthorizationPath(apiAuthorizationPath) {
|
||||
const redirectUrl = `${window.location.origin}/${apiAuthorizationPath}`;
|
||||
// It's important that we do a replace here so that when the user hits the back arrow on the
|
||||
// browser they get sent back to where it was on the app instead of to an endpoint on this
|
||||
// component.
|
||||
window.location.replace(redirectUrl);
|
||||
}
|
||||
|
||||
navigateToReturnUrl(returnUrl) {
|
||||
// It's important that we do a replace here so that we remove the callback uri with the
|
||||
// fragment containing the tokens from the browser history.
|
||||
window.location.replace(returnUrl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,65 +5,65 @@ import authService from './AuthorizeService';
|
|||
import { ApplicationPaths } from './ApiAuthorizationConstants';
|
||||
|
||||
export class LoginMenu extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isAuthenticated: false,
|
||||
userName: null
|
||||
};
|
||||
this.state = {
|
||||
isAuthenticated: false,
|
||||
userName: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._subscription = authService.subscribe(() => this.populateState());
|
||||
this.populateState();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
authService.unsubscribe(this._subscription);
|
||||
}
|
||||
|
||||
async populateState() {
|
||||
const [isAuthenticated, user] = await Promise.all([authService.isAuthenticated(), authService.getUser()])
|
||||
this.setState({
|
||||
isAuthenticated,
|
||||
userName: user && user.name
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isAuthenticated, userName } = this.state;
|
||||
if (!isAuthenticated) {
|
||||
const registerPath = `${ApplicationPaths.Register}`;
|
||||
const loginPath = `${ApplicationPaths.Login}`;
|
||||
return this.anonymousView(registerPath, loginPath);
|
||||
} else {
|
||||
const profilePath = `${ApplicationPaths.Profile}`;
|
||||
const logoutPath = `${ApplicationPaths.LogOut}`;
|
||||
const logoutState = { local: true };
|
||||
return this.authenticatedView(userName, profilePath, logoutPath, logoutState);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._subscription = authService.subscribe(() => this.populateState());
|
||||
this.populateState();
|
||||
}
|
||||
authenticatedView(userName, profilePath, logoutPath, logoutState) {
|
||||
return (<Fragment>
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to={profilePath}>Hello {userName}</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink replace tag={Link} className="text-dark" to={logoutPath} state={logoutState}>Logout</NavLink>
|
||||
</NavItem>
|
||||
</Fragment>);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
authService.unsubscribe(this._subscription);
|
||||
}
|
||||
|
||||
async populateState() {
|
||||
const [isAuthenticated, user] = await Promise.all([authService.isAuthenticated(), authService.getUser()])
|
||||
this.setState({
|
||||
isAuthenticated,
|
||||
userName: user && user.name
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isAuthenticated, userName } = this.state;
|
||||
if (!isAuthenticated) {
|
||||
const registerPath = `${ApplicationPaths.Register}`;
|
||||
const loginPath = `${ApplicationPaths.Login}`;
|
||||
return this.anonymousView(registerPath, loginPath);
|
||||
} else {
|
||||
const profilePath = `${ApplicationPaths.Profile}`;
|
||||
const logoutPath = { pathname: `${ApplicationPaths.LogOut}`, state: { local: true } };
|
||||
return this.authenticatedView(userName, profilePath, logoutPath);
|
||||
}
|
||||
}
|
||||
|
||||
authenticatedView(userName, profilePath, logoutPath) {
|
||||
return (<Fragment>
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to={profilePath}>Hello {userName}</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to={logoutPath}>Logout</NavLink>
|
||||
</NavItem>
|
||||
</Fragment>);
|
||||
|
||||
}
|
||||
|
||||
anonymousView(registerPath, loginPath) {
|
||||
return (<Fragment>
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to={registerPath}>Register</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to={loginPath}>Login</NavLink>
|
||||
</NavItem>
|
||||
</Fragment>);
|
||||
}
|
||||
anonymousView(registerPath, loginPath) {
|
||||
return (<Fragment>
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to={registerPath}>Register</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to={loginPath}>Login</NavLink>
|
||||
</NavItem>
|
||||
</Fragment>);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,121 +8,121 @@ import { QueryParameterNames, LogoutActions, ApplicationPaths } from './ApiAutho
|
|||
// This is the starting point for the logout process, which is usually initiated when a
|
||||
// user clicks on the logout button on the LoginMenu component.
|
||||
export class Logout extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
message: undefined,
|
||||
isReady: false,
|
||||
authenticated: false
|
||||
};
|
||||
}
|
||||
this.state = {
|
||||
message: undefined,
|
||||
isReady: false,
|
||||
authenticated: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const action = this.props.action;
|
||||
switch (action) {
|
||||
case LogoutActions.Logout:
|
||||
if (!!window.history.state.state.local) {
|
||||
this.logout(this.getReturnUrl());
|
||||
} else {
|
||||
// This prevents regular links to <app>/authentication/logout from triggering a logout
|
||||
this.setState({ isReady: true, message: "The logout was not initiated from within the page." });
|
||||
}
|
||||
break;
|
||||
case LogoutActions.LogoutCallback:
|
||||
this.processLogoutCallback();
|
||||
break;
|
||||
case LogoutActions.LoggedOut:
|
||||
this.setState({ isReady: true, message: "You successfully logged out!" });
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
|
||||
this.populateAuthenticationState();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isReady, message } = this.state;
|
||||
if (!isReady) {
|
||||
return <div></div>
|
||||
}
|
||||
if (!!message) {
|
||||
return (<div>{message}</div>);
|
||||
componentDidMount() {
|
||||
const action = this.props.action;
|
||||
switch (action) {
|
||||
case LogoutActions.Logout:
|
||||
if (!!window.history.state.usr.local) {
|
||||
this.logout(this.getReturnUrl());
|
||||
} else {
|
||||
const action = this.props.action;
|
||||
switch (action) {
|
||||
case LogoutActions.Logout:
|
||||
return (<div>Processing logout</div>);
|
||||
case LogoutActions.LogoutCallback:
|
||||
return (<div>Processing logout callback</div>);
|
||||
case LogoutActions.LoggedOut:
|
||||
return (<div>{message}</div>);
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
// This prevents regular links to <app>/authentication/logout from triggering a logout
|
||||
this.setState({ isReady: true, message: "The logout was not initiated from within the page." });
|
||||
}
|
||||
break;
|
||||
case LogoutActions.LogoutCallback:
|
||||
this.processLogoutCallback();
|
||||
break;
|
||||
case LogoutActions.LoggedOut:
|
||||
this.setState({ isReady: true, message: "You successfully logged out!" });
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
|
||||
async logout(returnUrl) {
|
||||
const state = { returnUrl };
|
||||
const isauthenticated = await authService.isAuthenticated();
|
||||
if (isauthenticated) {
|
||||
const result = await authService.signOut(state);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
break;
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(returnUrl);
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.setState({ message: result.message });
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid authentication result status.");
|
||||
}
|
||||
} else {
|
||||
this.setState({ message: "You successfully logged out!" });
|
||||
}
|
||||
}
|
||||
this.populateAuthenticationState();
|
||||
}
|
||||
|
||||
async processLogoutCallback() {
|
||||
const url = window.location.href;
|
||||
const result = await authService.completeSignOut(url);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
// There should not be any redirects as the only time completeAuthentication finishes
|
||||
// is when we are doing a redirect sign in flow.
|
||||
throw new Error('Should not redirect.');
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(this.getReturnUrl(result.state));
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.setState({ message: result.message });
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid authentication result status.");
|
||||
}
|
||||
render() {
|
||||
const { isReady, message } = this.state;
|
||||
if (!isReady) {
|
||||
return <div></div>
|
||||
}
|
||||
if (!!message) {
|
||||
return (<div>{message}</div>);
|
||||
} else {
|
||||
const action = this.props.action;
|
||||
switch (action) {
|
||||
case LogoutActions.Logout:
|
||||
return (<div>Processing logout</div>);
|
||||
case LogoutActions.LogoutCallback:
|
||||
return (<div>Processing logout callback</div>);
|
||||
case LogoutActions.LoggedOut:
|
||||
return (<div>{message}</div>);
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async populateAuthenticationState() {
|
||||
const authenticated = await authService.isAuthenticated();
|
||||
this.setState({ isReady: true, authenticated });
|
||||
async logout(returnUrl) {
|
||||
const state = { returnUrl };
|
||||
const isauthenticated = await authService.isAuthenticated();
|
||||
if (isauthenticated) {
|
||||
const result = await authService.signOut(state);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
break;
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(returnUrl);
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.setState({ message: result.message });
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid authentication result status.");
|
||||
}
|
||||
} else {
|
||||
this.setState({ message: "You successfully logged out!" });
|
||||
}
|
||||
}
|
||||
|
||||
getReturnUrl(state) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const fromQuery = params.get(QueryParameterNames.ReturnUrl);
|
||||
if (fromQuery && !fromQuery.startsWith(`${window.location.origin}/`)) {
|
||||
// This is an extra check to prevent open redirects.
|
||||
throw new Error("Invalid return url. The return url needs to have the same origin as the current page.")
|
||||
}
|
||||
return (state && state.returnUrl) ||
|
||||
fromQuery ||
|
||||
`${window.location.origin}${ApplicationPaths.LoggedOut}`;
|
||||
async processLogoutCallback() {
|
||||
const url = window.location.href;
|
||||
const result = await authService.completeSignOut(url);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
// There should not be any redirects as the only time completeAuthentication finishes
|
||||
// is when we are doing a redirect sign in flow.
|
||||
throw new Error('Should not redirect.');
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(this.getReturnUrl(result.state));
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.setState({ message: result.message });
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid authentication result status.");
|
||||
}
|
||||
}
|
||||
|
||||
navigateToReturnUrl(returnUrl) {
|
||||
return window.location.replace(returnUrl);
|
||||
async populateAuthenticationState() {
|
||||
const authenticated = await authService.isAuthenticated();
|
||||
this.setState({ isReady: true, authenticated });
|
||||
}
|
||||
|
||||
getReturnUrl(state) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const fromQuery = params.get(QueryParameterNames.ReturnUrl);
|
||||
if (fromQuery && !fromQuery.startsWith(`${window.location.origin}/`)) {
|
||||
// This is an extra check to prevent open redirects.
|
||||
throw new Error("Invalid return url. The return url needs to have the same origin as the current page.")
|
||||
}
|
||||
return (state && state.returnUrl) ||
|
||||
fromQuery ||
|
||||
`${window.location.origin}${ApplicationPaths.LoggedOut}`;
|
||||
}
|
||||
|
||||
navigateToReturnUrl(returnUrl) {
|
||||
return window.location.replace(returnUrl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'bootstrap/dist/css/bootstrap.css';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import App from './App';
|
||||
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
|
||||
|
@ -8,12 +8,12 @@ import reportWebVitals from './reportWebVitals';
|
|||
|
||||
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
|
||||
const rootElement = document.getElementById('root');
|
||||
const root = createRoot(rootElement);
|
||||
|
||||
ReactDOM.render(
|
||||
root.render(
|
||||
<BrowserRouter basename={baseUrl}>
|
||||
<App />
|
||||
</BrowserRouter>,
|
||||
rootElement);
|
||||
</BrowserRouter>);
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const createProxyMiddleware = require('http-proxy-middleware');
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||
const { env } = require('process');
|
||||
|
||||
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
|
||||
|
|
Загрузка…
Ссылка в новой задаче