diff --git a/Directory.Packages.props b/Directory.Packages.props
index d68aa1480..3daf2daaf 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -46,8 +46,8 @@
-
-
+
+
@@ -80,14 +80,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/samples/Balosar/Balosar.Server/Controllers/AuthorizationController.cs b/samples/Balosar/Balosar.Server/Controllers/AuthorizationController.cs
index fbbb9e17e..51444502b 100644
--- a/samples/Balosar/Balosar.Server/Controllers/AuthorizationController.cs
+++ b/samples/Balosar/Balosar.Server/Controllers/AuthorizationController.cs
@@ -57,7 +57,10 @@ public async Task Authorize()
// - If the user principal can't be extracted or the cookie is too old.
// - If prompt=login was specified by the client application.
// - If a max_age parameter was provided and the authentication cookie is not considered "fresh" enough.
- var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
+ //
+ // For scenarios where the default authentication handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ var result = await HttpContext.AuthenticateAsync();
if (result == null || !result.Succeeded || request.HasPrompt(Prompts.Login) ||
(request.MaxAge != null && result.Properties?.IssuedUtc != null &&
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)))
@@ -85,12 +88,12 @@ public async Task Authorize()
parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt)));
- return Challenge(
- authenticationSchemes: IdentityConstants.ApplicationScheme,
- properties: new AuthenticationProperties
- {
- RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
- });
+ // For scenarios where the default challenge handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ return Challenge(new AuthenticationProperties
+ {
+ RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
+ });
}
// Retrieve the profile of the logged in user.
diff --git a/samples/Dantooine/Dantooine.Server/Controllers/AuthorizationController.cs b/samples/Dantooine/Dantooine.Server/Controllers/AuthorizationController.cs
index 268e899c6..7bf7ca648 100644
--- a/samples/Dantooine/Dantooine.Server/Controllers/AuthorizationController.cs
+++ b/samples/Dantooine/Dantooine.Server/Controllers/AuthorizationController.cs
@@ -57,7 +57,10 @@ public async Task Authorize()
// - If the user principal can't be extracted or the cookie is too old.
// - If prompt=login was specified by the client application.
// - If a max_age parameter was provided and the authentication cookie is not considered "fresh" enough.
- var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
+ //
+ // For scenarios where the default authentication handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ var result = await HttpContext.AuthenticateAsync();
if (result == null || !result.Succeeded || request.HasPrompt(Prompts.Login) ||
(request.MaxAge != null && result.Properties?.IssuedUtc != null &&
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)))
@@ -85,12 +88,12 @@ public async Task Authorize()
parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt)));
- return Challenge(
- authenticationSchemes: IdentityConstants.ApplicationScheme,
- properties: new AuthenticationProperties
- {
- RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
- });
+ // For scenarios where the default challenge handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ return Challenge(new AuthenticationProperties
+ {
+ RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
+ });
}
// Retrieve the profile of the logged in user.
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/AuthenticationController.cs b/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/AuthenticationController.cs
index 5bc7c13ee..ade3f6f0d 100644
--- a/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/AuthenticationController.cs
+++ b/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/AuthenticationController.cs
@@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
+using OpenIddict.Abstractions;
using OpenIddict.Client.AspNetCore;
using static OpenIddict.Abstractions.OpenIddictConstants;
@@ -31,7 +32,10 @@ public async Task LogOut(string returnUrl)
{
// Retrieve the identity stored in the local authentication cookie. If it's not available,
// this indicate that the user is already logged out locally (or has not logged in yet).
- var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
+ //
+ // For scenarios where the default authentication handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ var result = await HttpContext.AuthenticateAsync();
if (result is not { Succeeded: true })
{
// Only allow local return URLs to prevent open redirect attacks.
@@ -39,7 +43,10 @@ public async Task LogOut(string returnUrl)
}
// Remove the local authentication cookie before triggering a redirection to the remote server.
- await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
+ //
+ // For scenarios where the default sign-out handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ await HttpContext.SignOutAsync();
var properties = new AuthenticationProperties(new Dictionary
{
@@ -93,47 +100,32 @@ public async Task LogInCallback()
// Such identities cannot be used as-is to build an authentication cookie in ASP.NET Core (as the
// antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but
// the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls.
- if (result.Principal.Identity is not ClaimsIdentity { IsAuthenticated: true })
+ if (result.Principal is not ClaimsPrincipal { Identity.IsAuthenticated: true })
{
throw new InvalidOperationException("The external authorization data cannot be used for authentication.");
}
// Build an identity based on the external claims and that will be used to create the authentication cookie.
- //
- // By default, all claims extracted during the authorization dance are available. The claims collection stored
- // in the cookie can be filtered out or mapped to different names depending the claim name or its issuer.
- var claims = new List(result.Principal.Claims
- .Select(claim => claim switch
- {
- // Map the standard "sub" and custom "id" claims to ClaimTypes.NameIdentifier, which is
- // the default claim type used by .NET and is required by the antiforgery components.
- { Type: Claims.Subject }
- => new Claim(ClaimTypes.NameIdentifier, claim.Value, claim.ValueType, claim.Issuer),
-
- // Map the standard "name" claim to ClaimTypes.Name.
- { Type: Claims.Name }
- => new Claim(ClaimTypes.Name, claim.Value, claim.ValueType, claim.Issuer),
-
- _ => claim
- })
- .Where(claim => claim switch
- {
- // Preserve the basic claims that are necessary for the application to work correctly.
- { Type: ClaimTypes.NameIdentifier or ClaimTypes.Name } => true,
+ var identity = new ClaimsIdentity(authenticationType: "ExternalLogin");
- // Don't preserve the other claims.
- _ => false
- }));
+ // By default, OpenIddict will automatically try to map the email/name and name identifier claims from
+ // their standard OpenID Connect or provider-specific equivalent, if available. If needed, additional
+ // claims can be resolved from the external identity and copied to the final authentication cookie.
+ identity.SetClaim(ClaimTypes.Email, result.Principal.GetClaim(ClaimTypes.Email))
+ .SetClaim(ClaimTypes.Name, result.Principal.GetClaim(ClaimTypes.Name))
+ .SetClaim(ClaimTypes.NameIdentifier, result.Principal.GetClaim(ClaimTypes.NameIdentifier));
- var identity = new ClaimsIdentity(claims,
- authenticationType: CookieAuthenticationDefaults.AuthenticationScheme,
- nameType: ClaimTypes.Name,
- roleType: ClaimTypes.Role);
+ // Preserve the registration identifier to be able to resolve it later.
+ identity.SetClaim(Claims.Private.RegistrationId, result.Principal.GetClaim(Claims.Private.RegistrationId));
// Build the authentication properties based on the properties that were added when the challenge was triggered.
- var properties = new AuthenticationProperties(result.Properties.Items);
+ var properties = new AuthenticationProperties(result.Properties.Items)
+ {
+ RedirectUri = result.Properties.RedirectUri ?? "/"
+ };
// If needed, the tokens returned by the authorization server can be stored in the authentication cookie.
+ //
// To make cookies less heavy, tokens that are not used are filtered out before creating the cookie.
properties.StoreTokens(result.Properties.GetTokens().Where(token => token switch
{
@@ -148,9 +140,12 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or
_ => false
}));
- // Ask the cookie authentication handler to return a new cookie and redirect
- // the user agent to the return URL stored in the authentication properties.
- return SignIn(new ClaimsPrincipal(identity), properties, CookieAuthenticationDefaults.AuthenticationScheme);
+ // Ask the default sign-in handler to return a new cookie and redirect the
+ // user agent to the return URL stored in the authentication properties.
+ //
+ // For scenarios where the default sign-in handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ return SignIn(new ClaimsPrincipal(identity), properties);
}
// Note: this controller uses the same callback action for all providers
diff --git a/samples/Mimban/Mimban.Server/Program.cs b/samples/Mimban/Mimban.Server/Program.cs
index 0c35be639..dc83fed68 100644
--- a/samples/Mimban/Mimban.Server/Program.cs
+++ b/samples/Mimban/Mimban.Server/Program.cs
@@ -157,7 +157,7 @@ await manager.CreateAsync(new OpenIddictApplicationDescriptor
// Resolve the claims extracted by OpenIddict from the userinfo response returned by GitHub.
var result = await context.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme);
- var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
+ var identity = new ClaimsIdentity(authenticationType: "ExternalLogin");
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, result.Principal!.FindFirst("id")!.Value));
var properties = new AuthenticationProperties
@@ -165,14 +165,19 @@ await manager.CreateAsync(new OpenIddictApplicationDescriptor
RedirectUri = result.Properties!.RedirectUri
};
- return Results.SignIn(new ClaimsPrincipal(identity), properties, CookieAuthenticationDefaults.AuthenticationScheme);
+ // For scenarios where the default sign-in handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ return Results.SignIn(new ClaimsPrincipal(identity), properties);
});
app.MapGet("/authorize", async (HttpContext context) =>
{
// Resolve the claims stored in the cookie created after the GitHub authentication dance.
// If the principal cannot be found, trigger a new challenge to redirect the user to GitHub.
- var principal = (await context.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme))?.Principal;
+ //
+ // For scenarios where the default authentication handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ var principal = (await context.AuthenticateAsync())?.Principal;
if (principal is null)
{
var properties = new AuthenticationProperties
diff --git a/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs b/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs
index 028b9b902..4ea89a7b2 100644
--- a/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs
+++ b/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs
@@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
+using OpenIddict.Abstractions;
using OpenIddict.Client.AspNetCore;
using static OpenIddict.Abstractions.OpenIddictConstants;
@@ -31,7 +32,10 @@ public async Task LogOut(string returnUrl)
{
// Retrieve the identity stored in the local authentication cookie. If it's not available,
// this indicate that the user is already logged out locally (or has not logged in yet).
- var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
+ //
+ // For scenarios where the default authentication handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ var result = await HttpContext.AuthenticateAsync();
if (result is not { Succeeded: true })
{
// Only allow local return URLs to prevent open redirect attacks.
@@ -39,7 +43,10 @@ public async Task LogOut(string returnUrl)
}
// Remove the local authentication cookie before triggering a redirection to the remote server.
- await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
+ //
+ // For scenarios where the default sign-out handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ await HttpContext.SignOutAsync();
var properties = new AuthenticationProperties(new Dictionary
{
@@ -93,47 +100,32 @@ public async Task LogInCallback()
// Such identities cannot be used as-is to build an authentication cookie in ASP.NET Core (as the
// antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but
// the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls.
- if (result.Principal.Identity is not ClaimsIdentity { IsAuthenticated: true })
+ if (result.Principal is not ClaimsPrincipal { Identity.IsAuthenticated: true })
{
throw new InvalidOperationException("The external authorization data cannot be used for authentication.");
}
// Build an identity based on the external claims and that will be used to create the authentication cookie.
- //
- // By default, all claims extracted during the authorization dance are available. The claims collection stored
- // in the cookie can be filtered out or mapped to different names depending the claim name or its issuer.
- var claims = new List(result.Principal.Claims
- .Select(claim => claim switch
- {
- // Map the standard "sub" and custom "id" claims to ClaimTypes.NameIdentifier, which is
- // the default claim type used by .NET and is required by the antiforgery components.
- { Type: Claims.Subject }
- => new Claim(ClaimTypes.NameIdentifier, claim.Value, claim.ValueType, claim.Issuer),
-
- // Map the standard "name" claim to ClaimTypes.Name.
- { Type: Claims.Name }
- => new Claim(ClaimTypes.Name, claim.Value, claim.ValueType, claim.Issuer),
-
- _ => claim
- })
- .Where(claim => claim switch
- {
- // Preserve the basic claims that are necessary for the application to work correctly.
- { Type: ClaimTypes.NameIdentifier or ClaimTypes.Name } => true,
+ var identity = new ClaimsIdentity(authenticationType: "ExternalLogin");
- // Don't preserve the other claims.
- _ => false
- }));
+ // By default, OpenIddict will automatically try to map the email/name and name identifier claims from
+ // their standard OpenID Connect or provider-specific equivalent, if available. If needed, additional
+ // claims can be resolved from the external identity and copied to the final authentication cookie.
+ identity.SetClaim(ClaimTypes.Email, result.Principal.GetClaim(ClaimTypes.Email))
+ .SetClaim(ClaimTypes.Name, result.Principal.GetClaim(ClaimTypes.Name))
+ .SetClaim(ClaimTypes.NameIdentifier, result.Principal.GetClaim(ClaimTypes.NameIdentifier));
- var identity = new ClaimsIdentity(claims,
- authenticationType: CookieAuthenticationDefaults.AuthenticationScheme,
- nameType: ClaimTypes.Name,
- roleType: ClaimTypes.Role);
+ // Preserve the registration identifier to be able to resolve it later.
+ identity.SetClaim(Claims.Private.RegistrationId, result.Principal.GetClaim(Claims.Private.RegistrationId));
// Build the authentication properties based on the properties that were added when the challenge was triggered.
- var properties = new AuthenticationProperties(result.Properties.Items);
+ var properties = new AuthenticationProperties(result.Properties.Items)
+ {
+ RedirectUri = result.Properties.RedirectUri ?? "/"
+ };
// If needed, the tokens returned by the authorization server can be stored in the authentication cookie.
+ //
// To make cookies less heavy, tokens that are not used are filtered out before creating the cookie.
properties.StoreTokens(result.Properties.GetTokens().Where(token => token switch
{
@@ -148,9 +140,12 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or
_ => false
}));
- // Ask the cookie authentication handler to return a new cookie and redirect
- // the user agent to the return URL stored in the authentication properties.
- return SignIn(new ClaimsPrincipal(identity), properties, CookieAuthenticationDefaults.AuthenticationScheme);
+ // Ask the default sign-in handler to return a new cookie and redirect the
+ // user agent to the return URL stored in the authentication properties.
+ //
+ // For scenarios where the default sign-in handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ return SignIn(new ClaimsPrincipal(identity), properties);
}
// Note: this controller uses the same callback action for all providers
diff --git a/samples/Velusia/Velusia.Client/Controllers/HomeController.cs b/samples/Velusia/Velusia.Client/Controllers/HomeController.cs
index c37d3023e..f1669887a 100644
--- a/samples/Velusia/Velusia.Client/Controllers/HomeController.cs
+++ b/samples/Velusia/Velusia.Client/Controllers/HomeController.cs
@@ -3,7 +3,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Client.AspNetCore;
@@ -23,8 +22,9 @@ public HomeController(IHttpClientFactory httpClientFactory)
[Authorize, HttpPost("~/")]
public async Task Index(CancellationToken cancellationToken)
{
- var token = await HttpContext.GetTokenAsync(CookieAuthenticationDefaults.AuthenticationScheme,
- OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken);
+ // For scenarios where the default authentication handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ var token = await HttpContext.GetTokenAsync(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken);
using var client = _httpClientFactory.CreateClient();
diff --git a/samples/Velusia/Velusia.Server/Controllers/AuthenticationController.cs b/samples/Velusia/Velusia.Server/Controllers/AuthenticationController.cs
new file mode 100644
index 000000000..7f84a88a9
--- /dev/null
+++ b/samples/Velusia/Velusia.Server/Controllers/AuthenticationController.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Linq;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Mvc;
+using OpenIddict.Abstractions;
+using OpenIddict.Client.AspNetCore;
+using static OpenIddict.Abstractions.OpenIddictConstants;
+
+namespace Velusia.Server.Controllers;
+
+public class AuthenticationController : Controller
+{
+ // Note: this controller uses the same callback action for all providers
+ // but for users who prefer using a different action per provider,
+ // the following action can be split into separate actions.
+ [HttpGet("~/callback/login/{provider}"), HttpPost("~/callback/login/{provider}"), IgnoreAntiforgeryToken]
+ public async Task LogInCallback()
+ {
+ // Retrieve the authorization data validated by OpenIddict as part of the callback handling.
+ var result = await HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme);
+
+ // Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons:
+ //
+ // * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable
+ // for applications that don't need a long-term access to the user's resources or don't want to store
+ // access/refresh tokens in a database or in an authentication cookie (which has security implications).
+ // It is also suitable for applications that don't need to authenticate users but only need to perform
+ // action(s) on their behalf by making API calls using the access token returned by the remote server.
+ //
+ // * Storing the external claims/tokens in a database (and optionally keeping the essential claims in an
+ // authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET
+ // Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens.
+ //
+ // Note: in this case, it's recommended to use column encryption to protect the tokens in the database.
+ //
+ // * Storing the external claims/tokens in an authentication cookie, which doesn't require having
+ // a user database but may be affected by the cookie size limits enforced by most browser vendors
+ // (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies).
+ //
+ // Note: this is the approach used here, but the external claims are first filtered to only persist
+ // a few claims like the user identifier. The same approach is used to store the access/refresh tokens.
+
+ // Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint,
+ // result.Principal.Identity will represent an unauthenticated identity and won't contain any claim.
+ //
+ // Such identities cannot be used as-is to build an authentication cookie in ASP.NET Core (as the
+ // antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but
+ // the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls.
+ if (result.Principal is not ClaimsPrincipal { Identity.IsAuthenticated: true })
+ {
+ throw new InvalidOperationException("The external authorization data cannot be used for authentication.");
+ }
+
+ // Build an identity based on the external claims and that will be used to create the authentication cookie.
+ var identity = new ClaimsIdentity(authenticationType: "ExternalLogin");
+
+ // By default, OpenIddict will automatically try to map the email/name and name identifier claims from
+ // their standard OpenID Connect or provider-specific equivalent, if available. If needed, additional
+ // claims can be resolved from the external identity and copied to the final authentication cookie.
+ identity.SetClaim(ClaimTypes.Email, result.Principal.GetClaim(ClaimTypes.Email))
+ .SetClaim(ClaimTypes.Name, result.Principal.GetClaim(ClaimTypes.Name))
+ .SetClaim(ClaimTypes.NameIdentifier, result.Principal.GetClaim(ClaimTypes.NameIdentifier));
+
+ // Preserve the registration identifier to be able to resolve it later.
+ identity.SetClaim(Claims.Private.RegistrationId, result.Principal.GetClaim(Claims.Private.RegistrationId));
+
+ // Build the authentication properties based on the properties that were added when the challenge was triggered.
+ var properties = new AuthenticationProperties(result.Properties.Items)
+ {
+ RedirectUri = result.Properties.RedirectUri ?? "/"
+ };
+
+ // If needed, the tokens returned by the authorization server can be stored in the authentication cookie.
+ // To make cookies less heavy, tokens that are not used are filtered out before creating the cookie.
+ properties.StoreTokens(result.Properties.GetTokens().Where(token => token switch
+ {
+ // Preserve the access and refresh tokens returned in the token response, if available.
+ {
+ Name: OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or
+ OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken
+ } => true,
+
+ // Ignore the other tokens.
+ _ => false
+ }));
+
+ // Ask the default sign-in handler to return a new cookie and redirect the
+ // user agent to the return URL stored in the authentication properties.
+ //
+ // For scenarios where the default sign-in handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ return SignIn(new ClaimsPrincipal(identity), properties);
+ }
+}
diff --git a/samples/Velusia/Velusia.Server/Controllers/AuthorizationController.cs b/samples/Velusia/Velusia.Server/Controllers/AuthorizationController.cs
index 277986030..61391c397 100644
--- a/samples/Velusia/Velusia.Server/Controllers/AuthorizationController.cs
+++ b/samples/Velusia/Velusia.Server/Controllers/AuthorizationController.cs
@@ -57,7 +57,10 @@ public async Task Authorize()
// - If the user principal can't be extracted or the cookie is too old.
// - If prompt=login was specified by the client application.
// - If a max_age parameter was provided and the authentication cookie is not considered "fresh" enough.
- var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
+ //
+ // For scenarios where the default authentication handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ var result = await HttpContext.AuthenticateAsync();
if (result == null || !result.Succeeded || request.HasPrompt(Prompts.Login) ||
(request.MaxAge != null && result.Properties?.IssuedUtc != null &&
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)))
@@ -85,12 +88,12 @@ public async Task Authorize()
parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt)));
- return Challenge(
- authenticationSchemes: IdentityConstants.ApplicationScheme,
- properties: new AuthenticationProperties
- {
- RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
- });
+ // For scenarios where the default challenge handler configured in the ASP.NET Core
+ // authentication options shouldn't be used, a specific scheme can be specified here.
+ return Challenge(new AuthenticationProperties
+ {
+ RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
+ });
}
// Retrieve the profile of the logged in user.
diff --git a/samples/Velusia/Velusia.Server/Startup.cs b/samples/Velusia/Velusia.Server/Startup.cs
index 5ced58f69..022bbe7c1 100644
--- a/samples/Velusia/Velusia.Server/Startup.cs
+++ b/samples/Velusia/Velusia.Server/Startup.cs
@@ -69,6 +69,43 @@ public void ConfigureServices(IServiceCollection services)
options.UseQuartz();
})
+ // Register the OpenIddict client components.
+ .AddClient(options =>
+ {
+ // Note: this sample uses the code flow, but you can enable the other flows if necessary.
+ options.AllowAuthorizationCodeFlow();
+
+ // Register the signing and encryption credentials used to protect
+ // sensitive data like the state tokens produced by OpenIddict.
+ options.AddDevelopmentEncryptionCertificate()
+ .AddDevelopmentSigningCertificate();
+
+ // Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
+ options.UseAspNetCore()
+ .EnableStatusCodePagesIntegration()
+ .EnableRedirectionEndpointPassthrough();
+
+ // Register the System.Net.Http integration and use the identity of the current
+ // assembly as a more specific user agent, which can be useful when dealing with
+ // providers that use the user agent as a way to throttle requests (e.g Reddit).
+ options.UseSystemNetHttp()
+ .SetProductInformation(typeof(Startup).Assembly);
+
+ // Register the Web providers integrations.
+ //
+ // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint
+ // URI per provider, unless all the registered providers support returning a special "iss"
+ // parameter containing their URL as part of authorization responses. For more information,
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4.
+ options.UseWebProviders()
+ .AddGitHub(options =>
+ {
+ options.SetClientId("c4ade52327b01ddacff3")
+ .SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122")
+ .SetRedirectUri("callback/login/github");
+ });
+ })
+
// Register the OpenIddict server components.
.AddServer(options =>
{