diff --git a/samples/sample-blazor/Program.cs b/samples/sample-blazor/Program.cs
index 014eb11..6022c9a 100644
--- a/samples/sample-blazor/Program.cs
+++ b/samples/sample-blazor/Program.cs
@@ -47,8 +47,35 @@
{
if (!(context.User?.Identity?.IsAuthenticated ?? false))
{
- await context.ChallengeAsync(new AuthenticationProperties { RedirectUri = "/" });
- } else {
+ var authProperties = new AuthenticationProperties
+ {
+ RedirectUri = "/"
+ };
+
+ ///
+ ///
+ authProperties.SetParameter("first_screen", LogtoParameters.Authentication.FirstScreen.Register);
+
+ // This parameter MUST be used together with `first_screen`.
+ authProperties.SetParameter("identifiers", string.Join(",", new[]
+ {
+ LogtoParameters.Authentication.Identifiers.Username,
+ }));
+
+ var directSignIn = new LogtoParameters.Authentication.DirectSignIn
+ {
+ Target = "github",
+ Method = LogtoParameters.Authentication.DirectSignIn.Methods.Social
+ };
+
+ ///
+ ///
+ authProperties.SetParameter("direct_sign_in", System.Text.Json.JsonSerializer.Serialize(directSignIn));
+
+ await context.ChallengeAsync(authProperties);
+ }
+ else
+ {
context.Response.Redirect("/");
}
});
diff --git a/samples/sample-mvc/Controllers/HomeController.cs b/samples/sample-mvc/Controllers/HomeController.cs
index b1dfa96..b0c813d 100644
--- a/samples/sample-mvc/Controllers/HomeController.cs
+++ b/samples/sample-mvc/Controllers/HomeController.cs
@@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using sample_mvc.Models;
+using System.Text.Json;
namespace sample_mvc.Controllers;
@@ -25,7 +26,32 @@ public async Task Index()
public IActionResult SignIn()
{
- return Challenge(new AuthenticationProperties { RedirectUri = "/" });
+ var authProperties = new AuthenticationProperties
+ {
+ RedirectUri = "/"
+ };
+
+ ///
+ ///
+ authProperties.SetParameter("first_screen", LogtoParameters.Authentication.FirstScreen.Register);
+
+ // This parameter MUST be used together with `first_screen`.
+ authProperties.SetParameter("identifiers", string.Join(",", new[]
+ {
+ LogtoParameters.Authentication.Identifiers.Username,
+ }));
+
+ var directSignIn = new LogtoParameters.Authentication.DirectSignIn
+ {
+ Target = "github",
+ Method = LogtoParameters.Authentication.DirectSignIn.Methods.Social
+ };
+
+ ///
+ ///
+ authProperties.SetParameter("direct_sign_in", JsonSerializer.Serialize(directSignIn));
+
+ return Challenge(authProperties);
}
// Use the `new` keyword to avoid conflict with the `ControllerBase.SignOut` method
diff --git a/samples/sample/Pages/Index.cshtml.cs b/samples/sample/Pages/Index.cshtml.cs
index ec836dc..1a94fc1 100644
--- a/samples/sample/Pages/Index.cshtml.cs
+++ b/samples/sample/Pages/Index.cshtml.cs
@@ -1,6 +1,7 @@
using Logto.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using System.Text.Json;
namespace sample.Pages;
@@ -22,7 +23,32 @@ public async Task OnGetAsync()
public async Task OnPostSignInAsync()
{
- await HttpContext.ChallengeAsync(new AuthenticationProperties { RedirectUri = "/" });
+ var authProperties = new AuthenticationProperties
+ {
+ RedirectUri = "/"
+ };
+
+ ///
+ ///
+ authProperties.SetParameter("first_screen", LogtoParameters.Authentication.FirstScreen.Register);
+
+ // This parameter MUST be used together with `first_screen`
+ authProperties.SetParameter("identifiers", string.Join(",", new[]
+ {
+ LogtoParameters.Authentication.Identifiers.Username,
+ }));
+
+ var directSignIn = new LogtoParameters.Authentication.DirectSignIn
+ {
+ Target = "github",
+ Method = LogtoParameters.Authentication.DirectSignIn.Methods.Social
+ };
+
+ ///
+ ///
+ authProperties.SetParameter("direct_sign_in", JsonSerializer.Serialize(directSignIn));
+
+ await HttpContext.ChallengeAsync(authProperties);
}
public async Task OnPostSignOutAsync()
diff --git a/src/Logto.AspNetCore.Authentication/LogtoParameters.cs b/src/Logto.AspNetCore.Authentication/LogtoParameters.cs
index 86a4cca..58b62b2 100644
--- a/src/Logto.AspNetCore.Authentication/LogtoParameters.cs
+++ b/src/Logto.AspNetCore.Authentication/LogtoParameters.cs
@@ -1,4 +1,5 @@
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
+using System.Collections.Generic;
namespace Logto.AspNetCore.Authentication;
@@ -115,4 +116,96 @@ public static class Claims
///
public const string Identities = "identities";
}
+
+ ///
+ /// The authentication parameters for Logto sign-in experience customization.
+ ///
+ public static class Authentication
+ {
+ ///
+ /// The first screen to show in the sign-in experience.
+ /// See for more details.
+ ///
+ public static class FirstScreen
+ {
+ ///
+ /// Show the register form first.
+ ///
+ public const string Register = "identifier:register";
+
+ ///
+ /// Show the sign-in form first.
+ ///
+ public const string SignIn = "identifier:sign_in";
+
+ ///
+ /// Show the single sign-on form first.
+ ///
+ public const string SingleSignOn = "single_sign_on";
+
+ ///
+ /// Show the reset password form first.
+ ///
+ public const string ResetPassword = "reset_password";
+ }
+
+ ///
+ /// The identifiers to use for authentication.
+ /// This parameter MUST be used together with .
+ ///
+ public static class Identifiers
+ {
+ ///
+ /// Use email for authentication.
+ ///
+ public const string Email = "email";
+
+ ///
+ /// Use phone for authentication.
+ ///
+ public const string Phone = "phone";
+
+ ///
+ /// Use username for authentication.
+ ///
+ public const string Username = "username";
+ }
+
+ ///
+ /// Direct sign-in configuration.
+ /// See for more details.
+ ///
+ public class DirectSignIn
+ {
+ ///
+ /// The target identifier for direct sign-in.
+ ///
+ public string Target { get; set; } = string.Empty;
+
+ ///
+ /// The sign-in method.
+ ///
+ public string Method { get; set; } = string.Empty;
+
+ public static class Methods
+ {
+ ///
+ /// Single sign-on method.
+ ///
+ public const string Sso = "sso";
+
+ ///
+ /// Social sign-in method.
+ ///
+ public const string Social = "social";
+ }
+ }
+
+ ///
+ /// Extra parameters to be passed to the authorization endpoint.
+ ///
+ public class ExtraParams : Dictionary
+ {
+ }
+ }
}
diff --git a/src/Logto.AspNetCore.Authentication/extensions/AuthenticationBuilderExtensions.cs b/src/Logto.AspNetCore.Authentication/extensions/AuthenticationBuilderExtensions.cs
index 4d4485f..2688a3d 100644
--- a/src/Logto.AspNetCore.Authentication/extensions/AuthenticationBuilderExtensions.cs
+++ b/src/Logto.AspNetCore.Authentication/extensions/AuthenticationBuilderExtensions.cs
@@ -9,6 +9,7 @@ namespace Logto.AspNetCore.Authentication;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
+using System.Threading.Tasks;
///
/// Extension methods to configure Logto authentication.
@@ -101,11 +102,49 @@ private static void ConfigureOpenIdConnectOptions(OpenIdConnectOptions options,
options.ClaimActions.MapAllExcept("nbf", "nonce", "c_hash", "at_hash");
options.Events = new OpenIdConnectEvents
{
+ OnRedirectToIdentityProvider = context =>
+ {
+ if (context.Properties.Parameters.TryGetValue("first_screen", out var firstScreen))
+ {
+ context.ProtocolMessage.Parameters.Add("first_screen", firstScreen?.ToString());
+ }
+
+ if (context.Properties.Parameters.TryGetValue("identifiers", out var identifiers))
+ {
+ context.ProtocolMessage.Parameters.Add("identifiers", identifiers?.ToString());
+ }
+
+ if (context.Properties.Parameters.TryGetValue("direct_sign_in", out var directSignIn))
+ {
+ var directSignInOption = System.Text.Json.JsonSerializer.Deserialize(
+ directSignIn?.ToString() ?? "{}"
+ );
+ if (directSignInOption != null && !string.IsNullOrEmpty(directSignInOption.Method) && !string.IsNullOrEmpty(directSignInOption.Target))
+ {
+ context.ProtocolMessage.Parameters.Add("direct_sign_in", $"{directSignInOption.Method}:{directSignInOption.Target}");
+ }
+ }
+
+ if (context.Properties.Parameters.TryGetValue("extra_params", out var extraParams))
+ {
+ var parameters = System.Text.Json.JsonSerializer.Deserialize(
+ extraParams?.ToString() ?? "{}"
+ );
+ if (parameters != null)
+ {
+ foreach (var param in parameters)
+ {
+ context.ProtocolMessage.Parameters.Add(param.Key, param.Value);
+ }
+ }
+ }
+
+ return Task.CompletedTask;
+ },
OnRedirectToIdentityProviderForSignOut = async context =>
{
// Clean up the cookie when signing out.
await context.HttpContext.SignOutAsync(cookieScheme);
-
// Rebuild parameters since we use client_id for sign-out, no need to use id_token_hint.
context.ProtocolMessage.Parameters.Remove(OpenIdConnectParameterNames.IdTokenHint);
context.ProtocolMessage.Parameters.Add(OpenIdConnectParameterNames.ClientId, logtoOptions.AppId);