Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 75 additions & 10 deletions aspnetcore/security/authorization/limitingidentitybyscheme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@
title: Authorize with a specific scheme in ASP.NET Core
author: wadepickett
description: This article explains how to limit identity to a specific scheme when working with multiple authentication methods.
ai-usage: ai-assisted
monikerRange: '>= aspnetcore-3.1'
ms.author: wpickett
ms.date: 01/22/2026
ms.date: 01/30/2026
uid: security/authorization/limitingidentitybyscheme
---
# Authorize with a specific scheme in ASP.NET Core

:::moniker range=">= aspnetcore-6.0"
:::moniker range=">= aspnetcore-7.0"

For an introduction to authentication schemes in ASP.NET Core, see [Authentication scheme](xref:security/authentication/index#authentication-scheme).

In some scenarios, such as Single Page Applications (SPAs), it's common to use multiple authentication methods. For example, the app may use cookie-based authentication to log in and JWT bearer authentication for JavaScript requests. In some cases, the app may have multiple instances of an authentication handler. For example, two cookie handlers where one contains a basic identity and one is created when a multi-factor authentication (MFA) has been triggered. MFA may be triggered because the user requested an operation that requires extra security. For more information on enforcing MFA when a user requests a resource that requires MFA, see the GitHub issue [Protect section with MFA](https://github.com/dotnet/AspNetCore.Docs/issues/15791#issuecomment-580464195).

An authentication scheme is named when the authentication service is configured during authentication. For example:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/AuthScheme/Program.cs?name=snippet&highlight=5-15)]
[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/7.x/AuthScheme/Program.cs?name=snippet&highlight=5-15)]

In the preceding code, two authentication handlers have been added: one for cookies and one for bearer.

Expand All @@ -28,46 +29,110 @@ In the preceding code, two authentication handlers have been added: one for cook

At the point of authorization, the app indicates the handler to be used. Select the handler with which the app will authorize by passing a comma-delimited list of authentication schemes to `[Authorize]`. The [`[Authorize]`](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) attribute specifies the authentication scheme or schemes to use regardless of whether a default is configured. For example:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/AuthScheme/Controllers/MixedController.cs?name=snippet&highlight=8,11-13)]
[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/7.x/AuthScheme/Controllers/MixedController.cs?name=snippet&highlight=8,11-13)]

In the preceding example, both the cookie and bearer handlers run and have a chance to create and append an identity for the current user. By specifying a single scheme only, the corresponding handler runs:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/AuthScheme/Controllers/MixedController.cs?name=snippet2&highlight=1)]
[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/7.x/AuthScheme/Controllers/MixedController.cs?name=snippet2&highlight=1)]

In the preceding code, only the handler with the "Bearer" scheme runs. Any cookie-based identities are ignored.

## Selecting the scheme with policies

If you prefer to specify the desired schemes in [policy](xref:security/authorization/policies), you can set the <xref:Microsoft.Net.Http.Server.AuthenticationSchemes> collection when adding a policy:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/AuthScheme/Program.cs?name=snippet2&highlight=6-15)]
[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/7.x/AuthScheme/Program.cs?name=snippet2&highlight=4-6)]

In the preceding example, the "Over18" policy only runs against the identity created by the "Bearer" handler. Use the policy by setting the `[Authorize]` attribute's `Policy` property:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/AuthScheme/Controllers/RegistrationController.cs?name=snippet&highlight=5)]
[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/7.x/AuthScheme/Controllers/RegistrationController.cs?name=snippet&highlight=5)]

## Use multiple authentication schemes

Some apps may need to support multiple types of authentication. For example, your app might authenticate users from Azure Active Directory and from a users database. Another example is an app that authenticates users from both Active Directory Federation Services and Azure Active Directory B2C. In this case, the app should accept a JWT bearer token from several issuers.

Add all authentication schemes you'd like to accept. For example, the following code adds two JWT bearer authentication schemes with different issuers:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/AuthScheme/Program.cs?name=snippet_ma&highlight=7-18)]
[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/7.x/AuthScheme/Program.cs?name=snippet_ma&highlight=7-18)]

> [!NOTE]
> Only one JWT bearer authentication is registered with the default authentication scheme `JwtBearerDefaults.AuthenticationScheme`. Additional authentication has to be registered with a unique authentication scheme.

Update the default authorization policy to accept both authentication schemes. For example:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/AuthScheme/Program.cs?name=snippet_ma&highlight=20-29)]
[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/7.x/AuthScheme/Program.cs?name=snippet_ma&highlight=20-26)]

As the default authorization policy is overridden, it's possible to use the `[Authorize]` attribute in controllers. The controller then accepts requests with JWT issued by the first or second issuer.

For more information on using multiple authentication schemes, see [Multiple jwt authentication schemes can't validate signature key (`dotnet/aspnetcore` #26002)](https://github.com/dotnet/aspnetcore/issues/26002).

The following example uses [Azure Active Directory B2C](/azure/active-directory-b2c/overview) and another [Azure Active Directory](/azure/active-directory/authentication/overview-authentication) tenant:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/AuthScheme/Program.cs?name=snippet_ma2&highlight=9-49)]
[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/7.x/AuthScheme/Program.cs?name=snippet_ma2&highlight=9-49)]

