[Templating]Api authorization spa integration (dotnet/aspnetcore#7721)
[Templating] Adds authentication support for the SPA templates * Adds a new -auth option to the Angular and React templates with None and Individual values. * When using authentication it configures the server to use the ApiAuthorization support built for 3.0 * For angular it adds an ApiAuthorization Angular module that handles the authentication flows through a Login and a Logout components and exposes an Http client interceptor, a routing guard and a Login menu component for the main Angular application to integrate authentication into the app. * For react it adds a Login and Logout components to handle the authentication flows, an AuthorizeRoute component to protect access to other routes and a Login menu component to integrate authentication into the application. Commit migrated from dotnet/aspnetcore@ea97934127
This commit is contained in:
Родитель
ef07ca3db8
Коммит
15aff40cc9
|
@ -17,6 +17,13 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="${MicrosoftAspNetCoreMvcNewtonsoftJsonPackageVersion}" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="${MicrosoftAspNetCoreSpaServicesExtensionsPackageVersion}" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="${MicrosoftAspNetCoreApiAuthorizationIdentityServerPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="${MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="${MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="${MicrosoftAspNetCoreIdentityUIPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="${MicrosoftEntityFrameworkCoreSqlServerPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' == 'True'" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="${MicrosoftEntityFrameworkCoreSqlitePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' != 'True'" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="${MicrosoftEntityFrameworkCoreToolsPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -9,12 +9,28 @@
|
|||
<IsShippingPackage>true</IsShippingPackage>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Lists the versions of dependencies not built in this repo. Packages produced from this repo should be listed as a PackageVersionVariableReference. -->
|
||||
<GeneratedContentProperties>
|
||||
MicrosoftEntityFrameworkCoreSqlitePackageVersion=$(MicrosoftEntityFrameworkCoreSqlitePackageVersion);
|
||||
MicrosoftEntityFrameworkCoreSqlServerPackageVersion=$(MicrosoftEntityFrameworkCoreSqlServerPackageVersion);
|
||||
MicrosoftEntityFrameworkCoreToolsPackageVersion=$(MicrosoftEntityFrameworkCoreToolsPackageVersion);
|
||||
MicrosoftExtensionsHostingPackageVersion=$(MicrosoftExtensionsHostingPackageVersion);
|
||||
MicrosoftNETCoreAppPackageVersion=$(MicrosoftNETCoreAppPackageVersion);
|
||||
</GeneratedContentProperties>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- These projects product packages that the templates depend on. See GenerateContent.targets -->
|
||||
<PackageVersionVariableReference Include="$(RepositoryRoot)src\Mvc\Mvc.NewtonsoftJson\src\Microsoft.AspNetCore.Mvc.NewtonsoftJson.csproj" />
|
||||
<PackageVersionVariableReference Include="$(RepositoryRoot)src\Middleware\SpaServices.Extensions\src\Microsoft.AspNetCore.SpaServices.Extensions.csproj" />
|
||||
<PackageVersionVariableReference Include="$(RepositoryRoot)src\Identity\ApiAuthorization.IdentityServer\src\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj" />
|
||||
<PackageVersionVariableReference Include="$(RepositoryRoot)src\Identity\EntityFrameworkCore\src\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj" />
|
||||
<PackageVersionVariableReference Include="$(RepositoryRoot)src\Identity\UI\src\Microsoft.AspNetCore.Identity.UI.csproj" />
|
||||
<PackageVersionVariableReference Include="$(RepositoryRoot)src\Middleware\Diagnostics.EntityFrameworkCore\src\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<GeneratedContent Include="Angular-CSharp.csproj.in" OutputPath="content/Angular-CSharp/Company.WebApplication1.csproj" />
|
||||
<GeneratedContent Include="React-CSharp.csproj.in" OutputPath="content/React-CSharp/Company.WebApplication1.csproj" />
|
||||
|
|
|
@ -14,6 +14,13 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="${MicrosoftAspNetCoreMvcNewtonsoftJsonPackageVersion}" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="${MicrosoftAspNetCoreSpaServicesExtensionsPackageVersion}" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="${MicrosoftAspNetCoreApiAuthorizationIdentityServerPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="${MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="${MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="${MicrosoftAspNetCoreIdentityUIPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="${MicrosoftEntityFrameworkCoreSqlServerPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' == 'True'" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="${MicrosoftEntityFrameworkCoreSqlitePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' != 'True'" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="${MicrosoftEntityFrameworkCoreToolsPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/dotnetcli.host",
|
||||
"symbolInfo": {
|
||||
"UseLocalDB": {
|
||||
"longName": "use-local-db"
|
||||
},
|
||||
"Framework": {
|
||||
"longName": "framework"
|
||||
},
|
||||
"skipRestore": {
|
||||
"longName": "no-restore",
|
||||
"shortName": ""
|
||||
},
|
||||
"HttpPort": {
|
||||
"isHidden": true
|
||||
},
|
||||
|
@ -18,9 +17,19 @@
|
|||
"longName": "exclude-launch-settings",
|
||||
"shortName": ""
|
||||
},
|
||||
"UserSecretsId": {
|
||||
"isHidden": true
|
||||
},
|
||||
"skipRestore": {
|
||||
"longName": "no-restore",
|
||||
"shortName": ""
|
||||
},
|
||||
"NoHttps": {
|
||||
"longName": "no-https",
|
||||
"shortName": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"usageExamples": [
|
||||
"--auth Individual"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -23,22 +23,88 @@
|
|||
"exclude": [
|
||||
".template.config/**"
|
||||
],
|
||||
"copyOnly": [
|
||||
"wwwroot/**"
|
||||
],
|
||||
"modifiers": [
|
||||
{
|
||||
"condition": "(!IndividualLocalAuth)",
|
||||
"exclude": [
|
||||
"Pages/Shared/_LoginPartial.cshtml",
|
||||
"Data/**",
|
||||
"Models/**",
|
||||
"ClientApp/src/api-authorization/**",
|
||||
"Controllers/OidcConfigurationController.cs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(!IndividualLocalAuth || UseLocalDB)",
|
||||
"exclude": [
|
||||
"app.db"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(ExcludeLaunchSettings)",
|
||||
"exclude": [
|
||||
"Properties/launchSettings.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(IndividualLocalAuth && UseLocalDB)",
|
||||
"rename": {
|
||||
"Data/SQLServer/": "Data/Migrations/"
|
||||
},
|
||||
"exclude": [
|
||||
"Data/SQLite/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(IndividualLocalAuth && !UseLocalDB)",
|
||||
"rename": {
|
||||
"Data/SQLite/": "Data/Migrations/"
|
||||
},
|
||||
"exclude": [
|
||||
"Data/SQLServer/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"symbols": {
|
||||
"auth": {
|
||||
"type": "parameter",
|
||||
"datatype": "choice",
|
||||
"choices": [
|
||||
{
|
||||
"choice": "None",
|
||||
"description": "No authentication"
|
||||
},
|
||||
{
|
||||
"choice": "Individual",
|
||||
"description": "Individual authentication"
|
||||
}
|
||||
],
|
||||
"defaultValue": "None",
|
||||
"description": "The type of authentication to use"
|
||||
},
|
||||
"UserSecretsId": {
|
||||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"replaces": "aspnet-Company.WebApplication1-53bc9b9d-9d6a-45d4-8429-2a2761773502",
|
||||
"defaultValue": "aspnet-Company.WebApplication1-53bc9b9d-9d6a-45d4-8429-2a2761773502",
|
||||
"description": "The ID to use for secrets (use with OrgReadAccess or Individual auth)."
|
||||
},
|
||||
"ExcludeLaunchSettings": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"defaultValue": "false",
|
||||
"description": "Whether to exclude launchSettings.json from the generated template."
|
||||
"description": "Whether to exclude launchSettings.json in the generated template."
|
||||
},
|
||||
"skipRestore": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"description": "If specified, skips the automatic restore of the project on create.",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
"HttpPort": {
|
||||
"type": "parameter",
|
||||
|
@ -61,7 +127,7 @@
|
|||
"HttpsPort": {
|
||||
"type": "parameter",
|
||||
"datatype": "integer",
|
||||
"description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used)."
|
||||
"description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if IndividualLocalAuth is used)."
|
||||
},
|
||||
"HttpsPortGenerated": {
|
||||
"type": "generated",
|
||||
|
@ -80,6 +146,30 @@
|
|||
},
|
||||
"replaces": "44300"
|
||||
},
|
||||
"IndividualLocalAuth": {
|
||||
"type": "computed",
|
||||
"value": "(auth == \"Individual\")"
|
||||
},
|
||||
"NoAuth": {
|
||||
"type": "computed",
|
||||
"value": "(!(IndividualLocalAuth))"
|
||||
},
|
||||
"RequiresHttps": {
|
||||
"type": "computed",
|
||||
"value": "(IndividualLocalAuth || !NoHttps)"
|
||||
},
|
||||
"NoHttps": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"defaultValue": "false",
|
||||
"description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth."
|
||||
},
|
||||
"UseLocalDB": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"defaultValue": "false",
|
||||
"description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual or --auth IndividualB2C is specified."
|
||||
},
|
||||
"Framework": {
|
||||
"type": "parameter",
|
||||
"description": "The target framework for the project.",
|
||||
|
@ -96,18 +186,6 @@
|
|||
"HostIdentifier": {
|
||||
"type": "bind",
|
||||
"binding": "HostIdentifier"
|
||||
},
|
||||
"skipRestore": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"description": "If specified, skips the automatic restore of the project on create.",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
"NoHttps": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"defaultValue": "false",
|
||||
"description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth."
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -12,42 +12,43 @@
|
|||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "6.1.10",
|
||||
"@angular/common": "6.1.10",
|
||||
"@angular/compiler": "6.1.10",
|
||||
"@angular/core": "6.1.10",
|
||||
"@angular/forms": "6.1.10",
|
||||
"@angular/http": "6.1.10",
|
||||
"@angular/platform-browser": "6.1.10",
|
||||
"@angular/platform-browser-dynamic": "6.1.10",
|
||||
"@angular/platform-server": "6.1.10",
|
||||
"@angular/router": "6.1.10",
|
||||
"@nguniversal/module-map-ngfactory-loader": "6.0.0",
|
||||
"core-js": "^2.5.4",
|
||||
"rxjs": "^6.0.0",
|
||||
"zone.js": "^0.8.26",
|
||||
"@angular/animations": "7.2.5",
|
||||
"@angular/common": "7.2.5",
|
||||
"@angular/compiler": "7.2.5",
|
||||
"@angular/core": "7.2.5",
|
||||
"@angular/forms": "7.2.5",
|
||||
"@angular/http": "7.2.5",
|
||||
"@angular/platform-browser": "7.2.5",
|
||||
"@angular/platform-browser-dynamic": "7.2.5",
|
||||
"@angular/platform-server": "7.2.5",
|
||||
"@angular/router": "7.2.5",
|
||||
"@nguniversal/module-map-ngfactory-loader": "7.1.0",
|
||||
"core-js": "^2.6.5",
|
||||
"rxjs": "^6.4.0",
|
||||
"zone.js": "^0.8.29",
|
||||
"aspnet-prerendering": "^3.0.1",
|
||||
"bootstrap": "^4.1.3",
|
||||
"bootstrap": "^4.3.1",
|
||||
"jquery": "3.3.1",
|
||||
"oidc-client": "^1.6.1",
|
||||
"popper.js": "^1.14.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.6.0",
|
||||
"@angular/cli": "~6.0.0",
|
||||
"@angular/compiler-cli": "6.1.10",
|
||||
"@angular/language-service": "^6.0.0",
|
||||
"@types/jasmine": "~2.8.6",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "~8.9.4",
|
||||
"codelyzer": "~4.2.1",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"@angular-devkit/build-angular": "~0.13.2",
|
||||
"@angular/cli": "~7.3.2",
|
||||
"@angular/compiler-cli": "7.2.5",
|
||||
"@angular/language-service": "^7.2.5",
|
||||
"@types/jasmine": "~3.3.9",
|
||||
"@types/jasminewd2": "~2.0.6",
|
||||
"@types/node": "~11.9.4",
|
||||
"codelyzer": "~4.5.0",
|
||||
"jasmine-core": "~3.3.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "^3.0.0",
|
||||
"karma": "^4.0.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~1.4.2",
|
||||
"karma-jasmine": "~1.1.1",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"typescript": "~2.7.2"
|
||||
"karma-coverage-istanbul-reporter": "~2.0.5",
|
||||
"karma-jasmine": "~2.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.0",
|
||||
"typescript": "~3.2.4"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"node-sass": "^4.9.3",
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
export const ApplicationName = 'Company.WebApplication1';
|
||||
|
||||
export const ReturnUrlType = 'returnUrl';
|
||||
|
||||
export const QueryParameterNames = {
|
||||
ReturnUrl: ReturnUrlType,
|
||||
Message: 'message'
|
||||
};
|
||||
|
||||
export const LogoutActions = {
|
||||
LogoutCallback: 'logout-callback',
|
||||
Logout: 'logout',
|
||||
LoggedOut: 'logged-out'
|
||||
};
|
||||
|
||||
export const LoginActions = {
|
||||
Login: 'login',
|
||||
LoginCallback: 'login-callback',
|
||||
LoginFailed: 'login-failed',
|
||||
Profile: 'profile',
|
||||
Register: 'register'
|
||||
};
|
||||
|
||||
let applicationPaths: ApplicationPathsType = {
|
||||
DefaultLoginRedirectPath: '/',
|
||||
ApiAuthorizationClientConfigurationUrl: `/_configuration/${ApplicationName}`,
|
||||
Login: `authentication/${LoginActions.Login}`,
|
||||
LoginFailed: `authentication/${LoginActions.LoginFailed}`,
|
||||
LoginCallback: `authentication/${LoginActions.LoginCallback}`,
|
||||
Register: `authentication/${LoginActions.Register}`,
|
||||
Profile: `authentication/${LoginActions.Profile}`,
|
||||
LogOut: `authentication/${LogoutActions.Logout}`,
|
||||
LoggedOut: `authentication/${LogoutActions.LoggedOut}`,
|
||||
LogOutCallback: `authentication/${LogoutActions.LogoutCallback}`,
|
||||
LoginPathComponents: [],
|
||||
LoginFailedPathComponents: [],
|
||||
LoginCallbackPathComponents: [],
|
||||
RegisterPathComponents: [],
|
||||
ProfilePathComponents: [],
|
||||
LogOutPathComponents: [],
|
||||
LoggedOutPathComponents: [],
|
||||
LogOutCallbackPathComponents: [],
|
||||
IdentityRegisterPath: '/Identity/Account/Register',
|
||||
IdentityManagePath: '/Identity/Account/Manage'
|
||||
};
|
||||
|
||||
applicationPaths = {
|
||||
...applicationPaths,
|
||||
LoginPathComponents: applicationPaths.Login.split('/'),
|
||||
LoginFailedPathComponents: applicationPaths.LoginFailed.split('/'),
|
||||
RegisterPathComponents: applicationPaths.Register.split('/'),
|
||||
ProfilePathComponents: applicationPaths.Profile.split('/'),
|
||||
LogOutPathComponents: applicationPaths.LogOut.split('/'),
|
||||
LoggedOutPathComponents: applicationPaths.LoggedOut.split('/'),
|
||||
LogOutCallbackPathComponents: applicationPaths.LogOutCallback.split('/')
|
||||
};
|
||||
|
||||
interface ApplicationPathsType {
|
||||
readonly DefaultLoginRedirectPath: string;
|
||||
readonly ApiAuthorizationClientConfigurationUrl: string;
|
||||
readonly Login: string;
|
||||
readonly LoginFailed: string;
|
||||
readonly LoginCallback: string;
|
||||
readonly Register: string;
|
||||
readonly Profile: string;
|
||||
readonly LogOut: string;
|
||||
readonly LoggedOut: string;
|
||||
readonly LogOutCallback: string;
|
||||
readonly LoginPathComponents: string [];
|
||||
readonly LoginFailedPathComponents: string [];
|
||||
readonly LoginCallbackPathComponents: string [];
|
||||
readonly RegisterPathComponents: string [];
|
||||
readonly ProfilePathComponents: string [];
|
||||
readonly LogOutPathComponents: string [];
|
||||
readonly LoggedOutPathComponents: string [];
|
||||
readonly LogOutCallbackPathComponents: string [];
|
||||
readonly IdentityRegisterPath: string;
|
||||
readonly IdentityManagePath: string;
|
||||
}
|
||||
|
||||
export const ApplicationPaths: ApplicationPathsType = applicationPaths;
|
|
@ -0,0 +1,13 @@
|
|||
import { ApiAuthorizationModule } from './api-authorization.module';
|
||||
|
||||
describe('ApiAuthorizationModule', () => {
|
||||
let apiAuthorizationModule: ApiAuthorizationModule;
|
||||
|
||||
beforeEach(() => {
|
||||
apiAuthorizationModule = new ApiAuthorizationModule();
|
||||
});
|
||||
|
||||
it('should create an instance', () => {
|
||||
expect(apiAuthorizationModule).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { LoginMenuComponent } from './login-menu/login-menu.component';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { LogoutComponent } from './logout/logout.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { ApplicationPaths } from './api-authorization.constants';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
HttpClientModule,
|
||||
RouterModule.forChild(
|
||||
[
|
||||
{ path: ApplicationPaths.Register, component: LoginComponent },
|
||||
{ path: ApplicationPaths.Profile, component: LoginComponent },
|
||||
{ path: ApplicationPaths.Login, component: LoginComponent },
|
||||
{ path: ApplicationPaths.LoginFailed, component: LoginComponent },
|
||||
{ path: ApplicationPaths.LoginCallback, component: LoginComponent },
|
||||
{ path: ApplicationPaths.LogOut, component: LogoutComponent },
|
||||
{ path: ApplicationPaths.LoggedOut, component: LogoutComponent },
|
||||
{ path: ApplicationPaths.LogOutCallback, component: LogoutComponent }
|
||||
]
|
||||
)
|
||||
],
|
||||
declarations: [LoginMenuComponent, LoginComponent, LogoutComponent],
|
||||
exports: [LoginMenuComponent, LoginComponent, LogoutComponent]
|
||||
})
|
||||
export class ApiAuthorizationModule { }
|
|
@ -0,0 +1,15 @@
|
|||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { AuthorizeGuard } from './authorize.guard';
|
||||
|
||||
describe('AuthorizeGuard', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [AuthorizeGuard]
|
||||
});
|
||||
});
|
||||
|
||||
it('should ...', inject([AuthorizeGuard], (guard: AuthorizeGuard) => {
|
||||
expect(guard).toBeTruthy();
|
||||
}));
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AuthorizeService } from './authorize.service';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { ApplicationPaths, QueryParameterNames } from './api-authorization.constants';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthorizeGuard implements CanActivate {
|
||||
constructor(private authorize: AuthorizeService, private router: Router) {
|
||||
}
|
||||
canActivate(
|
||||
_next: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
|
||||
return this.authorize.isAuthenticated()
|
||||
.pipe(tap(isAuthenticated => this.handleAuthorization(isAuthenticated, state)));
|
||||
}
|
||||
|
||||
private handleAuthorization(isAuthenticated: boolean, state: RouterStateSnapshot) {
|
||||
if (!isAuthenticated) {
|
||||
this.router.navigate(ApplicationPaths.LoginPathComponents, {
|
||||
queryParams: {
|
||||
[QueryParameterNames.ReturnUrl]: state.url
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { AuthorizeInterceptor } from './authorize.interceptor';
|
||||
|
||||
describe('AuthorizeInterceptor', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [AuthorizeInterceptor]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([AuthorizeInterceptor], (service: AuthorizeInterceptor) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
|
@ -0,0 +1,54 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AuthorizeService } from './authorize.service';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthorizeInterceptor implements HttpInterceptor {
|
||||
constructor(private authorize: AuthorizeService) { }
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
return this.authorize.getAccessToken()
|
||||
.pipe(mergeMap(token => this.processRequestWithToken(token, req, next)));
|
||||
}
|
||||
|
||||
// Checks if there is an access_token available in the authorize service
|
||||
// and adds it to the request in case it's targeted at the same origin as the
|
||||
// single page application.
|
||||
private processRequestWithToken(token: string, req: HttpRequest<any>, next: HttpHandler) {
|
||||
if (!!token && this.isSameOriginUrl(req)) {
|
||||
req = req.clone({
|
||||
setHeaders: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return next.handle(req);
|
||||
}
|
||||
|
||||
private isSameOriginUrl(req: any) {
|
||||
// It's an absolute url with the same origin.
|
||||
if (req.url.startsWith(`${window.location.origin}/`)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's a protocol relative url with the same origin.
|
||||
// For example: //www.example.com/api/Products
|
||||
if (req.url.startsWith(`//${window.location.host}/`)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's a relative url like /api/Products
|
||||
if (/^\/[^\/].*/.test(req.url)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's an absolute or protocol relative url that
|
||||
// doesn't have the same origin.
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { AuthorizeService } from './authorize.service';
|
||||
|
||||
describe('AuthorizeService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [AuthorizeService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([AuthorizeService], (service: AuthorizeService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
|
@ -0,0 +1,324 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { User, UserManager, WebStorageStateStore } from 'oidc-client';
|
||||
import { BehaviorSubject, concat, from, Observable } from 'rxjs';
|
||||
import { filter, map, mergeMap, take, tap } from 'rxjs/operators';
|
||||
import { ApplicationPaths, ApplicationName } from './api-authorization.constants';
|
||||
|
||||
export type IAuthenticationResult =
|
||||
SuccessAuthenticationResult |
|
||||
FailureAuthenticationResult |
|
||||
RedirectAuthenticationResult;
|
||||
|
||||
export interface SuccessAuthenticationResult {
|
||||
status: AuthenticationResultStatus.Success;
|
||||
state: any;
|
||||
}
|
||||
|
||||
export interface FailureAuthenticationResult {
|
||||
status: AuthenticationResultStatus.Fail;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface RedirectAuthenticationResult {
|
||||
status: AuthenticationResultStatus.Redirect;
|
||||
redirectUrl: string;
|
||||
}
|
||||
|
||||
export enum AuthenticationResultStatus {
|
||||
Success,
|
||||
Redirect,
|
||||
Fail
|
||||
}
|
||||
|
||||
export interface IUser {
|
||||
name: string;
|
||||
}
|
||||
|
||||
// Private interfaces
|
||||
enum LoginMode {
|
||||
Silent,
|
||||
PopUp,
|
||||
Redirect
|
||||
}
|
||||
|
||||
interface IAuthenticationState {
|
||||
mode: LoginMode;
|
||||
userState?: any;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthorizeService {
|
||||
private userManager: UserManager;
|
||||
private userSubject: BehaviorSubject<IUser | null> = new BehaviorSubject(null);
|
||||
|
||||
public isAuthenticated(): Observable<boolean> {
|
||||
return this.getUser().pipe(map(u => !!u));
|
||||
}
|
||||
|
||||
public getUser(): Observable<IUser | null> {
|
||||
return concat(
|
||||
this.userSubject.pipe(take(1), filter(u => !!u)),
|
||||
this.getUserFromStorage().pipe(filter(u => !!u), tap(u => this.userSubject.next(u))),
|
||||
this.userSubject.asObservable());
|
||||
}
|
||||
|
||||
public getAccessToken(): Observable<string> {
|
||||
return from(this.ensureUserManagerInitialized())
|
||||
.pipe(mergeMap(() => from(this.userManager.getUser())),
|
||||
map(user => 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.
|
||||
public async signIn(state: any): Promise<IAuthenticationResult> {
|
||||
await this.ensureUserManagerInitialized();
|
||||
let user: User = null;
|
||||
try {
|
||||
user = await this.userManager.signinSilent(this.createArguments(LoginMode.Silent));
|
||||
this.userSubject.next(user.profile);
|
||||
return this.success(state);
|
||||
} catch (silentError) {
|
||||
// User might not be authenticated, fallback to popup authentication
|
||||
console.log('Silent authentication error: ', silentError);
|
||||
|
||||
try {
|
||||
user = await this.userManager.signinPopup(this.createArguments(LoginMode.PopUp));
|
||||
this.userSubject.next(user.profile);
|
||||
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.');
|
||||
}
|
||||
console.log('Popup authentication error: ', popupError);
|
||||
|
||||
// PopUps might be blocked by the user, fallback to redirect
|
||||
try {
|
||||
const signInRequest = await this.userManager.createSigninRequest(
|
||||
this.createArguments(LoginMode.Redirect, state));
|
||||
return this.redirect(signInRequest.url);
|
||||
} catch (redirectError) {
|
||||
console.log('Redirect authentication error: ', redirectError);
|
||||
return this.error(redirectError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We are receiving a callback from the IdP. This code can be running in 3 situations:
|
||||
// 1) As a hidden iframe started by a silent login on signIn (above). The code in the main
|
||||
// browser window will close the iframe after returning from signInSilent.
|
||||
// 2) As a PopUp window started by a pop-up login on signIn (above). The code in the main
|
||||
// browser window will close the pop-up window after returning from signInPopUp
|
||||
// 3) On the main browser window when the IdP redirects back to the app. We will process
|
||||
// the response and redirect to the return url or display an error message.
|
||||
public async completeSignIn(url: string): Promise<IAuthenticationResult> {
|
||||
await this.ensureUserManagerInitialized();
|
||||
let response;
|
||||
try {
|
||||
response = await this.getSignInResponse(url);
|
||||
if (!!response.error) {
|
||||
return this.error(`${response.error}: ${response.error_description}`);
|
||||
}
|
||||
} catch (processSignInResponseError) {
|
||||
if (processSignInResponseError.error === 'login_required') {
|
||||
// This error is thrown by the underlying oidc client when it tries to log in
|
||||
// the user silently as in case 1 defined above and the IdP requires the user
|
||||
// to enter credentials. We let the user manager handle the response to notify
|
||||
// the main window.
|
||||
response = processSignInResponseError;
|
||||
} else {
|
||||
console.log('There was an error processing the sign-in response: ', processSignInResponseError);
|
||||
return this.error('There was an error processing the sign-in response.');
|
||||
}
|
||||
}
|
||||
|
||||
const authenticationState = response.state as IAuthenticationState;
|
||||
const mode = authenticationState.mode;
|
||||
|
||||
switch (mode) {
|
||||
case LoginMode.Silent:
|
||||
try {
|
||||
await this.userManager.signinSilentCallback(url);
|
||||
return this.success(undefined);
|
||||
} catch (silentCallbackError) {
|
||||
console.log('Silent callback authentication error: ', silentCallbackError);
|
||||
return this.error('Silent callback authentication error');
|
||||
}
|
||||
case LoginMode.PopUp:
|
||||
try {
|
||||
await this.userManager.signinPopupCallback(url);
|
||||
return this.success(undefined);
|
||||
} catch (popupCallbackError) {
|
||||
console.log('Popup callback authentication error: ', popupCallbackError);
|
||||
return this.error('Popup callback authentication error.');
|
||||
}
|
||||
case LoginMode.Redirect:
|
||||
try {
|
||||
const user = await this.userManager.signinRedirectCallback(url);
|
||||
this.userSubject.next(user.profile);
|
||||
return this.success(response.state.userState);
|
||||
} catch (redirectCallbackError) {
|
||||
console.log('Redirect callback authentication error: ', redirectCallbackError);
|
||||
return this.error('Redirect callback authentication error.');
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid login mode '${mode}'.`);
|
||||
}
|
||||
}
|
||||
// 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.
|
||||
public async signOut(state: any): Promise<IAuthenticationResult> {
|
||||
await this.ensureUserManagerInitialized();
|
||||
try {
|
||||
await this.userManager.signoutPopup(this.createArguments(LoginMode.PopUp));
|
||||
this.userSubject.next(null);
|
||||
return this.success(state);
|
||||
} catch (popupSignOutError) {
|
||||
console.log('Popup signout error: ', popupSignOutError);
|
||||
try {
|
||||
const signInRequest = await this.userManager.createSignoutRequest(
|
||||
this.createArguments(LoginMode.Redirect, state));
|
||||
return this.redirect(signInRequest.url);
|
||||
} catch (redirectSignOutError) {
|
||||
console.log('Redirect signout error: ', popupSignOutError);
|
||||
return this.error(redirectSignOutError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We are receiving a callback from the IdP. This code can be running in 2 situations:
|
||||
// 1) As a PopUp window started by a pop-up login on signOut (above). The code in the main
|
||||
// browser window will close the pop-up window after returning from signOutPopUp
|
||||
// 2) On the main browser window when the IdP redirects back to the app. We will process
|
||||
// the response and redirect to the logged-out url or display an error message.
|
||||
public async completeSignOut(url: string): Promise<IAuthenticationResult> {
|
||||
await this.ensureUserManagerInitialized();
|
||||
let response;
|
||||
try {
|
||||
response = await await this.getSignOutResponse(url);
|
||||
} catch (processSignOutResponseError) {
|
||||
console.log('There was an error processing the sign-out response: ', processSignOutResponseError);
|
||||
response = processSignOutResponseError;
|
||||
}
|
||||
|
||||
if (!!response.error) {
|
||||
return this.error(`${response.error}: ${response.error_description}`);
|
||||
}
|
||||
|
||||
const authenticationState = response.state as IAuthenticationState;
|
||||
const mode = (authenticationState && authenticationState.mode) ||
|
||||
!!window.opener ? LoginMode.PopUp : LoginMode.Redirect;
|
||||
|
||||
switch (mode) {
|
||||
case LoginMode.PopUp:
|
||||
try {
|
||||
await this.userManager.signoutPopupCallback(url);
|
||||
return this.success(response.state && response.state.userState);
|
||||
} catch (popupCallbackError) {
|
||||
console.log('Popup signout callback error: ', popupCallbackError);
|
||||
return this.error('Popup signout callback error');
|
||||
}
|
||||
case LoginMode.Redirect:
|
||||
try {
|
||||
await this.userManager.signoutRedirectCallback(url);
|
||||
this.userSubject.next(null);
|
||||
return this.success(response.state.userState);
|
||||
} catch (redirectCallbackError) {
|
||||
console.log('Redirect signout callback error: ', redirectCallbackError);
|
||||
return this.error('Redirect signout callback error');
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid LoginMode '${mode}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
private async getSignInResponse(url: string) {
|
||||
const keys = await this.userManager.settings.stateStore.getAllKeys();
|
||||
const states = keys.map(key => ({ key, state: this.userManager.settings.stateStore.get(key) }));
|
||||
for (const state of states) {
|
||||
state.state = await state.state;
|
||||
}
|
||||
try {
|
||||
const response = await this.userManager.processSigninResponse(url);
|
||||
return response;
|
||||
} finally {
|
||||
for (const state of states) {
|
||||
await this.userManager.settings.stateStore.set(state.key, state.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getSignOutResponse(url: string) {
|
||||
const keys = await this.userManager.settings.stateStore.getAllKeys();
|
||||
const states = keys.map(key => ({ key, state: this.userManager.settings.stateStore.get(key) }));
|
||||
for (const state of states) {
|
||||
state.state = await state.state;
|
||||
}
|
||||
try {
|
||||
const response = await this.userManager.processSignoutResponse(url);
|
||||
return response;
|
||||
} finally {
|
||||
for (const state of states) {
|
||||
await this.userManager.settings.stateStore.set(state.key, state.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createArguments(mode: LoginMode, state?: any): any {
|
||||
if (mode !== LoginMode.Silent) {
|
||||
return { data: { mode, userState: state } };
|
||||
} else {
|
||||
return { data: { mode, userState: state }, redirect_uri: this.userManager.settings.redirect_uri };
|
||||
}
|
||||
}
|
||||
|
||||
private error(message: string): IAuthenticationResult {
|
||||
return { status: AuthenticationResultStatus.Fail, message };
|
||||
}
|
||||
|
||||
private success(state: any): IAuthenticationResult {
|
||||
return { status: AuthenticationResultStatus.Success, state };
|
||||
}
|
||||
|
||||
private redirect(redirectUrl: string): IAuthenticationResult {
|
||||
return { status: AuthenticationResultStatus.Redirect, redirectUrl };
|
||||
}
|
||||
|
||||
private async ensureUserManagerInitialized(): Promise<void> {
|
||||
if (this.userManager !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(ApplicationPaths.ApiAuthorizationClientConfigurationUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Could not load settings for '${ApplicationName}'`);
|
||||
}
|
||||
|
||||
const settings: any = await response.json();
|
||||
settings.automaticSilentRenew = true;
|
||||
settings.includeIdTokenInSilentRenew = true;
|
||||
settings.userStore = new WebStorageStateStore({
|
||||
prefix: ApplicationName
|
||||
});
|
||||
this.userManager = new UserManager(settings);
|
||||
}
|
||||
|
||||
private getUserFromStorage(): Observable<IUser> {
|
||||
return from(this.ensureUserManagerInitialized())
|
||||
.pipe(
|
||||
mergeMap(() => this.userManager.getUser()),
|
||||
map(u => u && u.profile));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<ul class="navbar-nav" *ngIf="isAuthenticated | async">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" [routerLink]='["/authentication/profile"]' title="Manage">Hello {{ userName | async }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" [routerLink]='["/authentication/logout"]' [state]='{ local: true }' title="Logout">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav" *ngIf="!(isAuthenticated | async)">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" [routerLink]='["/authentication/register"]'>Register</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" [routerLink]='["/authentication/login"]'>Login</a>
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginMenuComponent } from './login-menu.component';
|
||||
|
||||
describe('LoginMenuComponent', () => {
|
||||
let component: LoginMenuComponent;
|
||||
let fixture: ComponentFixture<LoginMenuComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LoginMenuComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { AuthorizeService } from '../authorize.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login-menu',
|
||||
templateUrl: './login-menu.component.html',
|
||||
styleUrls: ['./login-menu.component.css']
|
||||
})
|
||||
export class LoginMenuComponent implements OnInit {
|
||||
public isAuthenticated: Observable<boolean>;
|
||||
public userName: Observable<string>;
|
||||
|
||||
constructor(private authorizeService: AuthorizeService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.isAuthenticated = this.authorizeService.isAuthenticated();
|
||||
this.userName = this.authorizeService.getUser().pipe(map(u => u && u.name));
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<p>{{ message | async }}</p>
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginComponent } from './login.component';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LoginComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,132 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { AuthorizeService, AuthenticationResultStatus } from '../authorize.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { LoginActions, QueryParameterNames, ApplicationPaths, ReturnUrlType } from '../api-authorization.constants';
|
||||
|
||||
// The main responsibility of this component is to handle the user's login process.
|
||||
// This is the starting point for the login process. Any component that needs to authenticate
|
||||
// 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.
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.css']
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
private message = new BehaviorSubject<string>(null);
|
||||
|
||||
constructor(
|
||||
private authorizeService: AuthorizeService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router) { }
|
||||
|
||||
async ngOnInit() {
|
||||
const action = this.activatedRoute.snapshot.url[1];
|
||||
switch (action.path) {
|
||||
case LoginActions.Login:
|
||||
await this.login(this.getReturnUrl());
|
||||
break;
|
||||
case LoginActions.LoginCallback:
|
||||
await this.processLoginCallback();
|
||||
break;
|
||||
case LoginActions.LoginFailed:
|
||||
const message = this.activatedRoute.snapshot.queryParamMap.get(QueryParameterNames.Message);
|
||||
this.message.next(message);
|
||||
break;
|
||||
case LoginActions.Profile:
|
||||
this.redirectToProfile();
|
||||
break;
|
||||
case LoginActions.Register:
|
||||
this.redirectToRegister();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async login(returnUrl: string): Promise<void> {
|
||||
const state: INavigationState = { returnUrl };
|
||||
const result = await this.authorizeService.signIn(state);
|
||||
this.message.next(undefined);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
// We replace the location here so that in case the user hits the back
|
||||
// arrow from within the login page he doesn't get into an infinite
|
||||
// redirect loop.
|
||||
window.location.replace(result.redirectUrl);
|
||||
break;
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(returnUrl);
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
await this.router.navigate(ApplicationPaths.LoginFailedPathComponents, {
|
||||
queryParams: { [QueryParameterNames.Message]: result.message }
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid status result ${(result as any).status}.`);
|
||||
}
|
||||
}
|
||||
|
||||
private async processLoginCallback(): Promise<void> {
|
||||
const url = window.location.href;
|
||||
const result = await this.authorizeService.completeSignIn(url);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
// There should not be any redirects as completeSignIn never redirects.
|
||||
throw new Error('Should not redirect.');
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(this.getReturnUrl(result.state));
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.message.next(result.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private redirectToRegister(): any {
|
||||
this.redirectToApiAuthorizationPath(
|
||||
`${ApplicationPaths.IdentityRegisterPath}?returnUrl=${encodeURI('/' + ApplicationPaths.Login)}`);
|
||||
}
|
||||
|
||||
private redirectToProfile(): void {
|
||||
this.redirectToApiAuthorizationPath(ApplicationPaths.IdentityManagePath);
|
||||
}
|
||||
|
||||
private async navigateToReturnUrl(returnUrl: string) {
|
||||
// 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.
|
||||
await this.router.navigateByUrl(returnUrl, {
|
||||
replaceUrl: true
|
||||
});
|
||||
}
|
||||
|
||||
private getReturnUrl(state?: INavigationState): string {
|
||||
const fromQuery = (this.activatedRoute.snapshot.queryParams as INavigationState).returnUrl;
|
||||
// If the url is comming from the query string, check that is either
|
||||
// a relative url or an absolute url
|
||||
if (fromQuery &&
|
||||
!(fromQuery.startsWith(`${window.location.origin}/`) ||
|
||||
/\/[^\/].*/.test(fromQuery))) {
|
||||
// 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 ||
|
||||
ApplicationPaths.DefaultLoginRedirectPath;
|
||||
}
|
||||
|
||||
private redirectToApiAuthorizationPath(apiAuthorizationPath: string) {
|
||||
// It's important that we do a replace here so that when the user hits the back arrow on the
|
||||
// browser he gets sent back to where it was on the app instead of to an endpoint on this
|
||||
// component.
|
||||
const redirectUrl = `${window.location.origin}${apiAuthorizationPath}`;
|
||||
window.location.replace(redirectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
interface INavigationState {
|
||||
[ReturnUrlType]: string;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<p>{{ message | async }}</p>
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LogoutComponent } from './logout.component';
|
||||
|
||||
describe('LogoutComponent', () => {
|
||||
let component: LogoutComponent;
|
||||
let fixture: ComponentFixture<LogoutComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LogoutComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LogoutComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,117 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { AuthenticationResultStatus, AuthorizeService } from '../authorize.service';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { LogoutActions, ApplicationPaths, ReturnUrlType } from '../api-authorization.constants';
|
||||
|
||||
// The main responsibility of this component is to handle the user's logout process.
|
||||
// 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.
|
||||
@Component({
|
||||
selector: 'app-logout',
|
||||
templateUrl: './logout.component.html',
|
||||
styleUrls: ['./logout.component.css']
|
||||
})
|
||||
export class LogoutComponent implements OnInit {
|
||||
private message = new BehaviorSubject<string>(null);
|
||||
|
||||
constructor(
|
||||
private authorizeService: AuthorizeService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router) { }
|
||||
|
||||
async ngOnInit() {
|
||||
const action = this.activatedRoute.snapshot.url[1];
|
||||
switch (action.path) {
|
||||
case LogoutActions.Logout:
|
||||
if (!!window.history.state.local) {
|
||||
await this.logout(this.getReturnUrl());
|
||||
} else {
|
||||
// This prevents regular links to <app>/authentication/logout from triggering a logout
|
||||
this.message.next('The logout was not initiated from within the page.');
|
||||
}
|
||||
|
||||
break;
|
||||
case LogoutActions.LogoutCallback:
|
||||
await this.processLogoutCallback();
|
||||
break;
|
||||
case LogoutActions.LoggedOut:
|
||||
this.message.next('You successfully logged out!');
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid action '${action}'`);
|
||||
}
|
||||
}
|
||||
|
||||
private async logout(returnUrl: string): Promise<void> {
|
||||
const state: INavigationState = { returnUrl };
|
||||
const isauthenticated = await this.authorizeService.isAuthenticated().pipe(
|
||||
take(1)
|
||||
).toPromise();
|
||||
if (isauthenticated) {
|
||||
const result = await this.authorizeService.signOut(state);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
// We replace the location here so that in case the user hits the back
|
||||
// arrow from within the IdP he doesn't get into an infinite redirect loop.
|
||||
window.location.replace(result.redirectUrl);
|
||||
break;
|
||||
case AuthenticationResultStatus.Success:
|
||||
await this.navigateToReturnUrl(returnUrl);
|
||||
break;
|
||||
case AuthenticationResultStatus.Fail:
|
||||
this.message.next(result.message);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid authentication result status.');
|
||||
}
|
||||
} else {
|
||||
this.message.next('You successfully logged out!');
|
||||
}
|
||||
}
|
||||
|
||||
private async processLogoutCallback(): Promise<void> {
|
||||
const url = window.location.href;
|
||||
const result = await this.authorizeService.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.message.next(result.message);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid authentication result status.');
|
||||
}
|
||||
}
|
||||
|
||||
private async navigateToReturnUrl(returnUrl: string) {
|
||||
await this.router.navigateByUrl(returnUrl, {
|
||||
replaceUrl: true
|
||||
});
|
||||
}
|
||||
|
||||
private getReturnUrl(state?: INavigationState): string {
|
||||
const fromQuery = (this.activatedRoute.snapshot.queryParams as INavigationState).returnUrl;
|
||||
// If the url is comming from the query string, check that is either
|
||||
// a relative url or an absolute url
|
||||
if (fromQuery &&
|
||||
!(fromQuery.startsWith(`${window.location.origin}/`) ||
|
||||
/\/[^\/].*/.test(fromQuery))) {
|
||||
// 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 ||
|
||||
ApplicationPaths.LoggedOut;
|
||||
}
|
||||
}
|
||||
|
||||
interface INavigationState {
|
||||
[ReturnUrlType]: string;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
@ -9,6 +9,11 @@ import { NavMenuComponent } from './nav-menu/nav-menu.component';
|
|||
import { HomeComponent } from './home/home.component';
|
||||
import { CounterComponent } from './counter/counter.component';
|
||||
import { FetchDataComponent } from './fetch-data/fetch-data.component';
|
||||
////#if (IndividualLocalAuth)
|
||||
import { ApiAuthorizationModule } from 'src/api-authorization/api-authorization.module';
|
||||
import { AuthorizeGuard } from 'src/api-authorization/authorize.guard';
|
||||
import { AuthorizeInterceptor } from 'src/api-authorization/authorize.interceptor';
|
||||
////#endif
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -22,13 +27,26 @@ import { FetchDataComponent } from './fetch-data/fetch-data.component';
|
|||
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
////#if (IndividualLocalAuth)
|
||||
ApiAuthorizationModule,
|
||||
////#endif
|
||||
RouterModule.forRoot([
|
||||
{ path: '', component: HomeComponent, pathMatch: 'full' },
|
||||
{ path: 'counter', component: CounterComponent },
|
||||
////#if (IndividualLocalAuth)
|
||||
{ path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
|
||||
////#else
|
||||
{ path: 'fetch-data', component: FetchDataComponent },
|
||||
////#endif
|
||||
])
|
||||
],
|
||||
////#if (IndividualLocalAuth)
|
||||
providers: [
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthorizeInterceptor, multi: true }
|
||||
],
|
||||
////#else
|
||||
providers: [],
|
||||
////#endif
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse" [ngClass]='{"show": isExpanded}'>
|
||||
<!--#if (IndividualLocalAuth) -->
|
||||
<app-login-menu></app-login-menu>
|
||||
<!--#endif -->
|
||||
<ul class="navbar-nav flex-grow">
|
||||
<li class="nav-item" [routerLinkActive]='["link-active"]' [routerLinkActiveOptions]='{ exact: true }'>
|
||||
<a class="nav-link text-dark" [routerLink]='["/"]'>Home</a>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Company.WebApplication1.Controllers
|
||||
{
|
||||
public class OidcConfigurationController : Controller
|
||||
{
|
||||
public OidcConfigurationController(IClientRequestParametersProvider clientRequestParametersProvider)
|
||||
{
|
||||
ClientRequestParametersProvider = clientRequestParametersProvider;
|
||||
}
|
||||
|
||||
public IClientRequestParametersProvider ClientRequestParametersProvider { get; }
|
||||
|
||||
[HttpGet("_configuration/{clientId}")]
|
||||
public IActionResult GetClientRequestParameters([FromRoute]string clientId)
|
||||
{
|
||||
var parameters = ClientRequestParametersProvider.GetClientParameters(HttpContext, clientId);
|
||||
return Ok(parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,9 +3,15 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
#if (IndividualLocalAuth)
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
#endif
|
||||
|
||||
namespace Company.WebApplication1.Controllers
|
||||
{
|
||||
#if (IndividualLocalAuth)
|
||||
[Authorize]
|
||||
#endif
|
||||
[Route("api/[controller]")]
|
||||
public class SampleDataController : Controller
|
||||
{
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
using Company.WebApplication1.Models;
|
||||
using IdentityServer4.EntityFramework.Options;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Company.WebApplication1.Data
|
||||
{
|
||||
public class ApplicationDbContext : ApiAuthorizationDbContext<ApplicationUser>
|
||||
{
|
||||
public ApplicationDbContext(
|
||||
DbContextOptions options,
|
||||
IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
304
src/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs
сгенерированный
Normal file
304
src/content/Angular-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs
сгенерированный
Normal file
|
@ -0,0 +1,304 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Company.WebApplication1.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("00000000000000_CreateIdentitySchema")]
|
||||
partial class CreateIdentitySchema
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
b.HasIndex("DeviceCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DeviceCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
public partial class CreateIdentitySchema : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
UserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||
PasswordHash = table.Column<string>(nullable: true),
|
||||
SecurityStamp = table.Column<string>(nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||
PhoneNumber = table.Column<string>(nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||
AccessFailedCount = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DeviceCodes",
|
||||
columns: table => new
|
||||
{
|
||||
UserCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DeviceCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: false),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DeviceCodes", x => x.UserCode);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PersistedGrants",
|
||||
columns: table => new
|
||||
{
|
||||
Key = table.Column<string>(maxLength: 200, nullable: false),
|
||||
Type = table.Column<string>(maxLength: 50, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PersistedGrants", x => x.Key);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
RoleId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderKey = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||
UserId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
RoleId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Name = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Value = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true,
|
||||
filter: "[NormalizedName] IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true,
|
||||
filter: "[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DeviceCodes_DeviceCode",
|
||||
table: "DeviceCodes",
|
||||
column: "DeviceCode",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DeviceCodes_UserCode",
|
||||
table: "DeviceCodes",
|
||||
column: "UserCode",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PersistedGrants_SubjectId_ClientId_Type",
|
||||
table: "PersistedGrants",
|
||||
columns: new[] { "SubjectId", "ClientId", "Type" });
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "DeviceCodes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PersistedGrants");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Company.WebApplication1.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
b.HasIndex("DeviceCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DeviceCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
297
src/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs
сгенерированный
Normal file
297
src/content/Angular-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs
сгенерированный
Normal file
|
@ -0,0 +1,297 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Company.WebApplication1.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("00000000000000_CreateIdentitySchema")]
|
||||
partial class CreateIdentitySchema
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1");
|
||||
|
||||
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
b.HasIndex("DeviceCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DeviceCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
public partial class CreateIdentitySchema : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
UserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||
PasswordHash = table.Column<string>(nullable: true),
|
||||
SecurityStamp = table.Column<string>(nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||
PhoneNumber = table.Column<string>(nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||
AccessFailedCount = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DeviceCodes",
|
||||
columns: table => new
|
||||
{
|
||||
UserCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DeviceCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: false),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DeviceCodes", x => x.UserCode);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PersistedGrants",
|
||||
columns: table => new
|
||||
{
|
||||
Key = table.Column<string>(maxLength: 200, nullable: false),
|
||||
Type = table.Column<string>(maxLength: 50, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PersistedGrants", x => x.Key);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
RoleId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderKey = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||
UserId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
RoleId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Name = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Value = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DeviceCodes_DeviceCode",
|
||||
table: "DeviceCodes",
|
||||
column: "DeviceCode",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DeviceCodes_UserCode",
|
||||
table: "DeviceCodes",
|
||||
column: "UserCode",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PersistedGrants_SubjectId_ClientId_Type",
|
||||
table: "PersistedGrants",
|
||||
columns: new[] { "SubjectId", "ClientId", "Type" });
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "DeviceCodes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PersistedGrants");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Company.WebApplication1.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1");
|
||||
|
||||
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
b.HasIndex("DeviceCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DeviceCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Company.WebApplication1.Models
|
||||
{
|
||||
public class ApplicationUser : IdentityUser
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
@using Microsoft.AspNetCore.Identity
|
||||
@using Company.WebApplication1.Models;
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
|
||||
<ul class="navbar-nav">
|
||||
@if (SignInManager.IsSignedIn(User))
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="/">
|
||||
<button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
|
||||
</form>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
|
@ -4,7 +4,7 @@
|
|||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:8080",
|
||||
//#if(NoHttps)
|
||||
//#if(!RequiresHttps)
|
||||
"sslPort": 0
|
||||
//#else
|
||||
"sslPort": 44300
|
||||
|
@ -22,7 +22,7 @@
|
|||
"Company.WebApplication1": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
//#if(NoHttps)
|
||||
//#if(!RequiresHttps)
|
||||
"applicationUrl": "http://localhost:5000",
|
||||
//#else
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||
|
@ -32,4 +32,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
#if (IndividualLocalAuth)
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
#if (!NoHttps)
|
||||
#if (IndividualLocalAuth)
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.UI;
|
||||
#endif
|
||||
#if (RequiresHttps)
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SpaServices.AngularCli;
|
||||
#if (IndividualLocalAuth)
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Company.WebApplication1.Data;
|
||||
using Company.WebApplication1.Models;
|
||||
#endif
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
@ -23,6 +35,26 @@ namespace Company.WebApplication1
|
|||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
#if (IndividualLocalAuth)
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
#if (UseLocalDB)
|
||||
options.UseSqlServer(
|
||||
Configuration.GetConnectionString("DefaultConnection")));
|
||||
#else
|
||||
options.UseSqlite(
|
||||
Configuration.GetConnectionString("DefaultConnection")));
|
||||
#endif
|
||||
|
||||
services.AddDefaultIdentity<ApplicationUser>()
|
||||
.AddDefaultUI(UIFramework.Bootstrap4)
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
services.AddIdentityServer()
|
||||
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddIdentityServerJwt();
|
||||
#endif
|
||||
services.AddMvc()
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
|
@ -39,11 +71,14 @@ namespace Company.WebApplication1
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
#if (IndividualLocalAuth)
|
||||
app.UseDatabaseErrorPage();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
#if (!NoHttps)
|
||||
#if (RequiresHttps)
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
@ -55,6 +90,11 @@ namespace Company.WebApplication1
|
|||
#endif
|
||||
app.UseStaticFiles();
|
||||
app.UseSpaStaticFiles();
|
||||
#if (IndividualLocalAuth)
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseIdentityServer();
|
||||
#endif
|
||||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
|
|
Двоичный файл не отображается.
|
@ -5,5 +5,14 @@
|
|||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
////#if (IndividualLocalAuth)
|
||||
// },
|
||||
// "IdentityServer": {
|
||||
// "Key": {
|
||||
// "Type": "Development"
|
||||
// }
|
||||
// }
|
||||
////#else
|
||||
// }
|
||||
////#endif
|
||||
}
|
||||
|
|
|
@ -1,8 +1,26 @@
|
|||
{
|
||||
////#if (IndividualLocalAuth)
|
||||
// "ConnectionStrings": {
|
||||
////#if (UseLocalDB)
|
||||
// "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-Company.WebApplication1-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true"
|
||||
////#else
|
||||
// "DefaultConnection": "DataSource=app.db"
|
||||
////#endif
|
||||
// },
|
||||
////#endif
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
},
|
||||
////#if (IndividualLocalAuth)
|
||||
// "IdentityServer": {
|
||||
// "Clients": {
|
||||
// "Company.WebApplication1": {
|
||||
// "Profile": "IdentityServerSPA"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
////#endif
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/dotnetcli.host",
|
||||
"symbolInfo": {
|
||||
"UseLocalDB": {
|
||||
"longName": "use-local-db"
|
||||
},
|
||||
"Framework": {
|
||||
"longName": "framework"
|
||||
},
|
||||
"skipRestore": {
|
||||
"longName": "no-restore",
|
||||
"shortName": ""
|
||||
},
|
||||
"HttpPort": {
|
||||
"isHidden": true
|
||||
},
|
||||
|
@ -18,9 +17,19 @@
|
|||
"longName": "exclude-launch-settings",
|
||||
"shortName": ""
|
||||
},
|
||||
"UserSecretsId": {
|
||||
"isHidden": true
|
||||
},
|
||||
"skipRestore": {
|
||||
"longName": "no-restore",
|
||||
"shortName": ""
|
||||
},
|
||||
"NoHttps": {
|
||||
"longName": "no-https",
|
||||
"shortName": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"usageExamples": [
|
||||
"--auth Individual"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -23,23 +23,89 @@
|
|||
"exclude": [
|
||||
".template.config/**"
|
||||
],
|
||||
"copyOnly": [
|
||||
"wwwroot/**"
|
||||
],
|
||||
"modifiers": [
|
||||
{
|
||||
"condition": "(!IndividualLocalAuth)",
|
||||
"exclude": [
|
||||
"Pages/Shared/_LoginPartial.cshtml",
|
||||
"Data/**",
|
||||
"Models/**",
|
||||
"ClientApp/src/components/api-authorization/**",
|
||||
"Controllers/OidcConfigurationController.cs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(!IndividualLocalAuth || UseLocalDB)",
|
||||
"exclude": [
|
||||
"app.db"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(ExcludeLaunchSettings)",
|
||||
"exclude": [
|
||||
"Properties/launchSettings.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(IndividualLocalAuth && UseLocalDB)",
|
||||
"rename": {
|
||||
"Data/SQLServer/": "Data/Migrations/"
|
||||
},
|
||||
"exclude": [
|
||||
"Data/SQLite/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"condition": "(IndividualLocalAuth && !UseLocalDB)",
|
||||
"rename": {
|
||||
"Data/SQLite/": "Data/Migrations/"
|
||||
},
|
||||
"exclude": [
|
||||
"Data/SQLServer/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"symbols": {
|
||||
"auth": {
|
||||
"type": "parameter",
|
||||
"datatype": "choice",
|
||||
"choices": [
|
||||
{
|
||||
"choice": "None",
|
||||
"description": "No authentication"
|
||||
},
|
||||
{
|
||||
"choice": "Individual",
|
||||
"description": "Individual authentication"
|
||||
}
|
||||
],
|
||||
"defaultValue": "None",
|
||||
"description": "The type of authentication to use"
|
||||
},
|
||||
"UserSecretsId": {
|
||||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"replaces": "aspnet-Company.WebApplication1-53bc9b9d-9d6a-45d4-8429-2a2761773502",
|
||||
"defaultValue": "aspnet-Company.WebApplication1-53bc9b9d-9d6a-45d4-8429-2a2761773502",
|
||||
"description": "The ID to use for secrets (use with OrgReadAccess or Individual auth)."
|
||||
},
|
||||
"ExcludeLaunchSettings": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"defaultValue": "false",
|
||||
"description": "Whether to exclude launchSettings.json from the generated template."
|
||||
},
|
||||
"skipRestore": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"description": "If specified, skips the automatic restore of the project on create.",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
"HttpPort": {
|
||||
"type": "parameter",
|
||||
"datatype": "integer",
|
||||
|
@ -61,7 +127,7 @@
|
|||
"HttpsPort": {
|
||||
"type": "parameter",
|
||||
"datatype": "integer",
|
||||
"description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used)."
|
||||
"description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if IndividualLocalAuth is used)."
|
||||
},
|
||||
"HttpsPortGenerated": {
|
||||
"type": "generated",
|
||||
|
@ -80,6 +146,30 @@
|
|||
},
|
||||
"replaces": "44300"
|
||||
},
|
||||
"IndividualLocalAuth": {
|
||||
"type": "computed",
|
||||
"value": "(auth == \"Individual\")"
|
||||
},
|
||||
"NoAuth": {
|
||||
"type": "computed",
|
||||
"value": "(!(IndividualLocalAuth))"
|
||||
},
|
||||
"RequiresHttps": {
|
||||
"type": "computed",
|
||||
"value": "(IndividualLocalAuth || !NoHttps)"
|
||||
},
|
||||
"NoHttps": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"defaultValue": "false",
|
||||
"description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth."
|
||||
},
|
||||
"UseLocalDB": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"defaultValue": "false",
|
||||
"description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual or --auth IndividualB2C is specified."
|
||||
},
|
||||
"Framework": {
|
||||
"type": "parameter",
|
||||
"description": "The target framework for the project.",
|
||||
|
@ -96,20 +186,9 @@
|
|||
"HostIdentifier": {
|
||||
"type": "bind",
|
||||
"binding": "HostIdentifier"
|
||||
},
|
||||
"skipRestore": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"description": "If specified, skips the automatic restore of the project on create.",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
"NoHttps": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"defaultValue": "false",
|
||||
"description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth."
|
||||
}
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"language": "C#",
|
||||
"type": "project"
|
||||
|
|
|
@ -1016,6 +1016,31 @@
|
|||
"babel-types": "^6.24.1"
|
||||
}
|
||||
},
|
||||
"babel-polyfill": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
|
||||
"integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"core-js": "^2.5.0",
|
||||
"regenerator-runtime": "^0.10.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz",
|
||||
"integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==",
|
||||
"optional": true
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.10.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
|
||||
"integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"babel-preset-env": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz",
|
||||
|
@ -4085,7 +4110,8 @@
|
|||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -4450,7 +4476,8 @@
|
|||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -4498,6 +4525,7 @@
|
|||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -4536,11 +4564,13 @@
|
|||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -6068,6 +6098,11 @@
|
|||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"jsrsasign": {
|
||||
"version": "8.0.12",
|
||||
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz",
|
||||
"integrity": "sha1-Iqu5ZW00owuVMENnIINeicLlwxY="
|
||||
},
|
||||
"jsx-ast-utils": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
|
||||
|
@ -6882,6 +6917,15 @@
|
|||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
||||
},
|
||||
"oidc-client": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/oidc-client/-/oidc-client-1.6.1.tgz",
|
||||
"integrity": "sha512-buA9G0hlFjUwxoL/xuHunBtTgoICtJSojnZtATqMliUTKxPYAzHprOQ85Lj0hgF+Zv8lI/ViqaNFDG0Z5KCUKA==",
|
||||
"requires": {
|
||||
"babel-polyfill": ">=6.9.1",
|
||||
"jsrsasign": "^8.0.12"
|
||||
}
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"bootstrap": "^4.1.3",
|
||||
"jquery": "3.3.1",
|
||||
"merge": "^1.2.1",
|
||||
"oidc-client": "^1.6.1",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"react-router-bootstrap": "^0.24.4",
|
||||
|
|
|
@ -4,6 +4,11 @@ import { Layout } from './components/Layout';
|
|||
import { Home } from './components/Home';
|
||||
import { FetchData } from './components/FetchData';
|
||||
import { Counter } from './components/Counter';
|
||||
////#if (IndividualLocalAuth)
|
||||
import AuthorizeRoute from './components/api-authorization/AuthorizeRoute';
|
||||
import ApiAuthorizationRoutes from './components/api-authorization/ApiAuthorizationRoutes'
|
||||
import { ApplicationPaths } from './components/api-authorization/ApiAuthorizationConstants';
|
||||
////#endif
|
||||
|
||||
export default class App extends Component {
|
||||
static displayName = App.name;
|
||||
|
@ -13,7 +18,13 @@ export default class App extends Component {
|
|||
<Layout>
|
||||
<Route exact path='/' component={Home} />
|
||||
<Route path='/counter' component={Counter} />
|
||||
////#if (!IndividualLocalAuth)
|
||||
<Route path='/fetch-data' component={FetchData} />
|
||||
////#endif
|
||||
////#if (IndividualLocalAuth)
|
||||
<AuthorizeRoute path='/fetch-data' component={FetchData} />
|
||||
<Route path={ApplicationPaths.ApiAuthorizationPrefix} Component={ApiAuthorizationRoutes} />
|
||||
////#endif
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const storeFake = (state: any) => ({
|
||||
default: () => {},
|
||||
subscribe: () => {},
|
||||
dispatch: () => {},
|
||||
getState: () => ({ ...state })
|
||||
});
|
||||
const store = storeFake({});
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<MemoryRouter>
|
||||
<App/>
|
||||
</MemoryRouter>
|
||||
</Provider>, document.createElement('div'));
|
||||
});
|
|
@ -1,14 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { Route } from 'react-router';
|
||||
import Layout from './components/Layout';
|
||||
import Home from './components/Home';
|
||||
import Counter from './components/Counter';
|
||||
import FetchData from './components/FetchData';
|
||||
|
||||
export default () => (
|
||||
<Layout>
|
||||
<Route exact path='/' component={Home}/>
|
||||
<Route path='/counter' component={Counter} />
|
||||
<Route path='/fetch-data/:startDateIndex?' component={FetchData} />
|
||||
</Layout>
|
||||
);
|
|
@ -1,35 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { ApplicationState } from '../store';
|
||||
import * as CounterStore from '../store/Counter';
|
||||
|
||||
type CounterProps =
|
||||
CounterStore.CounterState &
|
||||
typeof CounterStore.actionCreators &
|
||||
RouteComponentProps<{}>;
|
||||
|
||||
class Counter extends React.PureComponent<CounterProps> {
|
||||
public render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h1>Counter</h1>
|
||||
|
||||
<p>This is a simple example of a React component.</p>
|
||||
|
||||
<p>Current count: <strong>{this.props.count}</strong></p>
|
||||
|
||||
<button type="button"
|
||||
className="btn btn-primary btn-lg"
|
||||
onClick={() => { this.props.increment(); }}>
|
||||
Increment
|
||||
</button>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(
|
||||
(state: ApplicationState) => state.counter,
|
||||
CounterStore.actionCreators
|
||||
)(Counter);
|
|
@ -1,20 +1,21 @@
|
|||
import React, { Component } from 'react';
|
||||
////#if (IndividualLocalAuth)
|
||||
import authService from './api-authorization/AuthorizeService'
|
||||
////#endif
|
||||
|
||||
export class FetchData extends Component {
|
||||
static displayName = FetchData.name;
|
||||
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { forecasts: [], loading: true };
|
||||
|
||||
fetch('api/SampleData/WeatherForecasts')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
this.setState({ forecasts: data, loading: false });
|
||||
});
|
||||
}
|
||||
|
||||
static renderForecastsTable (forecasts) {
|
||||
componentDidMount() {
|
||||
this.populateWeatherData();
|
||||
}
|
||||
|
||||
static renderForecastsTable(forecasts) {
|
||||
return (
|
||||
<table className='table table-striped'>
|
||||
<thead>
|
||||
|
@ -39,7 +40,7 @@ export class FetchData extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
let contents = this.state.loading
|
||||
? <p><em>Loading...</em></p>
|
||||
: FetchData.renderForecastsTable(this.state.forecasts);
|
||||
|
@ -52,4 +53,19 @@ export class FetchData extends Component {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
async populateWeatherData() {
|
||||
////#if (IndividualLocalAuth)
|
||||
const token = authService.getAccessToken();
|
||||
const response = await fetch('api/SampleData/WeatherForecasts', {
|
||||
headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
const data = await response.json();
|
||||
this.setState({ forecasts: data, loading: false });
|
||||
////#else
|
||||
const response = await fetch('api/SampleData/WeatherForecasts');
|
||||
const data = await response.json();
|
||||
this.setState({ forecasts: data, loading: false });
|
||||
////#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ApplicationState } from '../store';
|
||||
import * as WeatherForecastsStore from '../store/WeatherForecasts';
|
||||
|
||||
// At runtime, Redux will merge together...
|
||||
type WeatherForecastProps =
|
||||
WeatherForecastsStore.WeatherForecastsState // ... state we've requested from the Redux store
|
||||
& typeof WeatherForecastsStore.actionCreators // ... plus action creators we've requested
|
||||
& RouteComponentProps<{ startDateIndex: string }>; // ... plus incoming routing parameters
|
||||
|
||||
|
||||
class FetchData extends React.PureComponent<WeatherForecastProps> {
|
||||
// This method is called when the component is first added to the document
|
||||
public componentDidMount() {
|
||||
this.ensureDataFetched();
|
||||
}
|
||||
|
||||
// This method is called when the route parameters change
|
||||
public componentDidUpdate() {
|
||||
this.ensureDataFetched();
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h1>Weather forecast</h1>
|
||||
<p>This component demonstrates fetching data from the server and working with URL parameters.</p>
|
||||
{ this.renderForecastsTable() }
|
||||
{ this.renderPagination() }
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
private ensureDataFetched() {
|
||||
const startDateIndex = parseInt(this.props.match.params.startDateIndex, 10) || 0;
|
||||
this.props.requestWeatherForecasts(startDateIndex);
|
||||
}
|
||||
|
||||
private renderForecastsTable() {
|
||||
return (
|
||||
<table className='table table-striped'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Temp. (C)</th>
|
||||
<th>Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.props.forecasts.map((forecast: WeatherForecastsStore.WeatherForecast) =>
|
||||
<tr key={forecast.dateFormatted}>
|
||||
<td>{forecast.dateFormatted}</td>
|
||||
<td>{forecast.temperatureC}</td>
|
||||
<td>{forecast.temperatureF}</td>
|
||||
<td>{forecast.summary}</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPagination() {
|
||||
const prevStartDateIndex = (this.props.startDateIndex || 0) - 5;
|
||||
const nextStartDateIndex = (this.props.startDateIndex || 0) + 5;
|
||||
|
||||
return (
|
||||
<div className="d-flex justify-content-between">
|
||||
<Link className='btn btn-outline-secondary btn-sm' to={`/fetch-data/${prevStartDateIndex}`}>Previous</Link>
|
||||
{this.props.isLoading && <span>Loading...</span>}
|
||||
<Link className='btn btn-outline-secondary btn-sm' to={`/fetch-data/${nextStartDateIndex}`}>Next</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: ApplicationState) => state.weatherForecasts, // Selects which state properties are merged into the component's props
|
||||
WeatherForecastsStore.actionCreators // Selects which action creators are merged into the component's props
|
||||
)(FetchData as any);
|
|
@ -1,23 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
const Home = () => (
|
||||
<div>
|
||||
<h1>Hello, world!</h1>
|
||||
<p>Welcome to your new single-page application, built with:</p>
|
||||
<ul>
|
||||
<li><a href='https://get.asp.net/'>ASP.NET Core</a> and <a href='https://msdn.microsoft.com/en-us/library/67ef8sbd.aspx'>C#</a> for cross-platform server-side code</li>
|
||||
<li><a href='https://facebook.github.io/react/'>React</a> and <a href='https://redux.js.org/'>Redux</a> for client-side code</li>
|
||||
<li><a href='http://getbootstrap.com/'>Bootstrap</a> for layout and styling</li>
|
||||
</ul>
|
||||
<p>To help you get started, we've also set up:</p>
|
||||
<ul>
|
||||
<li><strong>Client-side navigation</strong>. For example, click <em>Counter</em> then <em>Back</em> to return here.</li>
|
||||
<li><strong>Development server integration</strong>. In development mode, the development server from <code>create-react-app</code> runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.</li>
|
||||
<li><strong>Efficient production builds</strong>. In production mode, development-time features are disabled, and your <code>dotnet publish</code> configuration produces minified, efficiently bundled JavaScript files.</li>
|
||||
</ul>
|
||||
<p>The <code>ClientApp</code> subdirectory is a standard React application based on the <code>create-react-app</code> template. If you open a command prompt in that directory, you can run <code>npm</code> commands such as <code>npm test</code> or <code>npm install</code>.</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default connect()(Home);
|
|
@ -1,12 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { Container } from 'reactstrap';
|
||||
import NavMenu from './NavMenu';
|
||||
|
||||
export default (props: { children?: React.ReactNode }) => (
|
||||
<React.Fragment>
|
||||
<NavMenu/>
|
||||
<Container>
|
||||
{props.children}
|
||||
</Container>
|
||||
</React.Fragment>
|
||||
);
|
|
@ -1,6 +1,9 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Collapse, Container, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
////#if (IndividualLocalAuth)
|
||||
import { LoginMenu } from './api-authorization/LoginMenu';
|
||||
////#endif
|
||||
import './NavMenu.css';
|
||||
|
||||
export class NavMenu extends Component {
|
||||
|
@ -39,6 +42,10 @@ export class NavMenu extends Component {
|
|||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to="/fetch-data">Fetch data</NavLink>
|
||||
</NavItem>
|
||||
////#if (IndividualLocalAuth)
|
||||
<LoginMenu>
|
||||
</LoginMenu>
|
||||
////#endif
|
||||
</ul>
|
||||
</Collapse>
|
||||
</Container>
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { Collapse, Container, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
import './NavMenu.css';
|
||||
|
||||
export default class NavMenu extends React.PureComponent<{}, { isOpen: boolean }> {
|
||||
public state = {
|
||||
isOpen: false
|
||||
};
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<header>
|
||||
<Navbar className="navbar-expand-sm navbar-toggleable-sm border-bottom box-shadow mb-3" light>
|
||||
<Container>
|
||||
<NavbarBrand tag={Link} to="/">Company.WebApplication1</NavbarBrand>
|
||||
<NavbarToggler onClick={this.toggle} className="mr-2"/>
|
||||
<Collapse className="d-sm-inline-flex flex-sm-row-reverse" isOpen={this.state.isOpen} navbar>
|
||||
<ul className="navbar-nav flex-grow">
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to="/">Home</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to="/counter">Counter</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink tag={Link} className="text-dark" to="/fetch-data">Fetch data</NavLink>
|
||||
</NavItem>
|
||||
</ul>
|
||||
</Collapse>
|
||||
</Container>
|
||||
</Navbar>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
private toggle = () => {
|
||||
this.setState({
|
||||
isOpen: !this.state.isOpen
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
export const ApplicationName = 'Company.WebApplication1';
|
||||
|
||||
export const QueryParameterNames = {
|
||||
ReturnUrl: 'returnUrl',
|
||||
Message: 'message'
|
||||
};
|
||||
|
||||
export const LogoutActions = {
|
||||
LogoutCallback: 'logout-callback',
|
||||
Logout: 'logout',
|
||||
LoggedOut: 'logged-out'
|
||||
};
|
||||
|
||||
export const LoginActions = {
|
||||
Login: 'login',
|
||||
LoginCallback: 'login-callback',
|
||||
LoginFailed: 'login-failed',
|
||||
Profile: 'profile',
|
||||
Register: 'register'
|
||||
};
|
||||
|
||||
const prefix = '/authentication';
|
||||
|
||||
export const ApplicationPaths = {
|
||||
DefaultLoginRedirectPath: '/',
|
||||
ApiAuthorizationClientConfigurationUrl: `/_configuration/${ApplicationName}`,
|
||||
ApiAuthorizationPrefix = prefix,
|
||||
Login: `${prefix}/${LoginActions.Login}`,
|
||||
LoginFailed: `${prefix}/${LoginActions.LoginFailed}`,
|
||||
LoginCallback: `${prefix}/${LoginActions.LoginCallback}`,
|
||||
Register: `${prefix}/${LoginActions.Register}`,
|
||||
Profile: `${prefix}/${LoginActions.Profile}`,
|
||||
LogOut: `${prefix}/${LogoutActions.Logout}`,
|
||||
LoggedOut: `${prefix}/${LogoutActions.LoggedOut}`,
|
||||
LogOutCallback: `${prefix}/${LogoutActions.LogoutCallback}`,
|
||||
IdentityRegisterPath: '/Identity/Account/Register',
|
||||
IdentityManagePath: '/Identity/Account/Manage'
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
import React, { Component, Fragment } from 'react';
|
||||
import { Route } from 'react-router';
|
||||
import { Login } from './components/api-authorization/Login'
|
||||
import { Logout } from './components/api-authorization/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>);
|
||||
}
|
||||
}
|
||||
|
||||
function loginAction(name){
|
||||
return (<Login action={name}></Login>);
|
||||
}
|
||||
|
||||
function logoutAction(name) {
|
||||
return (<Logout action={name}></Logout>);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react'
|
||||
import { Component } from 'react'
|
||||
import { Route, Redirect } from 'react-router-dom'
|
||||
import { ApplicationPaths, QueryParameterNames } from './ApiAuthorizationConstants'
|
||||
import authService from './AuthorizeService'
|
||||
|
||||
export default class AuthorizeRoute extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
ready: false,
|
||||
authenticated: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.populateAuthenticationState();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { ready, authenticated } = this.state;
|
||||
const redirectUrl = `${ApplicationPaths.Login}?${QueryParameterNames.ReturnUrl}=${encodeURI(window.location.href)}`
|
||||
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} />
|
||||
}
|
||||
}} />
|
||||
}
|
||||
}
|
||||
|
||||
async populateAuthenticationState() {
|
||||
const authenticated = await authService.isAuthenticated();
|
||||
this.setState({ ready: true, authenticated });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
import { UserManager, WebStorageStateStore } from 'oidc-client';
|
||||
import { ApplicationPaths, ApplicationName } from './ApiAuthorizationConstants';
|
||||
|
||||
export class AuthorizeService {
|
||||
_callbacks = [];
|
||||
_nextSubscriptionId = 0;
|
||||
_user = null;
|
||||
_isAuthenticated = false;
|
||||
|
||||
async isAuthenticated() {
|
||||
const user = await this.getUser();
|
||||
return !!user;
|
||||
}
|
||||
|
||||
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(LoginMode.Silent));
|
||||
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 {
|
||||
const popUpUser = await this.userManager.signinPopup(this.createArguments(LoginMode.PopUp));
|
||||
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.");
|
||||
}
|
||||
console.log("Popup authentication error: ", popUpError);
|
||||
|
||||
// PopUps might be blocked by the user, fallback to redirect
|
||||
try {
|
||||
const signInRequest = await this.userManager.createSigninRequest(
|
||||
this.createArguments(LoginMode.Redirect, state));
|
||||
return this.redirect(signInRequest.url);
|
||||
} catch (redirectError) {
|
||||
console.log("Redirect authentication error: ", redirectError);
|
||||
return this.error(redirectError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We are receiving a callback from the IdP. This code can be running in 3 situations:
|
||||
// 1) As a hidden iframe started by a silent login on signIn (above). The code in the main
|
||||
// browser window will close the iframe after returning from signInSilent.
|
||||
// 2) As a PopUp window started by a pop-up login on signIn (above). The code in the main
|
||||
// browser window will close the pop-up window after returning from signInPopUp
|
||||
// 3) On the main browser window when the IdP redirects back to the app. We will process
|
||||
// the response and redirect to the return url or display an error message.
|
||||
async completeSignIn(url) {
|
||||
await this.ensureUserManagerInitialized();
|
||||
let response = undefined;
|
||||
try {
|
||||
response = await this.getSignInResponse(url);
|
||||
if (!!response.error) {
|
||||
return this.error(`${response.error}: ${response.error_description}`);
|
||||
}
|
||||
} catch (processSignInResponseError) {
|
||||
if (processSignInResponseError.error === "login_required") {
|
||||
// This error is thrown by the underlying oidc client when it tries to log in
|
||||
// the user silently as in case 1 defined above and the IdP requires the user
|
||||
// to enter credentials. We let the user manager handle the response to notify
|
||||
// the main window.
|
||||
response = processSignInResponseError;
|
||||
} else {
|
||||
console.log("There was an error processing the sign-in response: ", processSignInResponseError);
|
||||
return this.error("There was an error processing the sign-in response.");
|
||||
}
|
||||
}
|
||||
|
||||
const authenticationState = response.state;
|
||||
const mode = authenticationState.mode;
|
||||
|
||||
switch (mode) {
|
||||
case LoginMode.Silent:
|
||||
try {
|
||||
await this.userManager.signinSilentCallback(url);
|
||||
return this.success(undefined);
|
||||
} catch (silentCallbackError) {
|
||||
console.log("Silent callback authentication error: ", silentCallbackError);
|
||||
return this.error("Silent callback authentication error");
|
||||
}
|
||||
case LoginMode.PopUp:
|
||||
try {
|
||||
await this.userManager.signinPopupCallback(url);
|
||||
return this.success(undefined);
|
||||
} catch (popupCallbackError) {
|
||||
console.log("Popup callback authentication error: ", popupCallbackError);
|
||||
return this.error("Popup callback authentication error.");
|
||||
}
|
||||
case LoginMode.Redirect:
|
||||
try {
|
||||
let user = await this.userManager.signinRedirectCallback(url);
|
||||
this.updateState(user);
|
||||
return this.success(response.state.userState);
|
||||
} catch (redirectCallbackError) {
|
||||
console.log("Redirect callback authentication error: ", redirectCallbackError);
|
||||
return this.error("Redirect callback authentication error.");
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid login mode '${mode}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
await this.userManager.signoutPopup(this.createArguments(LoginMode.PopUp));
|
||||
this.updateState(undefined);
|
||||
return this.success(state);
|
||||
} catch (popupSignOutError) {
|
||||
console.log("Popup signout error: ", popupSignOutError);
|
||||
try {
|
||||
const signOutRequest = await this.userManager.createSignoutRequest(
|
||||
this.createArguments(LoginMode.Redirect, state));
|
||||
return this.redirect(signOutRequest.url);
|
||||
} catch (redirectSignOutError) {
|
||||
console.log("Redirect signout error: ", redirectSignOutError);
|
||||
return this.error(redirectSignOutError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We are receiving a callback from the IdP. This code can be running in 2 situations:
|
||||
// 1) As a PopUp window started by a pop-up login on signOut (above). The code in the main
|
||||
// browser window will close the pop-up window after returning from signOutPopUp
|
||||
// 2) On the main browser window when the IdP redirects back to the app. We will process
|
||||
// the response and redirect to the logged-out url or display an error message.
|
||||
async completeSignOut(url) {
|
||||
await this.ensureUserManagerInitialized();
|
||||
let response = undefined;
|
||||
try {
|
||||
response = await await this.getSignOutResponse(url);
|
||||
} catch (processSignOutResponseError) {
|
||||
console.log("There was an error processing the sign-out response: ", processSignOutResponseError);
|
||||
response = processSignOutResponseError;
|
||||
}
|
||||
|
||||
if (!!response.error) {
|
||||
return this.error(`${response.error}: ${response.error_description}`);
|
||||
}
|
||||
|
||||
const authenticationState = response.state;
|
||||
const mode = (authenticationState && authenticationState.mode) ||
|
||||
!!window.opener ? LoginMode.PopUp : LoginMode.Redirect;
|
||||
|
||||
switch (mode) {
|
||||
case LoginMode.PopUp:
|
||||
try {
|
||||
await this.userManager.signoutPopupCallback(url);
|
||||
return this.success(response.state && response.state.userState);
|
||||
} catch (popupCallbackError) {
|
||||
console.log("Popup signout callback error: ", popupCallbackError);
|
||||
return this.error("Popup signout callback error");
|
||||
}
|
||||
case LoginMode.Redirect:
|
||||
try {
|
||||
await this.userManager.signoutRedirectCallback(url);
|
||||
this.updateState(undefined);
|
||||
return this.success(response.state.userState);
|
||||
} catch (redirectCallbackError) {
|
||||
console.log("Redirect signout callback error: ", redirectCallbackError);
|
||||
return this.error("Redirect signout callback error");
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid LoginMode '${mode}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
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}`);
|
||||
}
|
||||
|
||||
this._callbacks = this._callbacks.splice(subscriptionIndex[0].index, 1);
|
||||
}
|
||||
|
||||
notifySubscribers() {
|
||||
for (let i = 0; i < this._callbacks.length; i++) {
|
||||
const callback = this._callbacks[i].callback;
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
async getSignInResponse(url) {
|
||||
const keys = await this.userManager.settings.stateStore.getAllKeys();
|
||||
const states = keys.map(key => ({ key, state: this.userManager.settings.stateStore.get(key) }));
|
||||
for (const state of states) {
|
||||
state.state = await state.state;
|
||||
}
|
||||
try {
|
||||
const response = await this.userManager.processSigninResponse(url);
|
||||
return response;
|
||||
} finally {
|
||||
for (const state of states) {
|
||||
await this.userManager.settings.stateStore.set(state.key, state.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getSignOutResponse(url) {
|
||||
const keys = await this.userManager.settings.stateStore.getAllKeys();
|
||||
const states = keys.map(key => ({ key, state: this.userManager.settings.stateStore.get(key) }));
|
||||
for (const state of states) {
|
||||
state.state = await state.state;
|
||||
}
|
||||
try {
|
||||
const response = await this.userManager.processSignoutResponse(url);
|
||||
return response;
|
||||
} finally {
|
||||
for (const state of states) {
|
||||
await this.userManager.settings.stateStore.set(state.key, state.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createArguments(mode, state) {
|
||||
if (mode !== LoginMode.Silent) {
|
||||
return { data: { mode, userState: state } };
|
||||
} else {
|
||||
return { data: { mode, userState: state }, redirect_uri: this.userManager.settings.redirect_uri };
|
||||
}
|
||||
}
|
||||
|
||||
error(message) {
|
||||
return { status: AuthenticationResultStatus.Fail, message };
|
||||
}
|
||||
|
||||
success(state) {
|
||||
return { status: AuthenticationResultStatus.Success, state };
|
||||
}
|
||||
|
||||
redirect(redirectUrl) {
|
||||
return { status: AuthenticationResultStatus.Redirect, redirectUrl };
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static get instance() { return authService }
|
||||
}
|
||||
|
||||
const LoginMode = {
|
||||
Silent: 'silent',
|
||||
PopUp: 'popup',
|
||||
Redirect: 'redirect'
|
||||
}
|
||||
|
||||
const authService = new AuthorizeService();
|
||||
|
||||
export default authService;
|
||||
|
||||
export const AuthenticationResultStatus = {
|
||||
Redirect: 'redirect',
|
||||
Success: 'success',
|
||||
Fail: 'fail'
|
||||
};
|
|
@ -0,0 +1,137 @@
|
|||
import React from 'react'
|
||||
import { Component } from 'react';
|
||||
import authService from './AuthorizeService';
|
||||
import { AuthenticationResultStatus } from './AuthorizeService';
|
||||
import { LoginActions, QueryParameterNames, ApplicationPaths } from './ApiAuthorizationConstants';
|
||||
|
||||
// The main responsibility of this component is to handle the user's login process.
|
||||
// This is the starting point for the login process. Any component that needs to authenticate
|
||||
// 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);
|
||||
|
||||
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:
|
||||
// We replace the location here so that in case the user hits the back
|
||||
// arrow from within the login page he doesn't get into an infinite
|
||||
// redirect loop.
|
||||
window.location.replace(result.redirectUrl);
|
||||
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) {
|
||||
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 he gets 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import React, { Component, Fragment } from 'react';
|
||||
import { NavItem, NavLink } from 'reactstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
import authService from './AuthorizeService';
|
||||
import { ApplicationPaths } from './ApiAuthorizationConstants';
|
||||
|
||||
export class LoginMenu extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
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 = { 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>);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
import React from 'react'
|
||||
import { Component } from 'react';
|
||||
import authService from './AuthorizeService';
|
||||
import { AuthenticationResultStatus } from './AuthorizeService';
|
||||
import { QueryParameterNames, LogoutActions, ApplicationPaths } from './ApiAuthorizationConstants';
|
||||
|
||||
// The main responsibility of this component is to handle the user's logout process.
|
||||
// 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);
|
||||
|
||||
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>);
|
||||
} 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 logout(returnUrl) {
|
||||
const state = { returnUrl };
|
||||
const isauthenticated = await authService.isAuthenticated();
|
||||
if (isauthenticated) {
|
||||
const result = await authService.signOut(state);
|
||||
switch (result.status) {
|
||||
case AuthenticationResultStatus.Redirect:
|
||||
// We replace the location here so that in case the user hits the back
|
||||
// arrow from within the IdP he doesn't get into an infinite redirect loop.
|
||||
window.location.replace(result.redirectUrl);
|
||||
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!" });
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
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,27 +0,0 @@
|
|||
import 'bootstrap/dist/css/bootstrap.css';
|
||||
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { ConnectedRouter } from 'connected-react-router';
|
||||
import { createBrowserHistory } from 'history';
|
||||
import configureStore from './store/configureStore';
|
||||
import App from './App';
|
||||
import registerServiceWorker from './registerServiceWorker';
|
||||
|
||||
// Create browser history to use in the Redux store
|
||||
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href') as string;
|
||||
const history = createBrowserHistory({ basename: baseUrl });
|
||||
|
||||
// Get the application-wide store instance, prepopulating with state from the server where available.
|
||||
const store = configureStore(history);
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<App />
|
||||
</ConnectedRouter>
|
||||
</Provider>,
|
||||
document.getElementById('root'));
|
||||
|
||||
registerServiceWorker();
|
|
@ -1 +0,0 @@
|
|||
/// <reference types="react-scripts" />
|
|
@ -1,105 +0,0 @@
|
|||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
||||
// cached resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
||||
// This link also includes instructions on opting out of this behavior.
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export default function register() {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const url = process.env.PUBLIC_URL as string;
|
||||
const publicUrl = new URL(url, window.location.toString());
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Lets check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl);
|
||||
} else {
|
||||
// Is not local host. Just register service worker
|
||||
registerValidSW(swUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl: string) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing as ServiceWorker;
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the old content will have been purged and
|
||||
// the fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in your web app.
|
||||
console.log('New content is available; please refresh.');
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl: string) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (response.status === 404 || (contentType && contentType.indexOf('javascript') === -1)) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('No internet connection found. App is running in offline mode.');
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import { Action, Reducer } from 'redux';
|
||||
|
||||
// -----------------
|
||||
// STATE - This defines the type of data maintained in the Redux store.
|
||||
|
||||
export interface CounterState {
|
||||
count: number;
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// ACTIONS - These are serializable (hence replayable) descriptions of state transitions.
|
||||
// They do not themselves have any side-effects; they just describe something that is going to happen.
|
||||
// Use @typeName and isActionType for type detection that works even after serialization/deserialization.
|
||||
|
||||
export interface IncrementCountAction { type: 'INCREMENT_COUNT' }
|
||||
export interface DecrementCountAction { type: 'DECREMENT_COUNT' }
|
||||
|
||||
// Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the
|
||||
// declared type strings (and not any other arbitrary string).
|
||||
export type KnownAction = IncrementCountAction | DecrementCountAction;
|
||||
|
||||
// ----------------
|
||||
// ACTION CREATORS - These are functions exposed to UI components that will trigger a state transition.
|
||||
// They don't directly mutate state, but they can have external side-effects (such as loading data).
|
||||
|
||||
export const actionCreators = {
|
||||
increment: () => <IncrementCountAction>{ type: 'INCREMENT_COUNT' },
|
||||
decrement: () => <DecrementCountAction>{ type: 'DECREMENT_COUNT' }
|
||||
};
|
||||
|
||||
// ----------------
|
||||
// REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state.
|
||||
|
||||
export const reducer: Reducer<CounterState> = (state: CounterState | undefined, incomingAction: Action): CounterState => {
|
||||
if (state === undefined) {
|
||||
return { count: 0 };
|
||||
}
|
||||
|
||||
const action = incomingAction as KnownAction;
|
||||
switch (action.type) {
|
||||
case 'INCREMENT_COUNT':
|
||||
return { count: state.count + 1 };
|
||||
case 'DECREMENT_COUNT':
|
||||
return { count: state.count - 1 };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
|
@ -1,91 +0,0 @@
|
|||
import { Action, Reducer } from 'redux';
|
||||
import { AppThunkAction } from './';
|
||||
|
||||
// -----------------
|
||||
// STATE - This defines the type of data maintained in the Redux store.
|
||||
|
||||
export interface WeatherForecastsState {
|
||||
isLoading: boolean;
|
||||
startDateIndex?: number;
|
||||
forecasts: WeatherForecast[];
|
||||
}
|
||||
|
||||
export interface WeatherForecast {
|
||||
dateFormatted: string;
|
||||
temperatureC: number;
|
||||
temperatureF: number;
|
||||
summary: string;
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// ACTIONS - These are serializable (hence replayable) descriptions of state transitions.
|
||||
// They do not themselves have any side-effects; they just describe something that is going to happen.
|
||||
|
||||
interface RequestWeatherForecastsAction {
|
||||
type: 'REQUEST_WEATHER_FORECASTS';
|
||||
startDateIndex: number;
|
||||
}
|
||||
|
||||
interface ReceiveWeatherForecastsAction {
|
||||
type: 'RECEIVE_WEATHER_FORECASTS';
|
||||
startDateIndex: number;
|
||||
forecasts: WeatherForecast[];
|
||||
}
|
||||
|
||||
// Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the
|
||||
// declared type strings (and not any other arbitrary string).
|
||||
type KnownAction = RequestWeatherForecastsAction | ReceiveWeatherForecastsAction;
|
||||
|
||||
// ----------------
|
||||
// ACTION CREATORS - These are functions exposed to UI components that will trigger a state transition.
|
||||
// They don't directly mutate state, but they can have external side-effects (such as loading data).
|
||||
|
||||
export const actionCreators = {
|
||||
requestWeatherForecasts: (startDateIndex: number): AppThunkAction<KnownAction> => (dispatch, getState) => {
|
||||
// Only load data if it's something we don't already have (and are not already loading)
|
||||
const appState = getState();
|
||||
if (appState && appState.weatherForecasts && startDateIndex !== appState.weatherForecasts.startDateIndex) {
|
||||
fetch(`api/SampleData/WeatherForecasts?startDateIndex=${startDateIndex}`)
|
||||
.then(response => response.json() as Promise<WeatherForecast[]>)
|
||||
.then(data => {
|
||||
dispatch({ type: 'RECEIVE_WEATHER_FORECASTS', startDateIndex: startDateIndex, forecasts: data });
|
||||
});
|
||||
|
||||
dispatch({ type: 'REQUEST_WEATHER_FORECASTS', startDateIndex: startDateIndex });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------
|
||||
// REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state.
|
||||
|
||||
const unloadedState: WeatherForecastsState = { forecasts: [], isLoading: false };
|
||||
|
||||
export const reducer: Reducer<WeatherForecastsState> = (state: WeatherForecastsState | undefined, incomingAction: Action): WeatherForecastsState => {
|
||||
if (state === undefined) {
|
||||
return unloadedState;
|
||||
}
|
||||
|
||||
const action = incomingAction as KnownAction;
|
||||
switch (action.type) {
|
||||
case 'REQUEST_WEATHER_FORECASTS':
|
||||
return {
|
||||
startDateIndex: action.startDateIndex,
|
||||
forecasts: state.forecasts,
|
||||
isLoading: true
|
||||
};
|
||||
case 'RECEIVE_WEATHER_FORECASTS':
|
||||
// Only accept the incoming data if it matches the most recent request. This ensures we correctly
|
||||
// handle out-of-order responses.
|
||||
if (action.startDateIndex === state.startDateIndex) {
|
||||
return {
|
||||
startDateIndex: action.startDateIndex,
|
||||
forecasts: action.forecasts,
|
||||
isLoading: false
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
import { applyMiddleware, combineReducers, compose, createStore, Reducer } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import { connectRouter, routerMiddleware } from 'connected-react-router';
|
||||
import { History } from 'history';
|
||||
import { ApplicationState, reducers } from './';
|
||||
|
||||
export default function configureStore(history: History, initialState?: ApplicationState) {
|
||||
const middleware = [
|
||||
thunk,
|
||||
routerMiddleware(history)
|
||||
];
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
...reducers,
|
||||
router: connectRouter(history)
|
||||
});
|
||||
|
||||
const enhancers = [];
|
||||
const windowIfDefined = typeof window === 'undefined' ? null : window as any;
|
||||
if (windowIfDefined && windowIfDefined.__REDUX_DEVTOOLS_EXTENSION__) {
|
||||
enhancers.push(windowIfDefined.__REDUX_DEVTOOLS_EXTENSION__());
|
||||
}
|
||||
|
||||
return createStore(
|
||||
rootReducer,
|
||||
initialState,
|
||||
compose(applyMiddleware(...middleware), ...enhancers)
|
||||
);
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import * as WeatherForecasts from './WeatherForecasts';
|
||||
import * as Counter from './Counter';
|
||||
|
||||
// The top-level state object
|
||||
export interface ApplicationState {
|
||||
counter: Counter.CounterState | undefined;
|
||||
weatherForecasts: WeatherForecasts.WeatherForecastsState | undefined;
|
||||
}
|
||||
|
||||
// Whenever an action is dispatched, Redux will update each top-level application state property using
|
||||
// the reducer with the matching name. It's important that the names match exactly, and that the reducer
|
||||
// acts on the corresponding ApplicationState property type.
|
||||
export const reducers = {
|
||||
counter: Counter.reducer,
|
||||
weatherForecasts: WeatherForecasts.reducer
|
||||
};
|
||||
|
||||
// This type can be used as a hint on action creators so that its 'dispatch' and 'getState' params are
|
||||
// correctly typed to match your store.
|
||||
export interface AppThunkAction<TAction> {
|
||||
(dispatch: (action: TAction) => void, getState: () => ApplicationState): void;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Company.WebApplication1.Controllers
|
||||
{
|
||||
public class OidcConfigurationController : Controller
|
||||
{
|
||||
public OidcConfigurationController(IClientRequestParametersProvider clientRequestParametersProvider)
|
||||
{
|
||||
ClientRequestParametersProvider = clientRequestParametersProvider;
|
||||
}
|
||||
|
||||
public IClientRequestParametersProvider ClientRequestParametersProvider { get; }
|
||||
|
||||
[HttpGet("_configuration/{clientId}")]
|
||||
public IActionResult GetClientRequestParameters([FromRoute]string clientId)
|
||||
{
|
||||
var parameters = ClientRequestParametersProvider.GetClientParameters(HttpContext, clientId);
|
||||
return Ok(parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,10 +2,16 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
#if (IndividualLocalAuth)
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Company.WebApplication1.Controllers
|
||||
{
|
||||
#if (IndividualLocalAuth)
|
||||
[Authorize]
|
||||
#endif
|
||||
[Route("api/[controller]")]
|
||||
public class SampleDataController : Controller
|
||||
{
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
using Company.WebApplication1.Models;
|
||||
using IdentityServer4.EntityFramework.Options;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Company.WebApplication1.Data
|
||||
{
|
||||
public class ApplicationDbContext : ApiAuthorizationDbContext<ApplicationUser>
|
||||
{
|
||||
public ApplicationDbContext(
|
||||
DbContextOptions options,
|
||||
IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
304
src/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs
сгенерированный
Normal file
304
src/content/React-CSharp/Data/SQLServer/00000000000000_CreateIdentitySchema.Designer.cs
сгенерированный
Normal file
|
@ -0,0 +1,304 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Company.WebApplication1.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("00000000000000_CreateIdentitySchema")]
|
||||
partial class CreateIdentitySchema
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
b.HasIndex("DeviceCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DeviceCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
public partial class CreateIdentitySchema : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
UserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||
PasswordHash = table.Column<string>(nullable: true),
|
||||
SecurityStamp = table.Column<string>(nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||
PhoneNumber = table.Column<string>(nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||
AccessFailedCount = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DeviceCodes",
|
||||
columns: table => new
|
||||
{
|
||||
UserCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DeviceCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: false),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DeviceCodes", x => x.UserCode);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PersistedGrants",
|
||||
columns: table => new
|
||||
{
|
||||
Key = table.Column<string>(maxLength: 200, nullable: false),
|
||||
Type = table.Column<string>(maxLength: 50, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PersistedGrants", x => x.Key);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
RoleId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderKey = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||
UserId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
RoleId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Name = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Value = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true,
|
||||
filter: "[NormalizedName] IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true,
|
||||
filter: "[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DeviceCodes_DeviceCode",
|
||||
table: "DeviceCodes",
|
||||
column: "DeviceCode",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DeviceCodes_UserCode",
|
||||
table: "DeviceCodes",
|
||||
column: "UserCode",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PersistedGrants_SubjectId_ClientId_Type",
|
||||
table: "PersistedGrants",
|
||||
columns: new[] { "SubjectId", "ClientId", "Type" });
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "DeviceCodes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PersistedGrants");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Company.WebApplication1.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
b.HasIndex("DeviceCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DeviceCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
297
src/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs
сгенерированный
Normal file
297
src/content/React-CSharp/Data/SQLite/00000000000000_CreateIdentitySchema.Designer.cs
сгенерированный
Normal file
|
@ -0,0 +1,297 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Company.WebApplication1.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("00000000000000_CreateIdentitySchema")]
|
||||
partial class CreateIdentitySchema
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1");
|
||||
|
||||
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
b.HasIndex("DeviceCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DeviceCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
public partial class CreateIdentitySchema : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
UserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||
PasswordHash = table.Column<string>(nullable: true),
|
||||
SecurityStamp = table.Column<string>(nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||
PhoneNumber = table.Column<string>(nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||
AccessFailedCount = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DeviceCodes",
|
||||
columns: table => new
|
||||
{
|
||||
UserCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
DeviceCode = table.Column<string>(maxLength: 200, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: false),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DeviceCodes", x => x.UserCode);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PersistedGrants",
|
||||
columns: table => new
|
||||
{
|
||||
Key = table.Column<string>(maxLength: 200, nullable: false),
|
||||
Type = table.Column<string>(maxLength: 50, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PersistedGrants", x => x.Key);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
RoleId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderKey = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||
UserId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
RoleId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Name = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Value = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DeviceCodes_DeviceCode",
|
||||
table: "DeviceCodes",
|
||||
column: "DeviceCode",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DeviceCodes_UserCode",
|
||||
table: "DeviceCodes",
|
||||
column: "UserCode",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PersistedGrants_SubjectId_ClientId_Type",
|
||||
table: "PersistedGrants",
|
||||
columns: new[] { "SubjectId", "ClientId", "Type" });
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "DeviceCodes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PersistedGrants");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Company.WebApplication1.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Company.WebApplication1.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1");
|
||||
|
||||
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
|
||||
{
|
||||
b.Property<string>("UserCode")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<string>("DeviceCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime?>("Expiration")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.HasKey("UserCode");
|
||||
|
||||
b.HasIndex("DeviceCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DeviceCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Company.WebApplication1.Models
|
||||
{
|
||||
public class ApplicationUser : IdentityUser
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
@using Microsoft.AspNetCore.Identity
|
||||
@using Company.WebApplication1.Models;
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
|
||||
<ul class="navbar-nav">
|
||||
@if (SignInManager.IsSignedIn(User))
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="/">
|
||||
<button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
|
||||
</form>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
|
@ -1,10 +1,22 @@
|
|||
#if (IndividualLocalAuth)
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
#if (!NoHttps)
|
||||
#if (IndividualLocalAuth)
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.UI;
|
||||
#endif
|
||||
#if (RequiresHttps)
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
|
||||
#if (IndividualLocalAuth)
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Company.WebApplication1.Data;
|
||||
using Company.WebApplication1.Models;
|
||||
#endif
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
@ -23,6 +35,26 @@ namespace Company.WebApplication1
|
|||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
#if (IndividualLocalAuth)
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
#if (UseLocalDB)
|
||||
options.UseSqlServer(
|
||||
Configuration.GetConnectionString("DefaultConnection")));
|
||||
#else
|
||||
options.UseSqlite(
|
||||
Configuration.GetConnectionString("DefaultConnection")));
|
||||
#endif
|
||||
|
||||
services.AddDefaultIdentity<ApplicationUser>()
|
||||
.AddDefaultUI(UIFramework.Bootstrap4)
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
services.AddIdentityServer()
|
||||
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddIdentityServerJwt();
|
||||
#endif
|
||||
services.AddMvc()
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
|
@ -39,11 +71,14 @@ namespace Company.WebApplication1
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
#if (IndividualLocalAuth)
|
||||
app.UseDatabaseErrorPage();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
#if (!NoHttps)
|
||||
#if (RequiresHttps)
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
@ -55,6 +90,11 @@ namespace Company.WebApplication1
|
|||
#endif
|
||||
app.UseStaticFiles();
|
||||
app.UseSpaStaticFiles();
|
||||
#if (IndividualLocalAuth)
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseIdentityServer();
|
||||
#endif
|
||||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
|
|
Двоичный файл не отображается.
|
@ -5,5 +5,14 @@
|
|||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
////#if (IndividualLocalAuth)
|
||||
// },
|
||||
// "IdentityServer": {
|
||||
// "Key": {
|
||||
// "Type": "Development"
|
||||
// }
|
||||
// }
|
||||
////#else
|
||||
// }
|
||||
////#endif
|
||||
}
|
||||
|
|
|
@ -1,8 +1,26 @@
|
|||
{
|
||||
////#if (IndividualLocalAuth)
|
||||
// "ConnectionStrings": {
|
||||
////#if (UseLocalDB)
|
||||
// "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-Company.WebApplication1-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true"
|
||||
////#else
|
||||
// "DefaultConnection": "DataSource=app.db"
|
||||
////#endif
|
||||
// },
|
||||
////#endif
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
},
|
||||
////#if (IndividualLocalAuth)
|
||||
// "IdentityServer": {
|
||||
// "Clients": {
|
||||
// "Company.WebApplication1": {
|
||||
// "Profile": "IdentityServerSPA"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
////#endif
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче