Skip to content

Commit

Permalink
feat: support identifiers, first_screen, direct_sign_in and extra_par…
Browse files Browse the repository at this point in the history
…ams sign-in params (#32)
  • Loading branch information
darcyYe authored Nov 14, 2024
1 parent f30361c commit c1b180f
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 5 deletions.
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

0 comments on commit c1b180f

Please sign in to comment.