Backport JS dependency updates (#50)

This commit is contained in:
Mackinnon Buck 2022-05-03 10:34:15 -07:00 коммит произвёл GitHub
Родитель 08a4e743cc
Коммит bb161e0460
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 10115 добавлений и 14753 удалений

8613
src/content/Angular-CSharp/ClientApp/package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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": {}
}

15015
src/content/React-CSharp/ClientApp/package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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}` :