In the preceding code, <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardDefaultSelector> is used to select a default scheme for the current request that authentication handlers should forward all authentication operations to by default. The default forwarding logic checks the most specific <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardAuthenticate>, <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardChallenge>, <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardForbid>, <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardSignIn>, and <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardSignOut> setting first, followed by checking the <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardDefaultSelector>, followed by <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardDefault>. The first non null result is used as the target scheme to forward to. For more information, see <xref:security/authentication/policyschemes>.

:::moniker-end

:::moniker range="= aspnetcore-6.0"

For an introduction to authentication schemes in ASP.NET Core, see [Authentication scheme](xref:security/authentication/index#authentication-scheme).

In some scenarios, such as Single Page Applications (SPAs), it's common to use multiple authentication methods. For example, the app may use cookie-based authentication to log in and JWT bearer authentication for JavaScript requests. In some cases, the app may have multiple instances of an authentication handler. For example, two cookie handlers where one contains a basic identity and one is created when a multi-factor authentication (MFA) has been triggered. MFA may be triggered because the user requested an operation that requires extra security. For more information on enforcing MFA when a user requests a resource that requires MFA, see the GitHub issue [Protect section with MFA](https://github.com/dotnet/AspNetCore.Docs/issues/15791#issuecomment-580464195).

An authentication scheme is named when the authentication service is configured during authentication. For example:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/6.x/AuthScheme/Program.cs?name=snippet&highlight=5-15)]

In the preceding code, two authentication handlers have been added: one for cookies and one for bearer.

>[!NOTE]
>Specifying the default scheme results in the `HttpContext.User` property being set to that identity. If that behavior isn't desired, disable it by invoking the parameterless form of `AddAuthentication`.

## Selecting the scheme with the Authorize attribute

At the point of authorization, the app indicates the handler to be used. Select the handler with which the app will authorize by passing a comma-delimited list of authentication schemes to `[Authorize]`. The [`[Authorize]`](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) attribute specifies the authentication scheme or schemes to use regardless of whether a default is configured. For example:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/6.x/AuthScheme/Controllers/MixedController.cs?name=snippet&highlight=8,11-13)]

In the preceding example, both the cookie and bearer handlers run and have a chance to create and append an identity for the current user. By specifying a single scheme only, the corresponding handler runs:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/6.x/AuthScheme/Controllers/MixedController.cs?name=snippet2&highlight=1)]

In the preceding code, only the handler with the "Bearer" scheme runs. Any cookie-based identities are ignored.

## Selecting the scheme with policies

If you prefer to specify the desired schemes in [policy](xref:security/authorization/policies), you can set the <xref:Microsoft.Net.Http.Server.AuthenticationSchemes> collection when adding a policy:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/6.x/AuthScheme/Program.cs?name=snippet2&highlight=6-15)]

In the preceding example, the "Over18" policy only runs against the identity created by the "Bearer" handler. Use the policy by setting the `[Authorize]` attribute's `Policy` property:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/6.x/AuthScheme/Controllers/RegistrationController.cs?name=snippet&highlight=5)]

## Use multiple authentication schemes

Some apps may need to support multiple types of authentication. For example, your app might authenticate users from Azure Active Directory and from a users database. Another example is an app that authenticates users from both Active Directory Federation Services and Azure Active Directory B2C. In this case, the app should accept a JWT bearer token from several issuers.

Add all authentication schemes you'd like to accept. For example, the following code adds two JWT bearer authentication schemes with different issuers:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/6.x/AuthScheme/Program.cs?name=snippet_ma&highlight=7-18)]

> [!NOTE]
> Only one JWT bearer authentication is registered with the default authentication scheme `JwtBearerDefaults.AuthenticationScheme`. Additional authentication has to be registered with a unique authentication scheme.

Update the default authorization policy to accept both authentication schemes. For example:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/6.x/AuthScheme/Program.cs?name=snippet_ma&highlight=20-29)]

As the default authorization policy is overridden, it's possible to use the `[Authorize]` attribute in controllers. The controller then accepts requests with JWT issued by the first or second issuer.

For more information on using multiple authentication schemes, see [Multiple jwt authentication schemes can't validate signature key (`dotnet/aspnetcore` #26002)](https://github.com/dotnet/aspnetcore/issues/26002).

The following example uses [Azure Active Directory B2C](/azure/active-directory-b2c/overview) and another [Azure Active Directory](/azure/active-directory/authentication/overview-authentication) tenant:

[!code-csharp[](~/security/authorization/limitingidentitybyscheme/samples/6.x/AuthScheme/Program.cs?name=snippet_ma2&highlight=9-49)]

In the preceding code, <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardDefaultSelector> is used to select a default scheme for the current request that authentication handlers should forward all authentication operations to by default. The default forwarding logic checks the most specific <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardAuthenticate>, <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardChallenge>, <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardForbid>, <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardSignIn>, and <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardSignOut> setting first, followed by checking the <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardDefaultSelector>, followed by <xref:Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions.ForwardDefault>. The first non null result is used as the target scheme to forward to. For more information, see <xref:security/authentication/policyschemes>.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
bin/
Bin/
obj/
Obj/

# Visual Studio 2015 cache/options directory
.vs/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config

# Windows Store app package directory
AppPackages/
BundleArtifacts/

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
orleans.codegen.cs

/node_modules

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe

# FAKE - F# Make
.fake/
Loading