├── .babelrc ├── .gitignore ├── .vscode └── settings.json ├── ClientApp ├── adal │ └── AuthenticationContext.ts ├── boot-client.tsx ├── boot-server.tsx ├── components │ ├── Auth.tsx │ ├── Counter.tsx │ ├── FetchData.tsx │ ├── Home.tsx │ ├── Layout.tsx │ └── NavMenu.tsx ├── configureStore.ts ├── css │ └── site.css ├── routes.tsx └── store │ ├── Counter.ts │ ├── WeatherForecasts.ts │ └── index.ts ├── Controllers ├── HomeController.cs └── SampleDataController.cs ├── NetCoreReactAzureAD.csproj ├── Program.cs ├── README.md ├── Startup.cs ├── Views ├── Home │ └── Index.cshtml ├── Shared │ ├── Error.cshtml │ └── _Layout.cshtml ├── _ViewImports.cshtml └── _ViewStart.cshtml ├── appsettings.json ├── global.json ├── package.json ├── tsconfig.json ├── web.config ├── webpack.config.js ├── webpack.config.vendor.js └── wwwroot └── favicon.ico /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["es2015", { "modules": false }], "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Properties/launchSettings.json 2 | 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 5 | # Auth Config 6 | *.authConfig.json 7 | /ClientApp/adal/adalConfig.ts 8 | 9 | # User-specific files 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Build results 19 | [Dd]ebug/ 20 | [Dd]ebugPublic/ 21 | [Rr]elease/ 22 | [Rr]eleases/ 23 | x64/ 24 | x86/ 25 | build/ 26 | bld/ 27 | bin/ 28 | Bin/ 29 | obj/ 30 | Obj/ 31 | 32 | # Visual Studio 2015 cache/options directory 33 | .vs/ 34 | /wwwroot/dist/** 35 | /ClientApp/dist/** 36 | 37 | # Workaround for https://github.com/aspnet/JavaScriptServices/issues/235 38 | !/wwwroot/dist/_placeholder.txt 39 | !/ClientApp/dist/_placeholder.txt 40 | 41 | /yarn.lock 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUNIT 48 | *.VisualState.xml 49 | TestResult.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # DNX 57 | project.lock.json 58 | artifacts/ 59 | 60 | *_i.c 61 | *_p.c 62 | *_i.h 63 | *.ilk 64 | *.meta 65 | *.obj 66 | *.pch 67 | *.pdb 68 | *.pgc 69 | *.pgd 70 | *.rsp 71 | *.sbr 72 | *.tlb 73 | *.tli 74 | *.tlh 75 | *.tmp 76 | *.tmp_proj 77 | *.log 78 | *.vspscc 79 | *.vssscc 80 | .builds 81 | *.pidb 82 | *.svclog 83 | *.scc 84 | 85 | # Chutzpah Test files 86 | _Chutzpah* 87 | 88 | # Visual C++ cache files 89 | ipch/ 90 | *.aps 91 | *.ncb 92 | *.opendb 93 | *.opensdf 94 | *.sdf 95 | *.cachefile 96 | 97 | # Visual Studio profiler 98 | *.psess 99 | *.vsp 100 | *.vspx 101 | *.sap 102 | 103 | # TFS 2012 Local Workspace 104 | $tf/ 105 | 106 | # Guidance Automation Toolkit 107 | *.gpState 108 | 109 | # ReSharper is a .NET coding add-in 110 | _ReSharper*/ 111 | *.[Rr]e[Ss]harper 112 | *.DotSettings.user 113 | 114 | # JustCode is a .NET coding add-in 115 | .JustCode 116 | 117 | # TeamCity is a build add-in 118 | _TeamCity* 119 | 120 | # DotCover is a Code Coverage Tool 121 | *.dotCover 122 | 123 | # NCrunch 124 | _NCrunch_* 125 | .*crunch*.local.xml 126 | nCrunchTemp_* 127 | 128 | # MightyMoose 129 | *.mm.* 130 | AutoTest.Net/ 131 | 132 | # Web workbench (sass) 133 | .sass-cache/ 134 | 135 | # Installshield output folder 136 | [Ee]xpress/ 137 | 138 | # DocProject is a documentation generator add-in 139 | DocProject/buildhelp/ 140 | DocProject/Help/*.HxT 141 | DocProject/Help/*.HxC 142 | DocProject/Help/*.hhc 143 | DocProject/Help/*.hhk 144 | DocProject/Help/*.hhp 145 | DocProject/Help/Html2 146 | DocProject/Help/html 147 | 148 | # Click-Once directory 149 | publish/ 150 | 151 | # Publish Web Output 152 | *.[Pp]ublish.xml 153 | *.azurePubxml 154 | # TODO: Comment the next line if you want to checkin your web deploy settings 155 | # but database connection strings (with potential passwords) will be unencrypted 156 | *.pubxml 157 | *.publishproj 158 | 159 | # NuGet Packages 160 | *.nupkg 161 | # The packages folder can be ignored because of Package Restore 162 | **/packages/* 163 | # except build/, which is used as an MSBuild target. 164 | !**/packages/build/ 165 | # Uncomment if necessary however generally it will be regenerated when needed 166 | #!**/packages/repositories.config 167 | 168 | # Microsoft Azure Build Output 169 | csx/ 170 | *.build.csdef 171 | 172 | # Microsoft Azure Emulator 173 | ecf/ 174 | rcf/ 175 | 176 | # Microsoft Azure ApplicationInsights config file 177 | ApplicationInsights.config 178 | 179 | # Windows Store app package directory 180 | AppPackages/ 181 | BundleArtifacts/ 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.pfx 196 | *.publishsettings 197 | orleans.codegen.cs 198 | 199 | /node_modules 200 | 201 | # RIA/Silverlight projects 202 | Generated_Code/ 203 | 204 | # Backup & report files from converting an old project file 205 | # to a newer Visual Studio version. Backup files are not needed, 206 | # because we have git ;-) 207 | _UpgradeReport_Files/ 208 | Backup*/ 209 | UpgradeLog*.XML 210 | UpgradeLog*.htm 211 | 212 | # SQL Server files 213 | *.mdf 214 | *.ldf 215 | 216 | # Business Intelligence projects 217 | *.rdl.data 218 | *.bim.layout 219 | *.bim_*.settings 220 | 221 | # Microsoft Fakes 222 | FakesAssemblies/ 223 | 224 | # GhostDoc plugin setting file 225 | *.GhostDoc.xml 226 | 227 | # Node.js Tools for Visual Studio 228 | .ntvs_analysis.dat 229 | 230 | # Visual Studio 6 build log 231 | *.plg 232 | 233 | # Visual Studio 6 workspace options file 234 | *.opt 235 | 236 | # Visual Studio LightSwitch build output 237 | **/*.HTMLClient/GeneratedArtifacts 238 | **/*.DesktopClient/GeneratedArtifacts 239 | **/*.DesktopClient/ModelManifest.xml 240 | **/*.Server/GeneratedArtifacts 241 | **/*.Server/ModelManifest.xml 242 | _Pvt_Extensions 243 | 244 | # Paket dependency manager 245 | .paket/paket.exe 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "vsicons.presets.angular": false 3 | } -------------------------------------------------------------------------------- /ClientApp/adal/AuthenticationContext.ts: -------------------------------------------------------------------------------- 1 | import AuthenticationContext from 'adal-angular'; 2 | import adalConfig from './adalConfig'; 3 | import q from 'q'; 4 | var _adal = new AuthenticationContext(adalConfig); 5 | var _oauthData = { isAuthenticated: false, userName: '', loginError: '', profile: '' }; 6 | 7 | var processAdalCallback = function() { 8 | 9 | var hash = window.location.hash; 10 | 11 | if (_adal.isCallback(hash)) { 12 | // callback can come from login or iframe request 13 | var requestInfo = _adal.getRequestInfo(hash); 14 | 15 | _adal.saveTokenFromHash(requestInfo); 16 | window.location.hash = ''; 17 | 18 | if (requestInfo.requestType !== _adal.REQUEST_TYPE.LOGIN) { 19 | if ((window as any).parent.AuthenticationContext === 'function' && (window as any).parent.AuthenticationContext()) { 20 | _adal.callback = (window as any).parent.AuthenticationContext().callback; 21 | } 22 | if (requestInfo.requestType === _adal.REQUEST_TYPE.RENEW_TOKEN) { 23 | _adal.callback =(window as any).parent.callBackMappedToRenewStates[requestInfo.stateResponse]; 24 | } 25 | } 26 | 27 | if (requestInfo.stateMatch) { 28 | if (typeof _adal.callback === 'function') { 29 | // Call within the same context without full page redirect keeps the callback 30 | if (requestInfo.requestType === _adal.REQUEST_TYPE.RENEW_TOKEN) { 31 | // Idtoken or Accestoken can be renewed 32 | if (requestInfo.parameters['access_token']) { 33 | _adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['access_token']); 34 | return; 35 | } else if (requestInfo.parameters['id_token']) { 36 | _adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['id_token']); 37 | return; 38 | } 39 | } 40 | } else { 41 | // normal full login redirect happened on the page 42 | updateDataFromCache(_adal.config.loginResource); 43 | if (_oauthData.userName) { 44 | //IDtoken is added as token for the app 45 | window.setTimeout(function() { 46 | updateDataFromCache(_adal.config.loginResource); 47 | // redirect to login requested page 48 | var loginStartPage = _adal._getItem(_adal.CONSTANTS.STORAGE.START_PAGE); 49 | if (loginStartPage) { 50 | (window as any).location.path(loginStartPage); 51 | } 52 | }, 1); 53 | } 54 | } 55 | } 56 | } 57 | } 58 | 59 | var updateDataFromCache = function(resource) { 60 | // only cache lookup here to not interrupt with events 61 | var token = _adal.getCachedToken(resource); 62 | _oauthData.isAuthenticated = token !== null && token.length > 0; 63 | var user = _adal.getCachedUser() || { userName: '' }; 64 | _oauthData.userName = user.userName; 65 | _oauthData.profile = user.profile; 66 | _oauthData.loginError = _adal.getLoginError(); 67 | }; 68 | 69 | var isAuthenticated = function() { 70 | var deferred = q.defer(); 71 | 72 | updateDataFromCache(_adal.config.loginResource); 73 | if (!_adal._renewActive && !_oauthData.isAuthenticated && !_oauthData.userName) { 74 | if (!_adal._getItem(_adal.CONSTANTS.STORAGE.FAILED_RENEW)) { 75 | // Idtoken is expired or not present 76 | _adal.acquireToken(_adal.config.loginResource, function(error, tokenOut) { 77 | if (error) { 78 | _adal.error('adal:loginFailure', 'auto renew failure'); 79 | deferred.reject(); 80 | } 81 | else { 82 | if (tokenOut) { 83 | _oauthData.isAuthenticated = true; 84 | deferred.resolve(); 85 | } 86 | else { 87 | deferred.reject(); 88 | } 89 | } 90 | }); 91 | } 92 | else { 93 | deferred.resolve(); 94 | } 95 | } 96 | else { 97 | deferred.resolve(); 98 | } 99 | 100 | return deferred.promise; 101 | } 102 | 103 | var makeRequest = function(settings) { 104 | var deferred = q.defer(); 105 | 106 | var xhr = new XMLHttpRequest(); 107 | xhr.onreadystatechange = function() { 108 | if (this.readyState === 4) { 109 | if (this.status === 200) { 110 | deferred.resolve(this.response); 111 | } 112 | else if (this.status >= 400) { 113 | deferred.reject(); 114 | } 115 | } 116 | } 117 | xhr.open(settings.method || 'GET', settings.url, true); 118 | 119 | for (var header in settings.headers) { 120 | xhr.setRequestHeader(header, settings.headers[header]); 121 | } 122 | 123 | xhr.responseType = settings.dataType || 'json'; 124 | xhr.send(settings.data); 125 | 126 | return deferred.promise; 127 | } 128 | 129 | var adalRequest = function(settings) { 130 | var deferred = q.defer(); 131 | 132 | isAuthenticated().then(function() { 133 | var resource = _adal.getResourceForEndpoint(settings.url); 134 | 135 | if (!resource) { 136 | _adal.info('No resource configured for \'' + settings.url + '\''); 137 | deferred.reject(); 138 | return deferred.promise; 139 | } 140 | 141 | var tokenStored = _adal.getCachedToken(resource); 142 | if (tokenStored) { 143 | if (!settings.headers) { 144 | settings.headers = {}; 145 | } 146 | 147 | settings.headers.Authorization = 'Bearer ' + tokenStored; 148 | 149 | makeRequest(settings).then(deferred.resolve, deferred.reject); 150 | } 151 | else { 152 | var isEndpoint = false; 153 | 154 | for (var endpointUrl in _adal.config.endpoints) { 155 | if (settings.url.indexOf(endpointUrl) > -1) { 156 | isEndpoint = true; 157 | } 158 | } 159 | 160 | if (_adal.loginInProgress()) { 161 | _adal.info('Login already in progress'); 162 | deferred.reject(); 163 | } 164 | else if (isEndpoint) { 165 | 166 | _adal.acquireToken(resource, function(error, tokenOut) { 167 | if (error) { 168 | debugger; 169 | deferred.reject(); 170 | _adal.error(error); 171 | } 172 | else { 173 | debugger; 174 | if (tokenOut) { 175 | _adal.verbose('Token is available'); 176 | if (!settings.headers) { 177 | settings.headers = {}; 178 | } 179 | settings.headers.Authorization = 'Bearer ' + tokenOut; 180 | makeRequest(settings).then(deferred.resolve, deferred.reject); 181 | } 182 | } 183 | }); 184 | } 185 | } 186 | }, function() { 187 | _adal.login(); 188 | }) 189 | 190 | return deferred.promise; 191 | } 192 | 193 | export {adalRequest, processAdalCallback}; -------------------------------------------------------------------------------- /ClientApp/boot-client.tsx: -------------------------------------------------------------------------------- 1 | import './css/site.css'; 2 | import 'bootstrap'; 3 | import * as React from 'react'; 4 | import * as ReactDOM from 'react-dom'; 5 | import { browserHistory, Router } from 'react-router'; 6 | import { Provider } from 'react-redux'; 7 | import { syncHistoryWithStore } from 'react-router-redux'; 8 | import routes from './routes'; 9 | import configureStore from './configureStore'; 10 | import { ApplicationState } from './store'; 11 | import AuthenticationContext from 'adal-angular'; 12 | import adalConfig from './adal/adalConfig'; 13 | 14 | 15 | import AuthContext from 'adal-angular'; 16 | import axios from "axios"; 17 | 18 | const resourceId = 'https://ashishpslalom.onmicrosoft.com/InvoiceApi'; 19 | // Get the application-wide store instance, prepopulating with state from the server where available. 20 | const initialState = (window as any).initialReduxState as ApplicationState; 21 | const store = configureStore(initialState); 22 | const history = syncHistoryWithStore(browserHistory, store); 23 | 24 | //This is needed because adal.js does a window.parent.AuthenticationContext() in getRequestInfo 25 | (window as any).AuthenticationContext = AuthContext; 26 | 27 | //ClientID is the AzureAD app registration Application ID. This is the one that lists approved reply URLs and keys. 28 | //It has no connection to the backend app, except that the backend app lists the Application ID as the intended audience for the JWT (token) we send. 29 | //Tenant is the AzureAD instance. 30 | //You will need to enable Implicit Grant Flow for the AzureAd Application (The one where you get the ClientId) 31 | //You will also need to add return URLs there, remember to add the auth path 32 | 33 | let authContext = new AuthContext(adalConfig); 34 | 35 | 36 | axios.interceptors.response.use(undefined, err => { 37 | if(err.response.status === 401 && err.response.config && !err.response.config.__isRetryRequest) { 38 | err.response.config.__isRetryRequest = true; 39 | 40 | return new Promise( (resolve, reject) => { 41 | authContext.acquireToken(resourceId, (message, token, msg) => { 42 | axios.defaults.headers.common['Authorization'] = `Bearer ${token}`; 43 | err.config.headers.Authorization = `Bearer ${token}`; 44 | axios(err.config).then(resolve, reject); 45 | }); 46 | }); 47 | } 48 | throw err; 49 | }); 50 | 51 | //This can safely be called whenever, as it doesn't crash if it isn't a callback. 52 | //I am not sure why I need this here, but without it, nothing works. 53 | authContext.handleWindowCallback(); 54 | 55 | 56 | if(!authContext.isCallback(window.location.hash)) { 57 | //Having both of these checks is to prevent having a token in localstorage, but no user. Relates to issue #471 58 | if(!authContext.getCachedToken(adalConfig.clientId) || !authContext.getCachedUser()) { 59 | authContext.login(); 60 | } else { 61 | ReactDOM.render( 62 | 63 | 64 | 65 | 66 | , 67 | document.getElementById('react-app')); 68 | 69 | 70 | 71 | } 72 | } -------------------------------------------------------------------------------- /ClientApp/boot-server.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { renderToString } from 'react-dom/server'; 4 | import { match, RouterContext } from 'react-router'; 5 | import createMemoryHistory from 'history/lib/createMemoryHistory'; 6 | import { createServerRenderer, RenderResult } from 'aspnet-prerendering'; 7 | import routes from './routes'; 8 | import configureStore from './configureStore'; 9 | 10 | export default createServerRenderer(params => { 11 | return new Promise((resolve, reject) => { 12 | // Match the incoming request against the list of client-side routes 13 | const store = configureStore(); 14 | match({ routes, location: params.location }, (error, redirectLocation, renderProps: any) => { 15 | if (error) { 16 | throw error; 17 | } 18 | 19 | // If there's a redirection, just send this information back to the host application 20 | if (redirectLocation) { 21 | resolve({ redirectUrl: redirectLocation.pathname }); 22 | return; 23 | } 24 | 25 | // If it didn't match any route, renderProps will be undefined 26 | if (!renderProps) { 27 | throw new Error(`The location '${ params.url }' doesn't match any route configured in react-router.`); 28 | } 29 | 30 | // Build an instance of the application 31 | const app = ( 32 | 33 | 34 | 35 | ); 36 | 37 | // Perform an initial render that will cause any async tasks (e.g., data access) to begin 38 | renderToString(app); 39 | 40 | // Once the tasks are done, we can perform the final render 41 | // We also send the redux store state, so the client can continue execution where the server left off 42 | params.domainTasks.then(() => { 43 | resolve({ 44 | html: renderToString(app), 45 | globals: { initialReduxState: store.getState() } 46 | }); 47 | }, reject); // Also propagate any errors back into the host application 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /ClientApp/components/Auth.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import AuthContext from 'adal-angular'; 3 | 4 | export default class Auth extends React.Component { 5 | componentDidMount() { 6 | 7 | (window as any).authContext.handleWindowCallback(); 8 | } 9 | render() { 10 | return (
); 11 | } 12 | } -------------------------------------------------------------------------------- /ClientApp/components/Counter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Link } from 'react-router'; 3 | import { connect } from 'react-redux'; 4 | import { ApplicationState } from '../store'; 5 | import * as CounterStore from '../store/Counter'; 6 | import * as WeatherForecasts from '../store/WeatherForecasts'; 7 | 8 | type CounterProps = CounterStore.CounterState & typeof CounterStore.actionCreators; 9 | 10 | class Counter extends React.Component { 11 | public render() { 12 | return
13 |

Counter

14 | 15 |

This is a simple example of a React component.

16 | 17 |

Current count: { this.props.count }

18 | 19 | 20 |
; 21 | } 22 | } 23 | 24 | // Wire up the React component to the Redux store 25 | export default connect( 26 | (state: ApplicationState) => state.counter, // Selects which state properties are merged into the component's props 27 | CounterStore.actionCreators // Selects which action creators are merged into the component's props 28 | )(Counter); 29 | -------------------------------------------------------------------------------- /ClientApp/components/FetchData.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Link } from 'react-router'; 3 | import { connect } from 'react-redux'; 4 | import { ApplicationState } from '../store'; 5 | import * as WeatherForecastsState from '../store/WeatherForecasts'; 6 | 7 | // At runtime, Redux will merge together... 8 | type WeatherForecastProps = 9 | WeatherForecastsState.WeatherForecastsState // ... state we've requested from the Redux store 10 | & typeof WeatherForecastsState.actionCreators // ... plus action creators we've requested 11 | & { params: { startDateIndex: string } }; // ... plus incoming routing parameters 12 | 13 | class FetchData extends React.Component { 14 | componentWillMount() { 15 | // This method runs when the component is first added to the page 16 | let startDateIndex = parseInt(this.props.params.startDateIndex) || 0; 17 | this.props.requestWeatherForecasts(startDateIndex); 18 | } 19 | 20 | componentWillReceiveProps(nextProps: WeatherForecastProps) { 21 | // This method runs when incoming props (e.g., route params) change 22 | let startDateIndex = parseInt(nextProps.params.startDateIndex) || 0; 23 | this.props.requestWeatherForecasts(startDateIndex); 24 | } 25 | 26 | public render() { 27 | return
28 |

