> For the complete documentation index, see [llms.txt](/docs/llms.txt)

# .NET | FusionAuth Docs

Quickstart integration of .NET web application with FusionAuth.

# .NET

In this quickstart, you are going to build an application with .NET and integrate it with FusionAuth. You'll be building it for [ChangeBank](https://www.youtube.com/watch?v=CXDxNCzUspM), a global leader in converting dollars into coins. It'll have areas reserved for users who have logged in as well as public facing sections.

The Docker Compose file and source code for a complete application are available at [https://github.com/FusionAuth/fusionauth-quickstart-dotnet-web](https://github.com/FusionAuth/fusionauth-quickstart-dotnet-web).

## Prerequisites[#](#prerequisites)

*   [.NET](https://dotnet.microsoft.com/en-us/download): This is the framework needed to run .NET applications. This app has been tested with .NET 7.0.7. This example should work with future compatible versions of .NET.
*   [Docker](https://www.docker.com): The quickest way to stand up FusionAuth. (There are [other ways](/docs/get-started/download-and-install)).
*   [Visual Studio Code](https://code.visualstudio.com/download): The editor for making changes to code. (You can use other editors as well.)

## General Architecture[#](#general-architecture)

Here's a typical application login flow before FusionAuth:

```mermaid
sequenceDiagram
    participant User
    participant App as Application

    User ->> App : View Homepage
    User ->> App : Click Login Link
    App ->> User : Show Login Form
    User ->> App : Fill Out and Submit Login Form
    App ->> App : Authenticates User
    App ->> User : Display User's Account or Other Info
```

Request flow during login before FusionAuth

And here's the same application login flow after introducing FusionAuth:

```mermaid
sequenceDiagram
    participant User
    participant App as Application
    participant FusionAuth

    User ->> App : View Homepage
    User ->> App : Click Login Link (to FusionAuth)
    User ->> FusionAuth : View Login Form
    FusionAuth ->> User : Show Login Form
    User ->> FusionAuth : Fill Out and Submit Login Form
    FusionAuth ->> FusionAuth : Authenticates User
    FusionAuth ->> User: Go to Redirect URI
    User ->> App: Request the Redirect URI
    App ->> FusionAuth : Is User Authenticated?
    FusionAuth ->> App : User is Authenticated
    App ->> User : Display User's Account or Other Info
```

Request flow during login after FusionAuth

In general, you are introducing FusionAuth in order to normalize and consolidate user data. This helps make sure it is consistent and up-to-date as well as offloading your login security and functionality to FusionAuth.

## Getting Started[#](#getting-started)

In this section, you'll get FusionAuth up and running and use .NET to create a new application.

### Clone the Code[#](#clone-the-code)

First off, grab the code from the repository and change into that directory.

```
git clone https://github.com/FusionAuth/fusionauth-quickstart-dotnet-web.git
cd fusionauth-quickstart-dotnet-web
```

### Run FusionAuth via Docker[#](#run-fusionauth-via-docker)

You'll find a Docker Compose file (`docker-compose.yml`) and an environment variables configuration file (`.env`) in the root directory of the repo.

Assuming you have Docker installed, you can stand up FusionAuth on your machine with the following.

```
docker compose up -d
```

Here you are using a bootstrapping feature of FusionAuth called [Kickstart](/docs/get-started/download-and-install/development/kickstart). When FusionAuth comes up for the first time, it will look at the `kickstart/kickstart.json` file and configure FusionAuth to your specified state.

If you ever want to reset the FusionAuth application, you need to delete the volumes created by Docker Compose by executing `docker compose down -v`, then re-run `docker compose up -d`.

FusionAuth will be initially configured with these settings:

*   Your client Id is `e9fdb985-9173-4e01-9d73-ac2d60d1dc8e`.
*   Your client secret is `super-secret-secret-that-should-be-regenerated-for-production`.
*   Your example username is `richard@example.com` and the password is `password`.
*   Your admin username is `admin@example.com` and the password is `password`.
*   The base URL of FusionAuth is `http://localhost:9011/`.

You can log in to the [FusionAuth admin UI](http://localhost:9011/admin) and look around if you want to, but with Docker and Kickstart, everything will already be configured correctly.

If you want to see where the FusionAuth values came from, they can be found in the [FusionAuth app](http://localhost:9011/admin). The tenant Id is found on the Tenants page. To see the Client Id and Client Secret, go to the Applications page and click the `View` icon under the actions for the ChangeBank application. You'll find the Client Id and Client Secret values in the `OAuth configuration` section.

The `.env` file contains passwords. In a real application, always add this file to your `.gitignore` file and never commit secrets to version control.

### Create your .NET 7 Application[#](#create-your-net-7-application)

Now you are going to create a .NET application using C#. While this section builds a simple .NET web application, you can use the same configuration to integrate your existing .NET application with FusionAuth.

You'll use [Razor Pages](https://learn.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-7.0&tabs=visual-studio) and .NET 7. This application will display common information to all users. There will also be a secured area, only available to an authenticated user. The [full source code](https://github.com/FusionAuth/fusionauth-quickstart-dotnet-web) is available if you want to download it and take a look.

If you simply want to run the application , there is a completed version in the 'complete-application' directory. You can use the following commands to get it up and running if you do not want to create your own.

```
cd complete-application
dotnet run
```

Then view the website at the following location `http://localhost:5000`.

To get started building your own application, open your command-line shell (i.e. Terminal on a Mac or PowerShell on Windows) and navigate to the directory of your choice. Then create a new web application using the `dotnet` CLI and go to that directory:

```
dotnet new webapp -o your-application
cd your-application
```

Creating a new application in Visual Studio will work as well. You will use the command line here for the simplicity of the example.

Once the application has been created, you can start up the web application using the following command in.

```
dotnet run
```

The output should look similar to the following.

```
Building...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5146
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/Shared/your-application
```

The application will be accessible in your browser at `http://localhost:{PortNumber}` where the port number will be displayed in your shell window, i.e `http://localhost:5146`.

Press Control+C in the terminal window to stop the application from running.

## Authentication[#](#authentication)

It's always smart to leverage existing libraries as they are likely to be more secure and better handle edge cases. You're going to add an `OpenIdConnect` library to the application. In your terminal window, make sure you're in the root directory for your application and run the command below to add the library.

```
dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect
```

This application will contain two pages that require authorization. The `Account` page will display the user's balance and the `MakeChange` page will make change for the user. To protect these pages you can use the [Authorize filter attribute](https://docs.microsoft.com/en-us/aspnet/core/razor-pages/filter?view=aspnetcore-3.1#authorize-filter-attribute) in the [page model](https://learn.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-7.0&tabs=visual-studio).

Create `Account.cshtml` and `Account.cshtml.cs` in the `Pages` directory. To protect the "Account" page you can use the [Authorize filter attribute](https://learn.microsoft.com/en-us/aspnet/core/razor-pages/filter?view=aspnetcore-7.0#authorize-filter-attribute) in the [page model](https://learn.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-7.0&tabs=visual-studio).

Copy the following code into `Account.cshtml.cs`:

```
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Authorization;

namespace Pages;

[Authorize]
public class AccountsModel : PageModel
{
    private readonly ILogger<PrivacyModel> _logger;

    public AccountsModel(ILogger<PrivacyModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
      
    }
}
```

Copy the following code into `Account.cshtml`:

```
@page
@model AccountsModel
@{
    ViewData["Title"] = "Account";
}

<div style="flex: 1;">
<!-- Application page -->
    <div class="column-container">
        <div class="app-container">
            <h3>Your balance</h3>
            <div class="balance">$100.00</div>
        </div>
    </div>
</div>
```

Create `MakeChange.cshtml` and `MakeChange.cshtml.cs` in the `Pages` directory. You will use the same [Authorize filter attribute](https://docs.microsoft.com/en-us/aspnet/core/razor-pages/filter?view=aspnetcore-3.1#authorize-filter-attribute) to secure the page.

Copy the following code into `MakeChange.cshtml.cs`:

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Pages
{
    [Authorize]
    public class MakeChangeModel : PageModel
    {
        public string Message { get; private set; } = "";
        public bool Error { get; private set; } = false;
        public string Amount { get; set; } = "0.00";

        public void OnGet()
        {
        }

        public void OnPost(string amount)
        {
            MakeChange(amount);
        }

        public void MakeChange(string amount)
        {
            try
            {
                Amount = amount;

                decimal remainingamount = Convert.ToDecimal(amount);

                Message = "We can make change for";

                var coins = new[] { // ordered
                    new { name = "quarters", nominal   = 0.25m },
                    new { name = "dimes", nominal      = 0.10m },
                    new { name = "nickels", nominal    = 0.05m },
                    new { name = "pennies", nominal   = 0.01m }
                };

                foreach (var coin in coins)
                {
                    int count = (int)(remainingamount / coin.nominal);
                    remainingamount -= count * coin.nominal;

                    Message += $" {count} {coin.name}";
                }

                Message += "!";

            }
            catch (Exception ex)
            {
                Message = @$"There was a problem converting the amount submitted. {ex.Message}";
            }
        }
    }
}
```

Copy the following code into `MakeChange.cshtml`:

```
@page
@model Pages.MakeChangeModel
@{
}

<div style="flex: 1;">
    <div class="column-container">
        <div class="app-container change-container">
            <h3>We Make Change</h3>
            <div class="change-message">
                @Model.Message
            </div>
            <form method="post">
                <div class="h-row">
                    <div class="change-label">Amount in USD: $</div>
                    <input class="change-input" name="amount" value="@Model.Amount"  />
                    <input class="change-submit" type="submit" value="Make Change" />
                </div>
            </form>
        </div>
    </div>
</div>
```

You will need a way to allow the user to logout of the application as well. You'll build the logout functionality in the `Logout` page. This will:

*   Remove the session cookie.
*   Redirect to the FusionAuth OAuth logout endpoint.

FusionAuth will then destroy its session and redirect the user back to the app's configured logout URL.

Add the following file to the `Pages` directory and call it `Logout.cshtml.cs`:

```
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Pages
{
    public class LogoutModel : PageModel
    {
        private readonly ILogger<LogoutModel> _logger;
        private readonly IConfiguration _configuration;

        public LogoutModel(ILogger<LogoutModel> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
        }

        public IActionResult OnGet()
        {
            SignOut("cookie", "oidc");
            var host = _configuration["FusionAuthSettings:Authority"];
            var cookieName = _configuration["FusionAuthSettings:CookieName"];

            var clientId = _configuration["FusionAuthSettings:ClientId"];
            var url = host + "/oauth2/logout?client_id=" + clientId;
            Response.Cookies.Delete(cookieName);
            return Redirect(url);
        }
    }
}
```

`OnGet` is the important method. Here you sign out using a method of the authentication library, delete the JWT cookie, and send the user to the FusionAuth OAuth logout endpoint.

Now add `Logout.cshtml` to the `Pages` directory. No content is necessary. Just declare the page and model as shown below.

```
@page
@model LogoutModel
@{
}
```

### Application Configuration[#](#application-configuration)

You will need to do a little plumbing to ensure things like having the correct settings and configuring the cookies properly to work over HTTP for the sample application.

#### App Settings[#](#app-settings)

Replace the text in the `appsettings.json` file with the text below. The important part here is that we are adding the `FusionAuthSettings` section so that the code above will run with the correct configuration settings. `Authority` is just the location of the user identity server, in this case, FusionAuth. `Urls` ensures the application runs on the specified port, 5000.

```
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Urls": "http://localhost:5000",
  "FusionAuthSettings": {
    "Authority": "http://localhost:9011",
    "CookieName": "mycookie",
    "ClientId": "e9fdb985-9173-4e01-9d73-ac2d60d1dc8e",
    "ClientSecret": "super-secret-secret-that-should-be-regenerated-for-production"
  }
}
```

#### Program Settings[#](#program-settings)

Replace the text in the `Program.cs` file. Since you are using unsecured cookies, you need to allow this in the configuration. Safari and Chrome require different settings and that is reflected in the code below.

```
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);

var app = builder.Build();

// Add this before any other middleware that might write cookies
app.UseCookiePolicy();

app.UseCookiePolicy(new CookiePolicyOptions
{
    Secure = CookieSecurePolicy.Always,
    //safari does not work if this is not set here and chrome works as well
    MinimumSameSitePolicy = SameSiteMode.Unspecified
});

startup.Configure(app, builder.Environment);
app.Run();
```

#### Startup Settings[#](#startup-settings)

Add the `Startup.cs` file in the root directory of the application. This sets the correct port and manages unsecured cookies for the browsers. The `CheckSameSite` and `DisallowsSameSiteNone` functions help manage the cookie settings for the different browsers.

```
using System;
using System.ComponentModel.DataAnnotations;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Logging;


public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
        services.AddRazorPages();

        services.AddAuthentication(options =>
            {
                options.DefaultScheme = "cookie";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("cookie", options =>
            {
                options.Cookie.Name = Configuration["FusionAuthSettings:CookieName"];
                options.Cookie.SameSite = SameSiteMode.None;

            })
            .AddOpenIdConnect("oidc", options =>
            {
                options.Authority = Configuration["FusionAuthSettings:Authority"];
                options.ClientId = Configuration["FusionAuthSettings:ClientId"];
                options.ClientSecret = Configuration["FusionAuthSettings:ClientSecret"];
                options.ResponseType = "code";
                options.RequireHttpsMetadata = false;
                options.Scope.Add("email");
            });

        services.Configure<CookiePolicyOptions>(options =>
        {
            options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
            options.OnAppendCookie = cookieContext =>
                CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
            options.OnDeleteCookie = cookieContext =>
                CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); });
        IdentityModelEventSource.ShowPII = true;
    }

    private void CheckSameSite(HttpContext httpContext, CookieOptions options)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = SameSiteMode.Unspecified;
            options.Secure = false;
        }
    }

    //  Read comments in https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1
    public bool DisallowsSameSiteNone(string userAgent)
    {
        // Check if a null or empty string has been passed in, since this
        // will cause further interrogation of the useragent to fail.
        if (String.IsNullOrWhiteSpace(userAgent))
            return false;

        // Cover all iOS based browsers here. This includes:
        // - Safari on iOS 12 for iPhone, iPod Touch, iPad
        // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
        // - Chrome on iOS 12 for iPhone, iPod Touch, iPad
        // All of which are broken by SameSite=None, because they use the iOS networking
        // stack.
        if (userAgent.Contains("CPU iPhone OS 12") ||
            userAgent.Contains("iPad; CPU OS 12"))
        {
            return true;
        }

        // Cover Mac OS X based browsers that use the Mac OS networking stack. 
        // This includes:
        // - Safari on Mac OS X.
        // This does not include:
        // - Chrome on Mac OS X
        // Because they do not use the Mac OS networking stack.
        if (userAgent.Contains("Macintosh; Intel Mac OS X") &&
            userAgent.Contains("Version/") && userAgent.Contains("Safari"))
        {
            return true;
        }

        // Cover Chrome 50-69, because some versions are broken by SameSite=None, 
        // and none in this range require it.
        // Note: this covers some pre-Chromium Edge versions, 
        // but pre-Chromium Edge does not require SameSite=None.
        if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
        {
            return true;
        }

        return false;
    }
}
```

There are a few settings applied to simplify the sample application. In a production application, do not allow cookies over `http` or keep the `ClientSecret` in a .json file for production applications. Get the `ClientSecret` from an environmental variable that has been set on a production machine or some other secure way.

#### Navigation[#](#navigation)

This application will display different menu items for authenticated users and non authenticated users. To add this type of navigation update `/Pages/Shared/_Layout.cshtml` with the following:

```
<html>
<head>
  <meta charset="utf-8" />
  <title>FusionAuth OpenID .Net Web Example</title>
  <link rel="stylesheet" href="~/css/changebank.css">
</head>
<body>
    <div id="page-container">
        @if (User.Identity != null && User.Identity.IsAuthenticated == true)
        {
            <div id="page-header">
                <div id="logo-header">
                    <img src="https://fusionauth.io/cdn/samplethemes/changebank/changebank.svg" />
                    <div class="h-row">
                        <p class="header-email">@User.Claims.Where(x => x.Type == "email").First().Value</p>
                        <a class="button-lg" href="/Logout">Logout</a>
                    </div>
                </div>
                <div id="menu-bar" class="menu-bar">
                    <a class="menu-link" " href="/makechange">Make Change</a>
                    <a class="menu-link" href="/account">Account</a>
                </div>
            </div>
        }
        else
        {
            <div id="page-header">
                <div id="logo-header">
                    <img src="https://fusionauth.io/cdn/samplethemes/changebank/changebank.svg" />
                    <a class="button-lg" href="/Account">Login</a>
                </div>

                <div id="menu-bar" class="menu-bar">
                    <a class="menu-link">About</a>
                    <a class="menu-link">Services</a>
                    <a class="menu-link">Products</a>
                    <a class="menu-link" style="text-decoration-line: underline;">Home</a>
                </div>
            </div>
        }
        @RenderBody()
    </div>
</body>
</html>
```

#### Homepage[#](#homepage)

Update the default page.

Copy the following code into `Pages/Index.cshtml.cs`:

```
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Pages;

public class IndexModel : PageModel
{
    private readonly ILogger<IndexModel> _logger;

    public IndexModel(ILogger<IndexModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
       
    }
}
```

Copy the following code into `Index.cshtml` and notice the logic to show a different view to users that have not logged in.

```
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}


<div style="flex: 1;">
    <div class="column-container">
        <div class="content-container">
            <div style="margin-bottom: 100px;">
                <h1>Welcome to Changebank</h1>
                @if (User.Identity != null && User.Identity.IsAuthenticated == true)
                {
                    <p>To get started, please select from the menu.</p>
                }
                else
                {
                    <p>To get started, <a href="/Account">log in or create a new account</a>.</p>
                }
            </div>
        </div>
        <div style="flex: 0;">
            <img src="~/img/money.jpg" style="max-width: 800px;"/>
        </div>
    </div>
</div>
```

#### Styling[#](#styling)

To give the pages the look and feel of an application, apply the following styling and images.

Add the `changebank.css` file in the `wwwroot/css` directory of the application.

```
h1 {
  color: #096324;
}

h3 {
  color: #096324;
  margin-top: 20px;
  margin-bottom: 40px;
}

a {
  color: #096324;
}

p {
  font-size: 18px;
}

.header-email {
  color: #096324;
  margin-right: 20px;
}

.fine-print {
  font-size: 16px;
}

body {
  font-family: sans-serif;
  padding: 0px;
  margin: 0px;
}

.h-row {
  display: flex;
  align-items: center;
}

#page-container {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

#page-header {
  flex: 0;
  display: flex;
  flex-direction: column;
}

#logo-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
}

