commit
41b73d6821
|
@ -15,7 +15,7 @@
|
|||
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
|
||||
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
|
||||
</environment>
|
||||
@Html.Raw(JavaScriptSnippet.FullScript)
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
|
|
|
@ -409,4 +409,5 @@ services.AddMvc()
|
|||
|
||||
## See also
|
||||
|
||||
* [Razor Pages authorization conventions](xref:security/authorization/razor-pages-authorization)
|
||||
* [Razor Pages custom route and page model providers](xref:mvc/razor-pages/razor-pages-convention-features)
|
||||
|
|
|
@ -290,3 +290,7 @@ The `ReplaceRouteValueFilter` attribute can be applied directly to a `PageModel`
|
|||
Request the Page3 page from the sample app with at `localhost:5000/OtherPages/Page3/TriggerValue`. Notice how the filter replaces the route value:
|
||||
|
||||
![Request to OtherPages/Page3 with a TriggerValue route segment results in the filter replacing the route value with ReplacementValue.](razor-pages-convention-features/_static/otherpages-page3-filter-replacement-value.png)
|
||||
|
||||
## See also
|
||||
|
||||
* [Razor Pages authorization conventions](xref:security/authorization/razor-pages-authorization)
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
title: Razor Pages authorization conventions in ASP.NET Core
|
||||
author: guardrex
|
||||
description: Learn how to control access to pages with conventions at startup that authorize users and allow anonymous users to access individual pages or folders of pages.
|
||||
ms.author: riande
|
||||
manager: wpickett
|
||||
ms.date: 10/27/2017
|
||||
ms.topic: article
|
||||
ms.assetid: f65ad22d-9472-478a-856c-c59c8681fa71
|
||||
ms.technology: aspnet
|
||||
ms.prod: asp.net-core
|
||||
uid: security/authorization/razor-pages-authorization
|
||||
---
|
||||
# Razor Pages authorization conventions in ASP.NET Core
|
||||
|
||||
By [Luke Latham](https://github.com/guardrex)
|
||||
|
||||
One way to control access in your Razor Pages app is to use authorization conventions at startup. These conventions allow you to authorize users and allow anonymous users to access individual pages or folders of pages. The conventions described in this topic automatically apply [authorization filters](xref:mvc/controllers/filters#authorization-filters) to control access.
|
||||
|
||||
[View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/authorization/razor-pages-authorization/sample) ([how to download](xref:tutorials/index#how-to-download-a-sample))
|
||||
|
||||
## Require authorization to access a page
|
||||
|
||||
Use the [AuthorizePage](/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.authorizepage) convention via [AddRazorPagesOptions](/dotnet/api/microsoft.extensions.dependencyinjection.mvcrazorpagesmvcbuilderextensions.addrazorpagesoptions) to add an [AuthorizeFilter](/dotnet/api/microsoft.aspnetcore.mvc.authorization.authorizefilter) to the page at the specified path:
|
||||
|
||||
[!code-csharp[Main](razor-pages-authorization/sample/Startup.cs?name=snippet1&highlight=2,4)]
|
||||
|
||||
The specified path is the View Engine path, which is the Razor Pages root relative path without an extension and containing only forward slashes.
|
||||
|
||||
An [AuthorizePage overload](/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.authorizepage#Microsoft_Extensions_DependencyInjection_PageConventionCollectionExtensions_AuthorizePage_Microsoft_AspNetCore_Mvc_ApplicationModels_PageConventionCollection_System_String_System_String_) is available if you need to specify an authorization policy.
|
||||
|
||||
## Require authorization to access a folder of pages
|
||||
|
||||
Use the [AuthorizeFolder](/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.authorizefolder) convention via [AddRazorPagesOptions](/dotnet/api/microsoft.extensions.dependencyinjection.mvcrazorpagesmvcbuilderextensions.addrazorpagesoptions) to add an [AuthorizeFilter](/dotnet/api/microsoft.aspnetcore.mvc.authorization.authorizefilter) to all of the pages in a folder at the specified path:
|
||||
|
||||
[!code-csharp[Main](razor-pages-authorization/sample/Startup.cs?name=snippet1&highlight=2,5)]
|
||||
|
||||
The specified path is the View Engine path, which is the Razor Pages root relative path.
|
||||
|
||||
An [AuthorizeFolder overload](/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.authorizefolder#Microsoft_Extensions_DependencyInjection_PageConventionCollectionExtensions_AuthorizeFolder_Microsoft_AspNetCore_Mvc_ApplicationModels_PageConventionCollection_System_String_System_String_) is available if you need to specify an authorization policy.
|
||||
|
||||
## Allow anonymous access to a page
|
||||
|
||||
Use the [AllowAnonymousToPage](/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.allowanonymoustopage) convention via [AddRazorPagesOptions](/dotnet/api/microsoft.extensions.dependencyinjection.mvcrazorpagesmvcbuilderextensions.addrazorpagesoptions) to add an [AllowAnonymousFilter](/dotnet/api/microsoft.aspnetcore.mvc.authorization.allowanonymousfilter) to a page at the specified path:
|
||||
|
||||
[!code-csharp[Main](razor-pages-authorization/sample/Startup.cs?name=snippet1&highlight=2,6)]
|
||||
|
||||
The specified path is the View Engine path, which is the Razor Pages root relative path without an extension and containing only forward slashes.
|
||||
|
||||
## Allow anonymous access to a folder of pages
|
||||
|
||||
Use the [AllowAnonymousToFolder](/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.allowanonymoustofolder) convention via [AddRazorPagesOptions](/dotnet/api/microsoft.extensions.dependencyinjection.mvcrazorpagesmvcbuilderextensions.addrazorpagesoptions) to add an [AllowAnonymousFilter](/dotnet/api/microsoft.aspnetcore.mvc.authorization.allowanonymousfilter) to all of the pages in a folder at the specified path:
|
||||
|
||||
[!code-csharp[Main](razor-pages-authorization/sample/Startup.cs?name=snippet1&highlight=2,7)]
|
||||
|
||||
The specified path is the View Engine path, which is the Razor Pages root relative path.
|
||||
|
||||
## Note on combining authorized and anonymous access
|
||||
|
||||
It's perfectly valid to specify that a folder of pages require authorization and specify that a page within that folder allows anonymous access:
|
||||
|
||||
```csharp
|
||||
// This works.
|
||||
.AuthorizeFolder("/Private").AllowAnonymousToPage("/Private/Public")
|
||||
```
|
||||
|
||||
The reverse, however, isn't true. You can't declare a folder of pages for anonymous access and specify a page within for authorization:
|
||||
|
||||
```csharp
|
||||
// This doesn't work!
|
||||
.AllowAnonymousToFolder("/Public").AuthorizePage("/Public/Private")
|
||||
```
|
||||
|
||||
Requiring authorization on the Private page won't work because when both the `AllowAnonymousFilter` and `AuthorizeFilter` filters are applied to the page, the `AllowAnonymousFilter` wins and controls access.
|
||||
|
||||
## See also
|
||||
|
||||
* [Razor Pages custom route and page model providers](xref:mvc/razor-pages/razor-pages-convention-features)
|
||||
* [PageConventionCollection](/dotnet/api/microsoft.aspnetcore.mvc.applicationmodels.pageconventioncollection) class
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"directory": "wwwroot/lib"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
|
||||
namespace AuthorizationSample.Controllers
|
||||
{
|
||||
[Route("[controller]/[action]")]
|
||||
public class AccountController : Controller
|
||||
{
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
return RedirectToPage("/Account/SignedOut");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace AuthorizationSample.Data
|
||||
{
|
||||
public class ApplicationUser
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string FullName { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
public static class UrlHelperExtensions
|
||||
{
|
||||
public static string GetLocalUrl(this IUrlHelper urlHelper, string localUrl)
|
||||
{
|
||||
if (!urlHelper.IsLocalUrl(localUrl))
|
||||
{
|
||||
return urlHelper.Page("/Index");
|
||||
}
|
||||
|
||||
return localUrl;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
@page
|
||||
@model AboutModel
|
||||
@{
|
||||
ViewData["Title"] = "About";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>@Model.Message</h2>
|
||||
|
||||
<p>Use this area to provide additional information.</p>
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace AuthorizationSample.Pages
|
||||
{
|
||||
public class AboutModel : PageModel
|
||||
{
|
||||
public string Message { get; private set; }
|
||||
|
||||
public string RouteDataText { get; private set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Message = "Your application description page.";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
@page
|
||||
@model LoginModel
|
||||
@{
|
||||
ViewData["Title"] = "Log in";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<form method="post">
|
||||
<h2>Use a local account to log in.</h2>
|
||||
<hr>
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Email"></label>
|
||||
<input asp-for="Input.Email" class="form-control">
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Password"></label>
|
||||
<input asp-for="Input.Password" class="form-control">
|
||||
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-default">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using AuthorizationSample.Data;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AuthorizationSample.Pages.Account
|
||||
{
|
||||
public class LoginModel : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
public string ReturnUrl { get; private set; }
|
||||
|
||||
[TempData]
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public class InputModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
public async Task OnGetAsync(string returnUrl = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ErrorMessage))
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, ErrorMessage);
|
||||
}
|
||||
|
||||
// Clear the existing external cookie
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
ReturnUrl = returnUrl;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
|
||||
{
|
||||
ReturnUrl = returnUrl;
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var user = await AuthenticateUser(Input.Email, Input.Password);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
|
||||
return Page();
|
||||
}
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.Name, user.Email),
|
||||
new Claim("FullName", user.FullName)
|
||||
};
|
||||
|
||||
var claimsIdentity = new ClaimsIdentity(
|
||||
claims, CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
await HttpContext.SignInAsync(
|
||||
CookieAuthenticationDefaults.AuthenticationScheme,
|
||||
new ClaimsPrincipal(claimsIdentity));
|
||||
|
||||
return LocalRedirect(Url.GetLocalUrl(returnUrl));
|
||||
}
|
||||
|
||||
// Something failed. Redisplay the form.
|
||||
return Page();
|
||||
}
|
||||
|
||||
private async Task<ApplicationUser> AuthenticateUser(string email, string password)
|
||||
{
|
||||
// For demonstration purposes, authenticate a user
|
||||
// with a static email address. Ignore the password.
|
||||
// Assume that checking the database takes 500ms
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
if (Input.Email == "maria.rodriguez@contoso.com")
|
||||
{
|
||||
return new ApplicationUser()
|
||||
{
|
||||
Email = "maria.rodriguez@contoso.com",
|
||||
FullName = "Maria Rodriguez"
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
@page
|
||||
@model SignedOutModel
|
||||
@{
|
||||
ViewData["Title"] = "Signed out";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<p>
|
||||
You have successfully signed out.
|
||||
</p>
|
|
@ -0,0 +1,20 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace AuthorizationSample.Pages.Account
|
||||
{
|
||||
public class SignedOutModel : PageModel
|
||||
{
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
// Redirect to home page if the user is authenticated.
|
||||
return RedirectToPage("/Index");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
@using AuthorizationSample.Pages.Account
|
|
@ -0,0 +1,27 @@
|
|||
@page
|
||||
@model ContactModel
|
||||
@{
|
||||
ViewData["Title"] = "Contact";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>@Model.Message</h2>
|
||||
|
||||
<p>
|
||||
This page requires authorization by convention:
|
||||
<code>
|
||||
options.Conventions.AuthorizePage("/Contact");
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<address>
|
||||
One Microsoft Way<br>
|
||||
Redmond, WA 98052-6399<br>
|
||||
<abbr title="Phone">P:</abbr>
|
||||
425.555.0100
|
||||
</address>
|
||||
|
||||
<address>
|
||||
<strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br>
|
||||
<strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
|
||||
</address>
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace AuthorizationSample.Pages
|
||||
{
|
||||
public class ContactModel : PageModel
|
||||
{
|
||||
public string Message { get; private set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Message = "Your contact page.";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
@page
|
||||
@model ErrorModel
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong> and restarting the app or adding <code>.UseEnvironment("Development")</code> to <code>WebHost</code> in <i>Program.cs</i> and restarting the app.
|
||||
</p>
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace AuthorizationSample.Pages
|
||||
{
|
||||
public class ErrorModel : PageModel
|
||||
{
|
||||
public string RequestId { get; private set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
@page
|
||||
@model IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Home page";
|
||||
}
|
||||
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="welcome">Hello @User.Claims.FirstOrDefault(c => c.Type == "FullName")?.Value!</div>
|
||||
<p>Username: @User.Identity.Name</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div id="myCarousel" class="carousel slide" data-ride="carousel" data-interval="6000">
|
||||
<ol class="carousel-indicators">
|
||||
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
|
||||
<li data-target="#myCarousel" data-slide-to="1"></li>
|
||||
<li data-target="#myCarousel" data-slide-to="2"></li>
|
||||
<li data-target="#myCarousel" data-slide-to="3"></li>
|
||||
</ol>
|
||||
<div class="carousel-inner" role="listbox">
|
||||
<div class="item active">
|
||||
<img src="~/images/banner1.svg" alt="ASP.NET" class="img-responsive" />
|
||||
<div class="carousel-caption" role="option">
|
||||
<p>
|
||||
Learn how to build ASP.NET apps that can run anywhere.
|
||||
<a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkID=525028&clcid=0x409">
|
||||
Learn More
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<img src="~/images/banner2.svg" alt="Visual Studio" class="img-responsive" />
|
||||
<div class="carousel-caption" role="option">
|
||||
<p>
|
||||
There are powerful new features in Visual Studio for building modern web apps.
|
||||
<a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkID=525030&clcid=0x409">
|
||||
Learn More
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<img src="~/images/banner3.svg" alt="Package Management" class="img-responsive" />
|
||||
<div class="carousel-caption" role="option">
|
||||
<p>
|
||||
Bring in libraries from NuGet, Bower, and npm, and automate tasks using Grunt or Gulp.
|
||||
<a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkID=525029&clcid=0x409">
|
||||
Learn More
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<img src="~/images/banner4.svg" alt="Microsoft Azure" class="img-responsive" />
|
||||
<div class="carousel-caption" role="option">
|
||||
<p>
|
||||
Learn how Microsoft's Azure cloud platform allows you to build, deploy, and scale web apps.
|
||||
<a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkID=525027&clcid=0x409">
|
||||
Learn More
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
|
||||
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
|
||||
<span class="sr-only">Previous</span>
|
||||
</a>
|
||||
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
|
||||
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
|
||||
<span class="sr-only">Next</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<h1>Application uses</h1>
|
||||
<ul>
|
||||
<li>Sample pages using ASP.NET Core Razor Pages</li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=518004">Bower</a> for managing client-side libraries</li>
|
||||
<li>Theming using <a href="https://go.microsoft.com/fwlink/?LinkID=398939">Bootstrap</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h1>How to</h1>
|
||||
<ul>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?linkid=852130">Working with Razor Pages.</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699315">Manage User Secrets using Secret Manager.</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699316">Use logging to log a message.</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699317">Add packages using NuGet.</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699318">Add client packages using Bower.</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699319">Target development, staging or production environment.</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h1>Overview</h1>
|
||||
<ul>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=518008">Conceptual overview of what is ASP.NET Core</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699320">Fundamentals of ASP.NET Core such as Startup and middleware.</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=398602">Working with Data</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkId=398603">Security</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkID=699321">Client side development</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkID=699322">Develop on different platforms</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkID=699323">Read more on the documentation site</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h1>Run & Deploy</h1>
|
||||
<ul>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkID=517851">Run your app</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkID=517853">Run tools such as EF migrations and more</a></li>
|
||||
<li><a href="https://go.microsoft.com/fwlink/?LinkID=398609">Publish to Microsoft Azure App Service</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace AuthorizationSample.Pages
|
||||
{
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
@page
|
||||
@namespace AuthorizationSample.Pages.Private
|
||||
@model PrivatePage1Model
|
||||
@{
|
||||
ViewData["Title"] = "Private Folder: Private Page 1";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>@Model.Message</h2>
|
||||
|
||||
<p>
|
||||
This page requires authorization by convention:
|
||||
<code>
|
||||
options.Conventions.AuthorizeFolder("/Private");
|
||||
</code>
|
||||
</p>
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace AuthorizationSample.Pages.Private
|
||||
{
|
||||
public class PrivatePage1Model : PageModel
|
||||
{
|
||||
public string Message { get; private set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Message = "A private page inside the Private folder.";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
@page
|
||||
@namespace AuthorizationSample.Pages.Private
|
||||
@model PrivatePage2Model
|
||||
@{
|
||||
ViewData["Title"] = "Private Folder: Private Page 2";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>@Model.Message</h2>
|
||||
|
||||
<p>
|
||||
This page requires authorization by convention:
|
||||
<code>
|
||||
options.Conventions.AuthorizeFolder("/Private");
|
||||
</code>
|
||||
</p>
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace AuthorizationSample.Pages.Private
|
||||
{
|
||||
public class PrivatePage2Model : PageModel
|
||||
{
|
||||
public string Message { get; private set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Message = "A private page inside the Private folder.";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
@page
|
||||
@namespace AuthorizationSample.Pages.Private
|
||||
@model PublicPageModel
|
||||
@{
|
||||
ViewData["Title"] = "Private Folder: Public Page";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>@Model.Message</h2>
|
||||
|
||||
<p>
|
||||
The <i>Private</i> folder requires authorization, but this page allows anonymous visitors by convention:
|
||||
<code>
|
||||
options.Conventions.AllowAnonymousToPage("/Private/PublicPage");
|
||||
</code>
|
||||
</p>
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace AuthorizationSample.Pages.Private
|
||||
{
|
||||
public class PublicPageModel : PageModel
|
||||
{
|
||||
public string Message { get; private set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Message = "A public page inside the Private folder.";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
@page
|
||||
@namespace AuthorizationSample.Pages.Private.Public
|
||||
@model PublicPageModel
|
||||
@{
|
||||
ViewData["Title"] = "Private Folder: PublicPages Folder: Public Page";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>@Model.Message</h2>
|
||||
|
||||
<p>
|
||||
The <i>Private</i> folder requires authorization, but this page is inside a <i>PublicPages</i> folder that allows anonymous visitors by convention:
|
||||
<code>
|
||||
options.Conventions.AllowAnonymousToFolder("/Private/PublicPages");
|
||||
</code>
|
||||
</p>
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace AuthorizationSample.Pages.Private.Public
|
||||
{
|
||||
public class PublicPageModel : PageModel
|
||||
{
|
||||
public string Message { get; private set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Message = "A public page inside a PublicPages folder inside the Private folder.";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
@using AuthorizationSample.Pages.Private.Public
|
|
@ -0,0 +1 @@
|
|||
@using AuthorizationSample.Pages.Private
|
|
@ -0,0 +1,82 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - AuthorizationSample</title>
|
||||
<environment include="Development">
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
|
||||
<link rel="stylesheet" href="~/css/site.css" />
|
||||
</environment>
|
||||
<environment exclude="Development">
|
||||
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
|
||||
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
|
||||
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
|
||||
crossorigin="anonymous"
|
||||
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u">
|
||||
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true">
|
||||
</environment>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a asp-page="/Index" class="navbar-brand">AuthorizationSample</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a asp-page="/Index">Home</a></li>
|
||||
<li>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Pages<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu multi-level">
|
||||
<li><a asp-page="/Private/PrivatePage1">Private Folder: Private Page 1 (Authentication Required)</a></li>
|
||||
<li><a asp-page="/Private/PrivatePage2">Private Folder: Private Page 2 (Authentication Required)</a></li>
|
||||
<li><a asp-page="/Private/PublicPage">Private Folder: Public Page</a></li>
|
||||
<li><a asp-page="/Private/PublicPages/PublicPage">Private Folder: Public Folder: Public Page</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a asp-page="/About">About</a></li>
|
||||
<li><a asp-page="/Contact">Contact (Authentication Required)</a></li>
|
||||
</ul>
|
||||
@await Html.PartialAsync("_LoginPartial")
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container body-content">
|
||||
@RenderBody()
|
||||
<hr>
|
||||
<footer>
|
||||
<p>©@System.DateTime.Now.Year - AuthorizationSample</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<environment include="Development">
|
||||
<script src="~/lib/jquery/dist/jquery.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
</environment>
|
||||
<environment exclude="Development">
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
|
||||
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
|
||||
asp-fallback-test="window.jQuery"
|
||||
crossorigin="anonymous"
|
||||
integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk">
|
||||
</script>
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
|
||||
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
|
||||
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
|
||||
crossorigin="anonymous"
|
||||
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
|
||||
</script>
|
||||
<script src="~/js/site.min.js" asp-append-version="true"></script>
|
||||
</environment>
|
||||
|
||||
@RenderSection("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<form asp-controller="Account" asp-action="Logout" method="post" id="logoutForm" class="navbar-right">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<button type="submit" class="btn btn-link navbar-btn navbar-link">Log out</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a asp-page="/Account/Login">Log in</a></li>
|
||||
</ul>
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<environment include="Development">
|
||||
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
||||
</environment>
|
||||
<environment exclude="Development">
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"
|
||||
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
|
||||
asp-fallback-test="window.jQuery && window.jQuery.validator"
|
||||
crossorigin="anonymous"
|
||||
integrity="sha384-Fnqn3nxp3506LP/7Y3j/25BlWeA3PXTyT1l78LjECcPaKCV12TsZP7yyMxOe/G/k">
|
||||
</script>
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"
|
||||
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
|
||||
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
|
||||
crossorigin="anonymous"
|
||||
integrity="sha384-JrXK+k53HACyavUKOsL+NkmSesD2P+73eDMrbTtTk0h4RmOF8hF8apPlkp26JlyH">
|
||||
</script>
|
||||
</environment>
|
|
@ -0,0 +1,4 @@
|
|||
@using AuthorizationSample
|
||||
@using AuthorizationSample.Data
|
||||
@namespace AuthorizationSample.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
|
@ -0,0 +1,3 @@
|
|||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AuthorizationSample
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BuildWebHost(args).Run();
|
||||
}
|
||||
|
||||
public static IWebHost BuildWebHost(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
# ASP.NET Core Authorization Sample
|
||||
|
||||
This sample illustrates use of Razor Pages authorization by conventions. This sample demonstrates the features described in the [Razor Pages authorization conventions](https://docs.microsoft.com/aspnet/core/security/authorization/razor-pages-authorization) topic.
|
||||
|
||||
User authorization in this sample uses the cookie authentication features described in the [Using Cookie Authentication without ASP.NET Core Identity](https://docs.microsoft.com/aspnet/core/security/authentication/cookie) topic. For information on using ASP.NET Core Identity, see the topics in the [Authentication](https://docs.microsoft.com/aspnet/core/security/authentication/index) section of the documentation.
|
||||
|
||||
When running the sample, use the email address **maria.rodriguez@contoso.com** to authenticate the user.
|
||||
|
||||
## Examples in this sample
|
||||
|
||||
| Feature | Description |
|
||||
| ------- | ----------- |
|
||||
| [AuthorizePage](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.authorizepage) | Adds an [AuthorizeFilter](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.authorization.authorizefilter) to the page with the specified path. |
|
||||
| [AuthorizeFolder](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.authorizefolder) | Adds an [AuthorizeFilter](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.authorization.authorizefilter) to all of the pages in a folder with the specified path. |
|
||||
| [AllowAnonymousToPage](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.allowanonymoustopage) | Adds an [AllowAnonymousFilter](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.authorization.allowanonymousfilter) to a page with the specified path. |
|
||||
| [AllowAnonymousToFolder](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.pageconventioncollectionextensions.allowanonymoustofolder) | Adds an [AllowAnonymousFilter](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.authorization.allowanonymousfilter) to all of the pages in a folder with the specified path. |
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace AuthorizationSample
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
#region snippet1
|
||||
services.AddMvc()
|
||||
.AddRazorPagesOptions(options =>
|
||||
{
|
||||
options.Conventions.AuthorizePage("/Contact");
|
||||
options.Conventions.AuthorizeFolder("/Private");
|
||||
options.Conventions.AllowAnonymousToPage("/Private/PublicPage");
|
||||
options.Conventions.AllowAnonymousToFolder("/Private/PublicPages");
|
||||
});
|
||||
#endregion
|
||||
|
||||
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddCookie();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseMvc();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "asp.net",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"bootstrap": "3.3.7",
|
||||
"jquery": "2.2.0",
|
||||
"jquery-validation": "1.14.0",
|
||||
"jquery-validation-unobtrusive": "3.2.6"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Configure bundling and minification for the project.
|
||||
// More info at https://go.microsoft.com/fwlink/?LinkId=808241
|
||||
[
|
||||
{
|
||||
"outputFileName": "wwwroot/css/site.min.css",
|
||||
// An array of relative input file paths. Globbing patterns supported
|
||||
"inputFiles": [
|
||||
"wwwroot/css/site.css"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outputFileName": "wwwroot/js/site.min.js",
|
||||
"inputFiles": [
|
||||
"wwwroot/js/site.js"
|
||||
],
|
||||
// Optionally specify minification options
|
||||
"minify": {
|
||||
"enabled": true,
|
||||
"renameLocals": true
|
||||
},
|
||||
// Optionally generate .map file
|
||||
"sourceMap": false
|
||||
}
|
||||
]
|
|
@ -0,0 +1,48 @@
|
|||
body {
|
||||
padding-top: 50px;
|
||||
padding-bottom: 20px
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px
|
||||
}
|
||||
|
||||
/* Wrapping element */
|
||||
/* Set some basic padding to keep content from hitting the edges */
|
||||
.body-content {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px
|
||||
}
|
||||
|
||||
/* Carousel */
|
||||
.carousel-caption p {
|
||||
font-size: 20px;
|
||||
line-height: 1.4
|
||||
}
|
||||
|
||||
/* Make .svg files in the carousel display properly in older browsers */
|
||||
.carousel-inner .item img[src$=".svg"] {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
/* QR code generator */
|
||||
#qrCode {
|
||||
margin: 15px
|
||||
}
|
||||
|
||||
.welcome {
|
||||
font-size:2.4em;
|
||||
margin-top:6px
|
||||
}
|
||||
|
||||
/* Hide/rearrange for smaller screens */
|
||||
@media screen and (max-width: 767px) {
|
||||
/* Hide captions */
|
||||
.carousel-caption {
|
||||
display: none
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
body{padding-top:50px;padding-bottom:20px}h1{font-size:30px}h2{font-size:24px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}.welcome{font-size:2.4em;margin-top:6px}@media screen and (max-width:767px){.carousel-caption{display:none}}
|
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.5 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.2 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1 @@
|
|||
// Write your Javascript code.
|
|
@ -228,6 +228,7 @@
|
|||
## [Authorization](xref:security/authorization/index)
|
||||
### [Introduction](xref:security/authorization/introduction)
|
||||
### [Create an app with user data protected by authorization](xref:security/authorization/secure-data)
|
||||
### [Razor Pages authorization](xref:security/authorization/razor-pages-authorization)
|
||||
### [Simple Authorization](xref:security/authorization/simple)
|
||||
### [Role based Authorization](xref:security/authorization/roles)
|
||||
### [Claims-Based Authorization](xref:security/authorization/claims)
|
||||
|
|
Loading…
Reference in New Issue