Weather forecast

29 |

This component demonstrates fetching data from the server and working with URL parameters.

30 | { this.renderForecastsTable() } 31 | { this.renderPagination() } 32 |
; 33 | } 34 | 35 | private renderForecastsTable() { 36 | return 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {this.props.forecasts.map(forecast => 47 | 48 | 49 | 50 | 51 | 52 | 53 | )} 54 | 55 |
DateTemp. (C)Temp. (F)Summary
{ forecast.dateFormatted }{ forecast.temperatureC }{ forecast.temperatureF }{ forecast.summary }
; 56 | } 57 | 58 | private renderPagination() { 59 | let prevStartDateIndex = this.props.startDateIndex - 5; 60 | let nextStartDateIndex = this.props.startDateIndex + 5; 61 | 62 | return

63 | Previous 64 | Next 65 | { this.props.isLoading ? Loading... : [] } 66 |

; 67 | } 68 | } 69 | 70 | export default connect( 71 | (state: ApplicationState) => state.weatherForecasts, // Selects which state properties are merged into the component's props 72 | WeatherForecastsState.actionCreators // Selects which action creators are merged into the component's props 73 | )(FetchData); 74 | -------------------------------------------------------------------------------- /ClientApp/components/Home.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default class Home extends React.Component { 4 | public render() { 5 | return
6 |

Hello, world!

7 |

Welcome to your new single-page application, built with:

8 | 14 |

To help you get started, we've also set up:

15 |
    16 |
  • Client-side navigation. For example, click Counter then Back to return here.
  • 17 |
  • Webpack dev middleware. In development mode, there's no need to run the webpack build tool. Your client-side resources are dynamically built on demand. Updates are available as soon as you modify any file.
  • 18 |
  • Hot module replacement. In development mode, you don't even need to reload the page after making most changes. Within seconds of saving changes to files, rebuilt React components will be injected directly into your running application, preserving its live state.
  • 19 |
  • Efficient production builds. In production mode, development-time features are disabled, and the webpack build tool produces minified static CSS and JavaScript files.
  • 20 |
  • Server-side prerendering. To optimize startup time, your React application is first rendered on the server. The initial HTML and state is then transferred to the browser, where client-side code picks up where the server left off.
  • 21 |
22 |
; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ClientApp/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { NavMenu } from './NavMenu'; 3 | 4 | export interface LayoutProps { 5 | body: React.ReactElement; 6 | } 7 | 8 | export class Layout extends React.Component { 9 | public render() { 10 | return
11 |
12 |
13 | 14 |
15 |
16 | { this.props.body } 17 |
18 |
19 |
; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ClientApp/components/NavMenu.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Link } from 'react-router'; 3 | 4 | export class NavMenu extends React.Component { 5 | public render() { 6 | return
7 |
8 |
9 | 15 | WebApplicationBasic 16 |
17 |
18 |
19 |
    20 |
  • 21 | 22 | Home 23 | 24 |
  • 25 |
  • 26 | 27 | Counter 28 | 29 |
  • 30 |
  • 31 | 32 | Fetch data 33 | 34 |
  • 35 |
36 |
37 |
38 |
; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ClientApp/configureStore.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose, combineReducers, GenericStoreEnhancer } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import { routerReducer } from 'react-router-redux'; 4 | import * as Store from './store'; 5 | 6 | export default function configureStore(initialState?: Store.ApplicationState) { 7 | // Build middleware. These are functions that can process the actions before they reach the store. 8 | const windowIfDefined = typeof window === 'undefined' ? null : window as any; 9 | // If devTools is installed, connect to it 10 | const devToolsExtension = windowIfDefined && windowIfDefined.devToolsExtension as () => GenericStoreEnhancer; 11 | const createStoreWithMiddleware = compose( 12 | applyMiddleware(thunk), 13 | devToolsExtension ? devToolsExtension() : f => f 14 | )(createStore); 15 | 16 | // Combine all reducers and instantiate the app-wide store instance 17 | const allReducers = buildRootReducer(Store.reducers); 18 | const store = createStoreWithMiddleware(allReducers, initialState) as Redux.Store; 19 | 20 | // Enable Webpack hot module replacement for reducers 21 | if (module.hot) { 22 | module.hot.accept('./store', () => { 23 | const nextRootReducer = require('./store'); 24 | store.replaceReducer(buildRootReducer(nextRootReducer.reducers)); 25 | }); 26 | } 27 | 28 | return store; 29 | } 30 | 31 | function buildRootReducer(allReducers) { 32 | return combineReducers(Object.assign({}, allReducers, { routing: routerReducer })); 33 | } 34 | -------------------------------------------------------------------------------- /ClientApp/css/site.css: -------------------------------------------------------------------------------- 1 | .main-nav li .glyphicon { 2 | margin-right: 10px; 3 | } 4 | 5 | /* Highlighting rules for nav menu items */ 6 | .main-nav li a.active, 7 | .main-nav li a.active:hover, 8 | .main-nav li a.active:focus { 9 | background-color: #4189C7; 10 | color: white; 11 | } 12 | 13 | /* Keep the nav menu independent of scrolling and on top of other items */ 14 | .main-nav { 15 | position: fixed; 16 | top: 0; 17 | left: 0; 18 | right: 0; 19 | z-index: 1; 20 | } 21 | 22 | @media (max-width: 767px) { 23 | /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */ 24 | body { 25 | padding-top: 50px; 26 | } 27 | } 28 | 29 | @media (min-width: 768px) { 30 | /* On small screens, convert the nav menu to a vertical sidebar */ 31 | .main-nav { 32 | height: 100%; 33 | width: calc(25% - 20px); 34 | } 35 | .main-nav .navbar { 36 | border-radius: 0px; 37 | border-width: 0px; 38 | height: 100%; 39 | } 40 | .main-nav .navbar-header { 41 | float: none; 42 | } 43 | .main-nav .navbar-collapse { 44 | border-top: 1px solid #444; 45 | padding: 0px; 46 | } 47 | .main-nav .navbar ul { 48 | float: none; 49 | } 50 | .main-nav .navbar li { 51 | float: none; 52 | font-size: 15px; 53 | margin: 6px; 54 | } 55 | .main-nav .navbar li a { 56 | padding: 10px 16px; 57 | border-radius: 4px; 58 | } 59 | .main-nav .navbar a { 60 | /* If a menu item's text is too long, truncate it */ 61 | width: 100%; 62 | white-space: nowrap; 63 | overflow: hidden; 64 | text-overflow: ellipsis; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ClientApp/routes.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router, Route, HistoryBase } from 'react-router'; 3 | import { Layout } from './components/Layout'; 4 | import Home from './components/Home'; 5 | import FetchData from './components/FetchData'; 6 | import Counter from './components/Counter'; 7 | import Auth from './components/Auth'; 8 | 9 | export default 10 | 11 | 12 | 13 | 14 | { /* Optional route segment that does not affect NavMenu highlighting */ } 15 | 16 | ; 17 | 18 | // Enable Hot Module Replacement (HMR) 19 | if (module.hot) { 20 | module.hot.accept(); 21 | } 22 | -------------------------------------------------------------------------------- /ClientApp/store/Counter.ts: -------------------------------------------------------------------------------- 1 | import { Action, Reducer } from 'redux'; 2 | 3 | // ----------------- 4 | // STATE - This defines the type of data maintained in the Redux store. 5 | 6 | export interface CounterState { 7 | count: number; 8 | } 9 | 10 | // ----------------- 11 | // ACTIONS - These are serializable (hence replayable) descriptions of state transitions. 12 | // They do not themselves have any side-effects; they just describe something that is going to happen. 13 | // Use @typeName and isActionType for type detection that works even after serialization/deserialization. 14 | 15 | interface IncrementCountAction { type: 'INCREMENT_COUNT' } 16 | interface DecrementCountAction { type: 'DECREMENT_COUNT' } 17 | 18 | // Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the 19 | // declared type strings (and not any other arbitrary string). 20 | type KnownAction = IncrementCountAction | DecrementCountAction; 21 | 22 | // ---------------- 23 | // ACTION CREATORS - These are functions exposed to UI components that will trigger a state transition. 24 | // They don't directly mutate state, but they can have external side-effects (such as loading data). 25 | 26 | export const actionCreators = { 27 | increment: () => { type: 'INCREMENT_COUNT' }, 28 | decrement: () => { type: 'DECREMENT_COUNT' } 29 | }; 30 | 31 | // ---------------- 32 | // REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state. 33 | 34 | export const reducer: Reducer = (state: CounterState, action: KnownAction) => { 35 | switch (action.type) { 36 | case 'INCREMENT_COUNT': 37 | return { count: state.count + 1 }; 38 | case 'DECREMENT_COUNT': 39 | return { count: state.count - 1 }; 40 | default: 41 | // The following line guarantees that every action in the KnownAction union has been covered by a case above 42 | const exhaustiveCheck: never = action; 43 | } 44 | 45 | // For unrecognized actions (or in cases where actions have no effect), must return the existing state 46 | // (or default initial state if none was supplied) 47 | return state || { count: 0 }; 48 | }; 49 | -------------------------------------------------------------------------------- /ClientApp/store/WeatherForecasts.ts: -------------------------------------------------------------------------------- 1 | import { fetch, addTask } from 'domain-task'; 2 | import { Action, Reducer, ActionCreator } from 'redux'; 3 | import { AppThunkAction } from './'; 4 | import {adalRequest} from '../adal/AuthenticationContext'; 5 | import axios from 'axios'; 6 | // ----------------- 7 | // STATE - This defines the type of data maintained in the Redux store. 8 | 9 | export interface WeatherForecastsState { 10 | isLoading: boolean; 11 | startDateIndex: number; 12 | forecasts: WeatherForecast[]; 13 | } 14 | 15 | export interface WeatherForecast { 16 | dateFormatted: string; 17 | temperatureC: number; 18 | temperatureF: number; 19 | summary: string; 20 | } 21 | 22 | // ----------------- 23 | // ACTIONS - These are serializable (hence replayable) descriptions of state transitions. 24 | // They do not themselves have any side-effects; they just describe something that is going to happen. 25 | 26 | interface RequestWeatherForecastsAction { 27 | type: 'REQUEST_WEATHER_FORECASTS', 28 | startDateIndex: number; 29 | } 30 | 31 | interface ReceiveWeatherForecastsAction { 32 | type: 'RECEIVE_WEATHER_FORECASTS', 33 | startDateIndex: number; 34 | forecasts: WeatherForecast[] 35 | } 36 | 37 | // Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the 38 | // declared type strings (and not any other arbitrary string). 39 | type KnownAction = RequestWeatherForecastsAction | ReceiveWeatherForecastsAction; 40 | 41 | // ---------------- 42 | // ACTION CREATORS - These are functions exposed to UI components that will trigger a state transition. 43 | // They don't directly mutate state, but they can have external side-effects (such as loading data). 44 | 45 | export const actionCreators = { 46 | requestWeatherForecasts: (startDateIndex: number): AppThunkAction => (dispatch, getState) => { 47 | // Only load data if it's something we don't already have (and are not already loading) 48 | if (startDateIndex !== getState().weatherForecasts.startDateIndex) { 49 | // fetch(`/api/SampleData/WeatherForecasts?startDateIndex=${ startDateIndex }`) 50 | // adalRequest({url:`/api/SampleData/WeatherForecasts?startDateIndex=${ startDateIndex }`}) 51 | 52 | let fetchTask = axios.get(`/api/SampleData/WeatherForecasts?startDateIndex=${ startDateIndex }`) 53 | .then(response => response.data as Promise) 54 | .then(data => { 55 | dispatch({ type: 'RECEIVE_WEATHER_FORECASTS', startDateIndex: startDateIndex, forecasts: data }); 56 | }); 57 | 58 | //addTask(fetchTask); // Ensure server-side prerendering waits for this to complete 59 | dispatch({ type: 'REQUEST_WEATHER_FORECASTS', startDateIndex: startDateIndex }); 60 | } 61 | } 62 | }; 63 | 64 | // ---------------- 65 | // REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state. 66 | 67 | const unloadedState: WeatherForecastsState = { startDateIndex: null, forecasts: [], isLoading: false }; 68 | 69 | export const reducer: Reducer = (state: WeatherForecastsState, action: KnownAction) => { 70 | switch (action.type) { 71 | case 'REQUEST_WEATHER_FORECASTS': 72 | return { 73 | startDateIndex: action.startDateIndex, 74 | forecasts: state.forecasts, 75 | isLoading: true 76 | }; 77 | case 'RECEIVE_WEATHER_FORECASTS': 78 | // Only accept the incoming data if it matches the most recent request. This ensures we correctly 79 | // handle out-of-order responses. 80 | if (action.startDateIndex === state.startDateIndex) { 81 | return { 82 | startDateIndex: action.startDateIndex, 83 | forecasts: action.forecasts, 84 | isLoading: false 85 | }; 86 | } 87 | break; 88 | default: 89 | // The following line guarantees that every action in the KnownAction union has been covered by a case above 90 | const exhaustiveCheck: never = action; 91 | } 92 | 93 | return state || unloadedState; 94 | }; 95 | -------------------------------------------------------------------------------- /ClientApp/store/index.ts: -------------------------------------------------------------------------------- 1 | import * as WeatherForecasts from './WeatherForecasts'; 2 | import * as Counter from './Counter'; 3 | 4 | // The top-level state object 5 | export interface ApplicationState { 6 | counter: Counter.CounterState, 7 | weatherForecasts: WeatherForecasts.WeatherForecastsState 8 | } 9 | 10 | // Whenever an action is dispatched, Redux will update each top-level application state property using 11 | // the reducer with the matching name. It's important that the names match exactly, and that the reducer 12 | // acts on the corresponding ApplicationState property type. 13 | export const reducers = { 14 | counter: Counter.reducer, 15 | weatherForecasts: WeatherForecasts.reducer 16 | }; 17 | 18 | // This type can be used as a hint on action creators so that its 'dispatch' and 'getState' params are 19 | // correctly typed to match your store. 20 | export interface AppThunkAction { 21 | (dispatch: (action: TAction) => void, getState: () => ApplicationState): void; 22 | } 23 | -------------------------------------------------------------------------------- /Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace WebApplicationBasic.Controllers 8 | { 9 | public class HomeController : Controller 10 | { 11 | public IActionResult Index() 12 | { 13 | return View(); 14 | } 15 | 16 | public IActionResult Error() 17 | { 18 | return View(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Controllers/SampleDataController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Authorization; 7 | 8 | namespace WebApplicationBasic.Controllers 9 | { 10 | [Authorize] 11 | [Route("api/[controller]")] 12 | public class SampleDataController : Controller 13 | { 14 | private static string[] Summaries = new[] 15 | { 16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 17 | }; 18 | 19 | [HttpGet("[action]")] 20 | public IEnumerable WeatherForecasts(int startDateIndex) 21 | { 22 | var rng = new Random(); 23 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast 24 | { 25 | DateFormatted = DateTime.Now.AddDays(index + startDateIndex).ToString("d"), 26 | TemperatureC = rng.Next(-20, 55), 27 | Summary = Summaries[rng.Next(Summaries.Length)] 28 | }); 29 | } 30 | 31 | public class WeatherForecast 32 | { 33 | public string DateFormatted { get; set; } 34 | public int TemperatureC { get; set; } 35 | public string Summary { get; set; } 36 | 37 | public int TemperatureF 38 | { 39 | get 40 | { 41 | return 32 + (int)(TemperatureC / 0.5556); 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /NetCoreReactAzureAD.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netcoreapp1.1 4 | true 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | %(DistFiles.Identity) 33 | PreserveNewest 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Hosting; 7 | 8 | namespace WebApplicationBasic 9 | { 10 | public class Program 11 | { 12 | public static void Main(string[] args) 13 | { 14 | var host = new WebHostBuilder() 15 | .UseKestrel() 16 | .UseContentRoot(Directory.GetCurrentDirectory()) 17 | .UseIISIntegration() 18 | .UseStartup() 19 | .Build(); 20 | 21 | host.Run(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React (with Redux) App with Azure AD Auth and .Net Core API 2 | If you are developing Single Page App (SPA) in React and the backend leverages .NET Core APIs and you want to secure the backend APIs using Azure AD, this repo can be used as your starter project. Since client in this case is a React based SPA, I am using [OAuth2 implicit grant flow in Azure Active Directory (AD)](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-dev-understanding-oauth2-implicit-grant) 3 | 4 | I used [Microsoft ASP.NET Core JavaScript Services](https://github.com/aspnet/JavaScriptServices) as my base project and did two things: 5 | 6 | 1. Secured the backend API Controller with Azure AD Authentication. The client needs to present valid OAuth2 bearer JWT tokens while making a request 7 | 2. Integrated [ADAL JS](https://github.com/AzureAD/azure-activedirectory-library-for-js) with React such that, if user has NOT logged in with Azure AD, (s)he would be redirected to Azure AD Login page, before user can see index.html page of the SPA 8 | 9 | ## Register Apps with Azure AD 10 | First, you need to register your backend APIs and Client Application with Azure AD. If you have not done it before, I recommend reading [Authentication Scenarios for Azure AD](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-scenarios), expecially the "Single Page Application (SPA)" section. As described in Azure AD documentation, when you register the client app, keep following in mind: 11 | 1. Register it as **Native** (Application Type) App 12 | 2. After registration is completed, open Manifest file in Azure AD Portal, and set "oauth2AllowImplicitFlow:true" 13 | 3. The Reply URL will be https://localhost:xxxxx/auth, where xxxx represents your local port where the application runs 14 | 4. Make a note of "App ID URI", you will need while setting up config files for the application 15 | 16 | 17 | Similarly, while registering the app for Backend APIs, keep following in mind: 18 | 1. Register it as **Web app / API** (Application Type) 19 | 2. Make a note of clientID and Redirect URL, you will need while setting up config files for the application 20 | 21 | ## Add Config Files 22 | 23 | Next, you need to create **adalConfig.ts** file under /ClientApp/adal folder in the folder structure and put following code in it: 24 | 25 | ```javascript 26 | const adalConfig = { 27 | tenant: '[YOUR TENANT NAME].onmicrosoft.com', 28 | clientId: '[CLIENT ID OF AZURE AD REGISTERED CLIENT APP]', 29 | extraQueryParameter: 'nux=1', 30 | endpoints: { 31 | '/api/': '["APP ID URI" OF REGISTERED BACKEND API]' 32 | }, 33 | postLogoutRedirectUri: window.location.origin, 34 | redirectUri: '[REDIRECT URI OF AZURE AD REGISTERED CLIENT APP]', 35 | cacheLocation: 'sessionStorage' 36 | }; 37 | 38 | export default adalConfig; 39 | ``` 40 | Of course, you need to replace values in square brackets with the values obtained from your Azure AD App registrations. 41 | 42 | Also, add **azureAd.authConfig.json** file at the root of the project (along with appsettings.json file) and put following in it: 43 | 44 | ```json 45 | { 46 | "Authentication": { 47 | "AzureAd": { 48 | "AADInstance": "https://login.microsoftonline.com/", 49 | "Audience": "['APP ID URI' OF REGISTERED BACKEND API]", 50 | "TenantId": "[Your tenant ID in the form of GUID]" 51 | } 52 | } 53 | } 54 | ``` 55 | ## Ready to Go! 56 | Now, open the **.csproj** from in VS.NET (2015/2017) and run the starter project. If everything is configured properly, you should be redirected to Azure AD login page. After successful login, you will be taken to index.html of the SPA. 57 | 58 | Also note that ADAL JS should auto-renew the tokens behind the scene so you should not worry about logging in again after few minutes. 59 | 60 | ## Contact 61 | If starter project is not working as described here, or you have additional questions or need a hand to set it up, do not hesitate to contact me @ [ashishqs@gmail.com](mailto:ashishqs@gmail.com) -------------------------------------------------------------------------------- /Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.SpaServices.Webpack; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Logging; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Authentication.JwtBearer; 10 | using System.Threading.Tasks; 11 | 12 | namespace WebApplicationBasic 13 | { 14 | public class Startup 15 | { 16 | public Startup(IHostingEnvironment env) 17 | { 18 | var builder = new ConfigurationBuilder() 19 | .SetBasePath(env.ContentRootPath) 20 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 21 | .AddJsonFile("azureAd.authConfig.json", optional: true, reloadOnChange: true) 22 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 23 | .AddEnvironmentVariables(); 24 | Configuration = builder.Build(); 25 | } 26 | 27 | public IConfigurationRoot Configuration { get; } 28 | 29 | // This method gets called by the runtime. Use this method to add services to the container. 30 | public void ConfigureServices(IServiceCollection services) 31 | { 32 | // Add framework services. 33 | services.AddMvc(); 34 | } 35 | 36 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 37 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 38 | { 39 | loggerFactory.AddConsole(Configuration.GetSection("Logging")); 40 | loggerFactory.AddDebug(); 41 | 42 | 43 | if (env.IsDevelopment()) 44 | { 45 | app.UseDeveloperExceptionPage(); 46 | app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { 47 | HotModuleReplacement = true, 48 | ReactHotModuleReplacement = true 49 | }); 50 | } 51 | else 52 | { 53 | app.UseExceptionHandler("/Home/Error"); 54 | } 55 | app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")), ApiAuthentication(Configuration)); 56 | 57 | app.UseStaticFiles(); 58 | 59 | app.UseMvc(routes => 60 | { 61 | routes.MapRoute( 62 | name: "default", 63 | template: "{controller=Home}/{action=Index}/{id?}"); 64 | 65 | routes.MapSpaFallbackRoute( 66 | name: "spa-fallback", 67 | defaults: new { controller = "Home", action = "Index" }); 68 | }); 69 | 70 | 71 | 72 | 73 | } 74 | 75 | 76 | private Action ApiAuthentication(IConfiguration configuration) 77 | { 78 | return branch => branch.UseJwtBearerAuthentication(new JwtBearerOptions() 79 | { 80 | Authority = configuration["Authentication:AzureAd:AADInstance"] + configuration["Authentication:AzureAd:TenantId"], 81 | Audience = configuration["Authentication:AzureAd:Audience"], 82 | AutomaticAuthenticate = true, 83 | AutomaticChallenge = true, 84 | RequireHttpsMetadata = false 85 | }); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 | 5 |
Loading...
6 | 7 | @section scripts { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |

Error.

6 |

An error occurred while processing your request.

7 | -------------------------------------------------------------------------------- /Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - WebApplicationBasic 7 | 8 | 9 | 10 | 11 | 12 | @RenderBody() 13 | 14 | 15 | @RenderSection("scripts", required: false) 16 | 17 | 18 | -------------------------------------------------------------------------------- /Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using WebApplicationBasic 2 | @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" 3 | @addTagHelper "*, Microsoft.AspNetCore.SpaServices" 4 | -------------------------------------------------------------------------------- /Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ 3 | "." 4 | ] 5 | 6 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebApplicationBasic", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@types/adal": "^1.0.25", 6 | "@types/history": "^2.0.0", 7 | "@types/react": "^0.14.29", 8 | "@types/react-dom": "^0.14.14", 9 | "@types/react-redux": "^4.4.29", 10 | "@types/react-router": "^2.0.30", 11 | "@types/react-router-redux": "4.0.30", 12 | "@types/redux": "3.5.27", 13 | "@types/webpack": "^2.2.0", 14 | "@types/webpack-env": "^1.13.0", 15 | "adal-angular": "^1.0.14", 16 | "aspnet-prerendering": "^2.0.0", 17 | "aspnet-webpack": "^1.0.27", 18 | "aspnet-webpack-react": "^1.0.4", 19 | "awesome-typescript-loader": "^3.0.0", 20 | "axios": "^0.15.3", 21 | "babel-core": "^6.5.2", 22 | "babel-loader": "^6.2.3", 23 | "babel-preset-es2015": "^6.5.0", 24 | "babel-preset-react": "^6.5.0", 25 | "bootstrap": "^3.3.6", 26 | "css-loader": "^0.23.1", 27 | "domain-task": "^3.0.0", 28 | "event-source-polyfill": "^0.0.7", 29 | "extract-text-webpack-plugin": "^2.0.0-rc", 30 | "file-loader": "^0.8.5", 31 | "jquery": "^2.2.1", 32 | "json-loader": "^0.5.4", 33 | "node-noop": "^1.0.0", 34 | "q": "^1.4.1", 35 | "react": "^15.3.2", 36 | "react-dom": "^15.3.2", 37 | "react-redux": "^4.4.5", 38 | "react-router": "^2.8.1", 39 | "react-router-redux": "^4.0.6", 40 | "redux": "^3.6.0", 41 | "redux-thunk": "^2.2.0", 42 | "style-loader": "^0.13.0", 43 | "typescript": "^2.2.1", 44 | "url-loader": "^0.5.7", 45 | "webpack": "^2.2.0", 46 | "webpack-hot-middleware": "^2.12.2", 47 | "webpack-merge": "^0.14.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "moduleResolution": "node", 5 | "target": "es6", 6 | "jsx": "preserve", 7 | "experimentalDecorators": true, 8 | "sourceMap": true, 9 | "skipDefaultLibCheck": true, 10 | "allowJs": false, 11 | "lib": ["es6", "dom"], 12 | "types": [ "webpack-env" ], 13 | "paths": { 14 | // Fix "Duplicate identifier" errors caused by multiple dependencies fetching their own copies of type definitions. 15 | // We tell TypeScript which type definitions module to treat as the canonical one (instead of combining all of them). 16 | "history": ["./node_modules/@types/history/index"], 17 | "redux": ["./node_modules/@types/redux/index"], 18 | "react": ["./node_modules/@types/react/index"] 19 | } 20 | }, 21 | "exclude": [ 22 | "bin", 23 | "node_modules" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 4 | const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin; 5 | const merge = require('webpack-merge'); 6 | 7 | module.exports = (env) => { 8 | const isDevBuild = !(env && env.prod); 9 | 10 | // Configuration in common to both client-side and server-side bundles 11 | const sharedConfig = () => ({ 12 | stats: { modules: false }, 13 | resolve: { extensions: [ '.js', '.jsx', '.ts', '.tsx' ] }, 14 | output: { 15 | filename: '[name].js', 16 | publicPath: '/dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix 17 | }, 18 | module: { 19 | rules: [ 20 | { test: /\.tsx?$/, include: /ClientApp/, use: 'babel-loader' }, 21 | { test: /\.tsx?$/, include: /ClientApp/, use: 'awesome-typescript-loader?silent=true' } 22 | ] 23 | }, 24 | plugins: [new CheckerPlugin()] 25 | }); 26 | 27 | // Configuration for client-side bundle suitable for running in browsers 28 | const clientBundleOutputDir = './wwwroot/dist'; 29 | const clientBundleConfig = merge(sharedConfig(), { 30 | entry: { 'main-client': './ClientApp/boot-client.tsx' }, 31 | module: { 32 | rules: [ 33 | { test: /\.css$/, use: ExtractTextPlugin.extract({ use: 'css-loader' }) }, 34 | { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' } 35 | ] 36 | }, 37 | output: { path: path.join(__dirname, clientBundleOutputDir) }, 38 | plugins: [ 39 | new ExtractTextPlugin('site.css'), 40 | new webpack.DllReferencePlugin({ 41 | context: __dirname, 42 | manifest: require('./wwwroot/dist/vendor-manifest.json') 43 | }) 44 | ].concat(isDevBuild ? [ 45 | // Plugins that apply in development builds only 46 | new webpack.SourceMapDevToolPlugin({ 47 | filename: '[file].map', // Remove this line if you prefer inline source maps 48 | moduleFilenameTemplate: path.relative(clientBundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk 49 | }) 50 | ] : [ 51 | // Plugins that apply in production builds only 52 | new webpack.optimize.UglifyJsPlugin() 53 | ]) 54 | }); 55 | 56 | // Configuration for server-side (prerendering) bundle suitable for running in Node 57 | const serverBundleConfig = merge(sharedConfig(), { 58 | resolve: { mainFields: ['main'] }, 59 | entry: { 'main-server': './ClientApp/boot-server.tsx' }, 60 | plugins: [ 61 | new webpack.DllReferencePlugin({ 62 | context: __dirname, 63 | manifest: require('./ClientApp/dist/vendor-manifest.json'), 64 | sourceType: 'commonjs2', 65 | name: './vendor' 66 | }) 67 | ], 68 | output: { 69 | libraryTarget: 'commonjs', 70 | path: path.join(__dirname, './ClientApp/dist') 71 | }, 72 | target: 'node', 73 | devtool: 'inline-source-map' 74 | }); 75 | 76 | return [clientBundleConfig, serverBundleConfig]; 77 | }; 78 | -------------------------------------------------------------------------------- /webpack.config.vendor.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 4 | const merge = require('webpack-merge'); 5 | 6 | module.exports = (env) => { 7 | const isDevBuild = !(env && env.prod); 8 | const extractCSS = new ExtractTextPlugin('vendor.css'); 9 | 10 | const sharedConfig = { 11 | stats: { modules: false }, 12 | resolve: { extensions: [ '.js' ] }, 13 | module: { 14 | rules: [ 15 | { test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' } 16 | ] 17 | }, 18 | entry: { 19 | vendor: [ 20 | 'bootstrap', 21 | 'bootstrap/dist/css/bootstrap.css', 22 | 'domain-task', 23 | 'event-source-polyfill', 24 | 'react', 25 | 'react-dom', 26 | 'react-router', 27 | 'react-redux', 28 | 'redux', 29 | 'redux-thunk', 30 | 'react-router-redux', 31 | 'jquery' 32 | ], 33 | }, 34 | output: { 35 | publicPath: '/dist/', 36 | filename: '[name].js', 37 | library: '[name]_[hash]', 38 | }, 39 | plugins: [ 40 | new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable) 41 | new webpack.NormalModuleReplacementPlugin(/\/iconv-loader$/, require.resolve('node-noop')), // Workaround for https://github.com/andris9/encoding/issues/16 42 | new webpack.DefinePlugin({ 43 | 'process.env.NODE_ENV': isDevBuild ? '"development"' : '"production"' 44 | }) 45 | ] 46 | }; 47 | 48 | const clientBundleConfig = merge(sharedConfig, { 49 | output: { path: path.join(__dirname, 'wwwroot', 'dist') }, 50 | module: { 51 | rules: [ 52 | { test: /\.css(\?|$)/, use: extractCSS.extract({ use: 'css-loader' }) } 53 | ] 54 | }, 55 | plugins: [ 56 | extractCSS, 57 | new webpack.DllPlugin({ 58 | path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'), 59 | name: '[name]_[hash]' 60 | }) 61 | ].concat(isDevBuild ? [] : [ 62 | new webpack.optimize.UglifyJsPlugin() 63 | ]) 64 | }); 65 | 66 | const serverBundleConfig = merge(sharedConfig, { 67 | target: 'node', 68 | resolve: { mainFields: ['main'] }, 69 | output: { 70 | path: path.join(__dirname, 'ClientApp', 'dist'), 71 | libraryTarget: 'commonjs2', 72 | }, 73 | module: { 74 | rules: [ { test: /\.css(\?|$)/, use: 'css-loader' } ] 75 | }, 76 | entry: { vendor: ['aspnet-prerendering', 'react-dom/server'] }, 77 | plugins: [ 78 | new webpack.DllPlugin({ 79 | path: path.join(__dirname, 'ClientApp', 'dist', '[name]-manifest.json'), 80 | name: '[name]_[hash]' 81 | }) 82 | ] 83 | }); 84 | 85 | return [clientBundleConfig, serverBundleConfig]; 86 | }; 87 | -------------------------------------------------------------------------------- /wwwroot/favicon.ico: -------------------------------------------------------------------------------- 1 |  hF ��00 �%V@@ (B�:(  @��у��̅��˅��˅��ʅ��ʅ��Ʌ�������������������������������۶�����������������������������������������������������������������������۳u�НL�Ыn�����������������������������������������������������ܵx�ҢV�Щm�����������������������������������������������������ܶ{�ӣX�Ъn�����������������������������������������������������޸�ե\�Ѭq�����������������������������������������������������຃�רa�Ӯu�����������������������������������������������������Ệ�ةd�ԯx������������������������������������������������S����⽉�ګi�հ{������������������������������������������������E����㾌�ܯq�ص�������������������������������������������������E����侎�ܭo�ײ�������������������������������������������������E����俏�ݮq�س�������������������������������������������������E����忑�ޯu�ܻ�������������������������������������������������C�����ϭ������ġ�������������������������������������������ҟ�����������������������������������������������������������������������������A����������������������������������ן�����������������������������������������( @ � ((()������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ʠ��ˡ��ʞ��ɞ��л����������������������������������������������������������������������������������������������������������ˢ�ΘC�ϛG�͙A�͙B�ϴ�����������������������������������������������������������������������������������������������������������̥�ЛJ�џP�ӤX�ӤX�Ѻ�����������������������������������������������������������������������������������������������������������ͥ�ϛI�Ԧ\�ўN�НK�ж�����������������������������������������������������������������������������������������������������������Χ�ѝL�է^�ўN�МL�ж�����������������������������������������������������������������������������������������������������������Ϩ�ўN�ը`�џP�НM�з�����������������������������������������������������������������������������������������������������������ϩ�ѝM�֨`�џP�НM�ж�����������������������������������������������������������������������������������������������������������ѫ�ӡU�׫f�ӢV�ҠS�Ѹ�����������������������������������������������������������������������������������������������������������Ѭ�ҟQ�שc�ӠS�ҟP�Ѹ�����������������������������������������������������������������������������������������������������������Ү�գY�٭j�եZ�ԣX�Һ�����������������������������������������������������������������������������������������������������������Ү�բW�٬i�դY�ԢW�ҹ�����������������������������������������������������������������������������������������������������������Ӱ�֤\�ڮm�צ^�դ[�һ����������������������������������������������������������������������������������������������...#���������ӱ�֣[�ڭk�֤\�գY�Ӻ�������������������������������������������������������������������������������������������������������ճ�٧b�ܰr�٧c�ئ`�Ի�������������������������������������������������������������������������������������������������������Բ�פ^�ۮo�إ`�פ]�Ӻ�������������������������������������������������������������������������������������������������������յ�ڨe�޲w�޴y�޲w��Ĩ������������������������������������������������������������������������������������������������������Դ�٦a�ݯr�٦a�إ_�Ժ�������������������������������������������������������������������������������������������������������ն�۩g�޲w�۪i�کg�ս�������������������������������������������������������������������������������������������������������յ�ڧd�ޱu�کf�ڧd�ռ�������������������������������������������������������������������������������������������������������շ�ۨg�޲w�۪j�۩g�ս�������������������������������������������������������������������������������������������������������ַ�۩i�߲y�ܫk�۪i�ս��������������������������������������������������������������������������������������������‡���������շ�ۨi�߲y�ݬn�ܫm��¥�������������������������������������������������������������������������������������������Ç���������ָ�ܪl�ޮr�߲y���~��˵��������������������������������������������������������������������������������������������������������ַ�ܨi�ݫl�ܩi�ܩj�ս����������������������������������������������������������������������������������������ۿ�������������������շ��ָ��ַ��ָ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ܿ���������������������(0` �%��� ihhi��������������������������������~��~��~��~}��~}���������������~��~��~��~��~��~��~�JIH_��ԁ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zwv����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������۵w�۳u�۴u�۴v�ڳs�ڲs�ڳu��̼�����������������������������������������������������������������������������������������������������������������������������������������������������������������ϛH�ΘC�ΙD�ϛF�͘@�͘@�ΚE��­�����������������������������������������������������������������������������������������������������������������������������������������������������������������ЛI�ΙD�ΚE�ΚF�͗@�͗@�ΙD��¬�����������������������������������������������������������������������������������������������������������������������������������������������������������������џP�МL�ўO�֩c�֪c�ժc�֫e��Ƶ�����������������������������������������������������������������������������������������������������������������������������������������������������������������ѝM�ϛI�џP�׫f�ўN�ОL�џP��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ѝL�ϛH�ўO�ըa�ϚG�ΙE�ϜJ��®�����������������������������������������������������������������������������������������������������������������������������������������������������������������ҠQ�ўN�ҢT�׫f�ўM�НL�ўO��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ҠS�ўO�ҢU�׬g�ўN�НL�џP��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ҟP�ѝL�ҡR�֪d�НK�ϛI�ѝN��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ҠR�ѝM�ӢU�׫f�ОM�НK�ўO��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ԤZ�ӢW�զ]�ٯm�ӡU�ҠS�ӢW��ı�����������������������������������������������������������������������������������������������������������������������������������������������������������������ӢV�ҟQ�ӣW�جh�ўP�ўN�ҟQ��ð���������������������������������������������������������������������������������������������������������������������������������������������{zz�����������������ԢV�ҟQ�ԢX�٬i�ҟP�ўO�ҠR��İ���������������������������������������������������������������������������������������������������������������������������������������������~}�����������������֥^�դZ�֨`�ڱq�դY�ԣX�ԤZ��Ų�����������������������������������������������������������������������������������������������������������������������������������������������������������������֣Z�աV�֥\�گm�ӢU�ӠT�ԣW��ı�����������������������������������������������������������������������������������������������������������������������������������������������������������������֤\�բX�צ^�ڰp�ԣX�ԢV�գZ��IJ�����������������������������������������������������������������������������������������������������������������������������������������������������������������اa�֥]�بd�ܲt�ץ]�դ[�է^��ų�����������������������������������������������������������������������������������������������������������������������������������������999K$$$)����������������ץ^�֢Y�צ_�ۯp�բX�ԡV�դZ��IJ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ץ_�֣[�ק`�ܰq�գY�բW�֤[��Ų����������������������������������������������������������������������������������������������������������������������������������������� ����������������کf�٨c�ګi�޳x�ئa�ئ`�بc��ƴ����������������������������������������������������������������������������������������������������������������������������������������� ����������������٧b�إ_�٩e�ݱu�פ^�פ\�צ`��ų����������������������������������������������������������������������������������������������������������������������������������������� ����������������٧b�פ^�بe�ݱu�ף]�֣\�פ^��IJ����������������������������������������������������������������������������������������������������������������������������������������� ����������������کg�٧c�۫i�ก�ܮp�ܭn�޳y��ʼ����������������������������������������������������������������������������������������������������������������������������������������� ����������������کh�ڨe�۫k�ข�ܯq�ۮp�ܯr��Ǹ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ڨe�٦b�کh�޲w�٥`�ؤ_�٦b��Ŵ����������������������������������������������������������������������������������������������������������������������������������������� ����������������۩h�ڨe�۫k�ߴz�ڧd�٧c�کf��Ƶ����������������������������������������������������������������������������������������������������������������������������������������� ����������������۪k�۩h�ܭn���~�کh�کf�۫i��ƶ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ڨh�ڧd�۫k�ߴz�ڧd�٦c�کf��Ƶ����������������������������������������������������������������������������������������������������������������������������������������� ����������������۩j�ۨf�۫l�ߵ|�ڨf�ڧd�۪h��Ƶ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ܬn�ܪk�ݮq�ᶀ�۪k�۪j�ܬl��Ƕ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ܪk�ۨh�ܬn���}�۩g�ڨf�۪j��ƶ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ܪl�ۨh�ܬn���~�ܩi�ۨh�ܬn��Ƿ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ݬp�ܫm�ޮr�⺇��������ƛ����������������������������������������������������������������������������������������������������������������������������������������������������������������ݫm�ܩj�ܪl�ݮr�ݬn�ܭo�����ʽ���������������������������������������������������������������������������������������������������������������������������������������������������������������ܪl�ܨi�ܪj�ݫm�ܨi�ۨi�ܪk��ƶ��������������������������������������������������������������������������������������������������������������������������������������������������������������ݫo�ܪl�ݫm�ݬp�ܪl�ܪl�ݬo��Ƿ������������������������������������������������������������������������������������������������������������������������������������!����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!��������������������������������������������������������Ͽ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������п������������������������������������������������������������������������������������������������������߿���!��������������������������������������������������������������ֿ�������������������������������������������������������������������������������������������������������!������������������������������������������������������������������������������������������������������������������������������(@� B������ +>>>u===w===u===u===u===u===u===u===u<<>>q>>>q>>>q>>>q>>>q>>>q===q===q===q===q===q===q===q===q???s9���<<>>��������������������ОM�ΘB�ΘC�ΘA�НK�ΙB�͘?�͗>�ΛF�ΙD�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ўN�ϙE�ϚE�ΙD�ОL�ΚD�ΙB�͘@�ϛG�ΚE�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������џN�ϙD�ϙE�ΙD�ϜJ�͗A�͖>�̕=�ΙD�͘A�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ҡV�џP�ўO�МK�حj�ڲr�ٱo�ٰn�ٳs�ڲp�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ҠR�МJ�ЛJ�ΙD�۵x�ԤY�џN�џM�ҢT�ҠR�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ҠQ�ЛH�ϛH�ΘC�ڲs�џO�ΘD�ΘC�МJ�ϛH�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ҠR�ЛH�МI�ΘD�ڲs�ѠP�ϙE�ΘE�НL�ϜJ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ԣW�ҟP�ҟP�НK�ܶy�ӣW�ўM�ўM�ҠR�џP�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ԣX�ҟQ�ҟQ�НL�ܶy�ӤX�ўM�ОM�ҠR�џQ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������ӡU�ѝK�ѝL�ЛF�۴u�ҡS�ЛH�ϛG�ўN�ѝL�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������ԣW�ўM�ѝM�МH�۴v�ӢU�НJ�ЛI�џP�ѝN�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������ԣW�ўN�ҞN�ќJ�ܴx�ӢV�НK�НJ�ҟQ�ўO�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������֧^�գY�ԤZ�ӡU�޹��֨_�ӡU�ӡT�ԤZ�ԣX�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������դZ�ҟQ�ҟQ�ѝK�ܶy�ԢW�ўM�ѝL�ҠR�ҟP�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������ե[�ӠR�ҠR�ўN�޶z�գY�ѝO�ўN�ӡS�ҠR�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������֥[�ԟQ�ҠR�ќM�޶z�դY�ҞN�ѝN�ӡS�ӡQ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@@@��������������������רb�֥\�֥\�ԣW�߻��שb�գX�ԣX�զ]�եZ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@@@��������������������צ_�բW�բW�ԠR�޹�ק^�ԡS�ԠS�ԣY�գW�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BBB��������������������צ^�աU�աU�ӟP�޸~�֦\�ҠR�ӠR�գX�ԣV�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BBB��������������������اa�֣Z�գZ�աU�߹��רa�բW�բV�֥\�֣Z�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BBB��������������������ڪf�ק`�צ`�֤\�༆�٫g�צ]�֥]�֨a�֨`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������999a>>>]'��������������������اa�֢X�עX�՟T�߹��ا`�ՠU�ԠT�֤[�դY����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������بd�֣[�֣Z�աV�แ�اa�բW�բV�֥\�֤[����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������٨d�ף[�֣\�աV�ṁ�קa�բW�բV�֥\�֤[����������������������������������������������������������������������������������������������������������������������������������������������Ŀ����������������������������������������� �����������������������۬j�ڨd�کe�ئ`�⽉�۬j�ئ`�ئ`�٩e�٩c����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۪g�٦`�٦`�ؤ\�ễ�کg�פ\�פ]�٧a�צ`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ڪf�إ^�פ^�֢Z�ễ�٨e�ף[�֣Z�ئa�ץ^����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۪g�إ_�ؤ_�֢[�⻅�٨e�ע[�֢[�٦a�֣Z����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۫i�٦b�٧b�ؤ]�㽉�ܯp�ڨf�کf�۫j�฀����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ܭn�۫i�۪i�ڧc��“�⾌�ễ�ẃ�⼉�ṃ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۪i�٥a�٦a�أ]�⺆�ڨe�עZ�֢Z�إ`�פ^����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۫j�ڦb�ڧc�ؤ^�⼈�۫i�٥_�٤_�کe�٦c����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۫k�ڦc�ڧc�٤_�㼈�۫i�٤`�ئ`�ڨf�٨d����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݮq�ܫl�ܬl�۪h�����ݰs�۫j�۫i�ܭn�ܭl����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ܫl�ڧd�ڧe�٥`�㽊�ܬk�٦b�٦b�۩g�کf����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ܫm�ڧe�ڧe�٥a�㽊�ܬl�ڦb�٦b�۩h�کf����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ܫm�ۧe�ۨe�٥a�㽊�ܬl�ڦc�ڦb�۪h�۩f����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ޭq�ܪj�ܪk�ۨf�俎�ݮq�۩h�۩h�ܬm�ܫk����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݮr�ܫl�ܫl�۩h�忐�ޯs�۪j�۪j�ܭn�ܭm����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݬn�ۧf�ۨg�ڦc�佋�ݬn�ڧd�ڧd�۪j�۪i����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݬo�ۨh�ۨh�ڦd�佌�ݬn�ۧe�ۧe�۩i�ڧd����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݭq�ܩj�ܪj�ۧf�����߲w�ݭo�ݫm���{�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������߯u�ެp�ݭp�ݫm�㻈�忐�位�㼉��ɡ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ݭp�ܨh�ܩh�ܨh�ܫn�ۧf�ۦd�ڦc�ܪk�ްt����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ݭp�ܨi�ܩj�ܩi�ޭq�ܪk�ܨi�ۨh�ݫm�۪j���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ݭp�ܧh�ܨi�ܩh�ޭq�ܪj�ܨh�ۨg�ݬm�ܫl��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ޯt�ݬp�ݭp�ݭp�߯u�ݭq�ޭp�ݬo�ޮs�ޮr�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� --------------------------------------------------------------------------------