.menu-bar {
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  height: 35px;
  padding: 15px 50px 15px 30px;
  background-color: #096324;
  font-size: 20px;
}

.menu-link {
  font-weight: 600;
  color: #FFFFFF;
  margin-left: 40px;
}

.menu-link {
  font-weight: 600;
  color: #FFFFFF;
  margin-left: 40px;
}

.inactive {
  text-decoration-line: none;
}

.button-lg {
  width: 150px;
  height: 30px;
  background-color: #096324;
  color: #FFFFFF;
  font-size: 16px; 
  font-weight: 700; 
  border-radius: 10px; 
  text-align: center;
  padding-top: 10px;
  text-decoration-line: none;
}

.column-container {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}

.content-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 60px 20px 20px 40px;
}

.balance {
  font-size: 50px;
  font-weight: 800;
}

.change-label {
  font-size: 20px;
  margin-right: 5px;
}

.change-input {
  font-size: 20px;
  height: 40px;
  text-align: end;
  padding-right: 10px;
}

.change-submit {
  font-size: 15px;
  height: 40px;
  margin-left: 15px;
  border-radius: 5px;
}

.change-message {
  font-size: 20px;
  margin-bottom: 15px;
}

.error-message {
  font-size: 20px;
  color: #FF0000;
  margin-bottom: 15px;
}

