Skip to content

feat: support identifiers, first_screen, direct_sign_in and extra_params sign-in params #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 14, 2024
Merged
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
31 changes: 29 additions & 2 deletions samples/sample-blazor/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,35 @@
{
if (!(context.User?.Identity?.IsAuthenticated ?? false))
{
await context.ChallengeAsync(new AuthenticationProperties { RedirectUri = "/" });
} else {
var authProperties = new AuthenticationProperties
{
RedirectUri = "/"
};

/// <see href="https://docs.logto.io/docs/references/openid-connect/authentication-parameters/#first-screen"/>
/// <see cref="LogtoParameters.Authentication.FirstScreen"/>
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
};

/// <see href="https://docs.logto.io/docs/references/openid-connect/authentication-parameters/#direct-sign-in"/>
/// <see cref="LogtoParameters.Authentication.DirectSignIn"/>
authProperties.SetParameter("direct_sign_in", System.Text.Json.JsonSerializer.Serialize(directSignIn));

await context.ChallengeAsync(authProperties);
}
else
{
context.Response.Redirect("/");
}
});
Expand Down
28 changes: 27 additions & 1 deletion samples/sample-mvc/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using sample_mvc.Models;
using System.Text.Json;

namespace sample_mvc.Controllers;

Expand All @@ -25,7 +26,32 @@ public async Task<IActionResult> Index()

public IActionResult SignIn()
{
return Challenge(new AuthenticationProperties { RedirectUri = "/" });
var authProperties = new AuthenticationProperties
{
RedirectUri = "/"
};

/// <see href="https://docs.logto.io/docs/references/openid-connect/authentication-parameters/#first-screen"/>
/// <see cref="LogtoParameters.Authentication.FirstScreen"/>
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
};

/// <see href="https://docs.logto.io/docs/references/openid-connect/authentication-parameters/#direct-sign-in"/>
/// <see cref="LogtoParameters.Authentication.DirectSignIn"/>
authProperties.SetParameter("direct_sign_in", JsonSerializer.Serialize(directSignIn));

return Challenge(authProperties);
}

// Use the `new` keyword to avoid conflict with the `ControllerBase.SignOut` method
Expand Down
28 changes: 27 additions & 1 deletion samples/sample/Pages/Index.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Logto.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Text.Json;

namespace sample.Pages;

Expand All @@ -22,7 +23,32 @@ public async Task OnGetAsync()

public async Task OnPostSignInAsync()
{
await HttpContext.ChallengeAsync(new AuthenticationProperties { RedirectUri = "/" });
var authProperties = new AuthenticationProperties
{
RedirectUri = "/"
};

/// <see href="https://docs.logto.io/docs/references/openid-connect/authentication-parameters/#first-screen"/>
/// <see cref="LogtoParameters.Authentication.FirstScreen"/>
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
};

/// <see href="https://docs.logto.io/docs/references/openid-connect/authentication-parameters/#direct-sign-in"/>
/// <see cref="LogtoParameters.Authentication.DirectSignIn"/>
authProperties.SetParameter("direct_sign_in", JsonSerializer.Serialize(directSignIn));

await HttpContext.ChallengeAsync(authProperties);
}

public async Task OnPostSignOutAsync()
Expand Down
93 changes: 93 additions & 0 deletions src/Logto.AspNetCore.Authentication/LogtoParameters.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Collections.Generic;

namespace Logto.AspNetCore.Authentication;

Expand Down Expand Up @@ -115,4 +116,96 @@ public static class Claims
/// </summary>
public const string Identities = "identities";
}

/// <summary>
/// The authentication parameters for Logto sign-in experience customization.
/// </summary>
public static class Authentication
{
/// <summary>
/// The first screen to show in the sign-in experience.
/// See <see href="https://docs.logto.io/docs/references/openid-connect/authentication-parameters/#first-screen"/> for more details.
/// </summary>
public static class FirstScreen
{
/// <summary>
/// Show the register form first.
/// </summary>
public const string Register = "identifier:register";

/// <summary>
/// Show the sign-in form first.
/// </summary>
public const string SignIn = "identifier:sign_in";

/// <summary>
/// Show the single sign-on form first.
/// </summary>
public const string SingleSignOn = "single_sign_on";

/// <summary>
/// Show the reset password form first.
/// </summary>
public const string ResetPassword = "reset_password";
}

/// <summary>
/// The identifiers to use for authentication.
/// This parameter MUST be used together with <see cref="FirstScreen"/>.
/// </summary>
public static class Identifiers
{
/// <summary>
/// Use email for authentication.
/// </summary>
public const string Email = "email";

/// <summary>
/// Use phone for authentication.
/// </summary>
public const string Phone = "phone";

/// <summary>
/// Use username for authentication.
/// </summary>
public const string Username = "username";
}

/// <summary>
/// Direct sign-in configuration.
/// See <see href="https://docs.logto.io/docs/references/openid-connect/authentication-parameters/#direct-sign-in"/> for more details.
/// </summary>
public class DirectSignIn
{
/// <summary>
/// The target identifier for direct sign-in.
/// </summary>
public string Target { get; set; } = string.Empty;

/// <summary>
/// The sign-in method.
/// </summary>
public string Method { get; set; } = string.Empty;

public static class Methods
{
/// <summary>
/// Single sign-on method.
/// </summary>
public const string Sso = "sso";

/// <summary>
/// Social sign-in method.
/// </summary>
public const string Social = "social";
}
}

/// <summary>
/// Extra parameters to be passed to the authorization endpoint.
/// </summary>
public class ExtraParams : Dictionary<string, string>
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Logto.AspNetCore.Authentication;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

/// <summary>
/// Extension methods to configure Logto authentication.
Expand Down Expand Up @@ -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<LogtoParameters.Authentication.DirectSignIn>(
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<LogtoParameters.Authentication.ExtraParams>(
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 <c>client_id</c> for sign-out, no need to use <c>id_token_hint</c>.
context.ProtocolMessage.Parameters.Remove(OpenIdConnectParameterNames.IdTokenHint);
context.ProtocolMessage.Parameters.Add(OpenIdConnectParameterNames.ClientId, logtoOptions.AppId);
Expand Down
Loading