.app-container {
  flex: 0;
  min-width: 440px;
  display: flex;
  flex-direction: column;
  margin-top: 40px;
  margin-left: 80px;
}

.change-container {
  flex: 1;
}
```

Copy the money image for the `Index` page. Run the following command in a terminal window. Make sure you are in the root directory for your application.

```
curl --create-dirs --output wwwroot/img/money.jpg https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-ruby-on-rails-web/main/complete-app/app/assets/images/money.jpg
```

#### A Little Clean Up[#](#a-little-clean-up)

Next we need to do a little clean up and make sure all the namespaces work together in the application.

Replace the text in the `Pages/_ViewImports.cshtml` file with the following code.

```
@namespace Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
```

Replace the text in the `Pages/Error.cshtml.cs` file with the following code.

```
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Pages;

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    private readonly ILogger<ErrorModel> _logger;

    public ErrorModel(ILogger<ErrorModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
    }
}
```

Replace the text in the `Pages/Privacy.cshtml.cs` file with the following code.

```
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Pages;

public class PrivacyModel : PageModel
{
    private readonly ILogger<PrivacyModel> _logger;

    public PrivacyModel(ILogger<PrivacyModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
    }
}
```

## Run The Application[#](#run-the-application)

You can start up the .NET application using this command in a terminal window from the root directory of your application:

```
dotnet run
```

You can now open up an incognito window and visit the app at [http://localhost:5000](http://localhost:5000). Log in with the user account you created when setting up FusionAuth, and you'll see the email of the user next to a logout button.

## Made it this far? Want a free t-shirt? We got ya.

Thank you for spending some time getting familiar with FusionAuth.

\*Offer only valid in the United States and Canada, while supplies last.

## Next Steps[#](#next-steps)

This quickstart is a great way to get a proof of concept up and running quickly, but to run your application in production, there are some things you're going to want to do.

### FusionAuth Customization

FusionAuth gives you the ability to customize just about everything to do with the user's experience and the integration of your application. This includes:

*   [Hosted pages](/docs/customize/look-and-feel/) such as login, registration, email verification, and many more.
*   [Email templates](/docs/customize/email-and-messages/email-templates).
*   [User data and custom claims in access token JWTs](/articles/tokens/jwt-components-explained).

### Security

*   [Microsoft.AspNetCore.Authentication.OpenIDConnect](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.OpenIdConnect) ASP.NET Core middleware that enables an application to support the OpenID Connect authentication workflow.
*   You may want to customize the [token expiration times and policies](/docs/lifecycle/authenticate-users/oauth/) in FusionAuth.
*   Choose [password rules](/docs/get-started/core-concepts/tenants#password) and a [hashing algorithm](/docs/reference/password-hashes) that meet your security needs.

### Tenant and Application Management

*   Model your application topology using [Applications](/docs/get-started/core-concepts/applications), [Roles](/docs/get-started/core-concepts/roles), [Groups](/docs/get-started/core-concepts/groups), [Entities](/docs/get-started/core-concepts/groups), and more.
*   Set up [MFA](/docs/lifecycle/authenticate-users/multi-factor-authentication), [Social login](/docs/lifecycle/authenticate-users/identity-providers/), or [SAML](/docs/lifecycle/authenticate-users/identity-providers/overview-samlv2) integrations.
*   Integrate with external systems using [Webhooks](/docs/extend/events-and-webhooks/), [SCIM](/docs/lifecycle/migrate-users/scim), and [Lambdas](/docs/extend/code/lambdas/).

## Troubleshooting[#](#troubleshooting)

If you run into an issue with cookies on Safari or other browsers, you might need to run the .NET application under SSL.

You can follow [this guide](https://learn.microsoft.com/en-us/dotnet/core/additional-tools/self-signed-certificates-guide) to install development SSL certificates for your .NET environment.

Alternatively, you can run the project using [Visual Studio](https://visualstudio.microsoft.com), which will run the project using SSL.

If you do this, make sure to update the `Authorized Redirect URL` to reflect the `https` protocol. Also note that the project will probably run on a different port when using SSL, so you must update that as well. To do so, log into the administrative user interface, navigate to Applications , then click the Edit button on your application and navigate to the OAuth tab. You can have more than one URL.

If you change the name of the application from 'your-application' to some other name, you will need to make sure the correct namespace is used in all of your code.

There are [multiple ways of deploying an application](https://docs.microsoft.com/en-us/dotnet/core/deploying/), but publishing ensures your deployment process is repeatable. You can use the [RID catalog](https://learn.microsoft.com/en-us/dotnet/core/rid-catalog) to build different versions of this application for different platforms.

If you have trouble running the application, please verify all the files are updated as instructed. The directory for the `your-application` tree should look like this:

```
└── your-application
    ├── appsettings.Development.json
    ├── appsettings.json
    ├── bin/
        ├── ...
    ├── obj/
        ├── ...
    ├── Pages/
    │   ├── _ViewImports.cshtml
    │   ├── _ViewStart.cshtml
    |   |── Account.cshtml
    |   |── Accuont.cshtml.cs
    │   ├── Error.cshtml
    │   ├── Error.cshtml.cs
    │   ├── Index.cshtml
    │   ├── Index.cshtml.cs
    │   ├── Logout.cshtml
    │   ├── Logout.cshtml.cs
    |   |── MakeChange.cshtml
    |   |── MakeChange.cshtml.cs
    │   ├── Privacy.cshtml
    │   ├── Privacy.cshtml.cs
    │   └── Shared/
    │       ├── _Layout.cshtml
    │       ├── _Layout.cshtml.css
    │       └── _ValidateScriptsPartial.cshtml
    ├── Program.cs
    ├── Properties/
        ├── ...
    ├── Startup.cs
    └── wwwroot/
        ├── ...
```

The full code is available [here](https://github.com/FusionAuth/fusionauth-quickstart-dotnet-web).