Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
33 changes: 31 additions & 2 deletions samples/sample-blazor/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,37 @@
{
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);
authProperties.SetParameter("identifiers", string.Join(",", new[]
{
LogtoParameters.Authentication.Identifiers.Username,
}));

// Set `direct_sign_in`
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));

// Set `extra_params`
var extraParams = new LogtoParameters.Authentication.ExtraParams
{
{ "utm_source", "website" },
{ "utm_medium", "organic" }
};
authProperties.SetParameter("extra_params", System.Text.Json.JsonSerializer.Serialize(extraParams));

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 = "/"
};

authProperties.SetParameter("first_screen", LogtoParameters.Authentication.FirstScreen.Register);
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));

var extraParams = new LogtoParameters.Authentication.ExtraParams
{
{ "utm_source", "website" },
{ "utm_medium", "organic" }
};
authProperties.SetParameter("extra_params", JsonSerializer.Serialize(extraParams));

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 = "/"
};

authProperties.SetParameter("first_screen", LogtoParameters.Authentication.FirstScreen.Register);
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));

var extraParams = new LogtoParameters.Authentication.ExtraParams
{
{ "utm_source", "website" },
{ "utm_medium", "organic" }
};
authProperties.SetParameter("extra_params", JsonSerializer.Serialize(extraParams));

await HttpContext.ChallengeAsync(authProperties);
}

public async Task OnPostSignOutAsync()
Expand Down
90 changes: 90 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,93 @@ 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.
/// </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.
/// </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.
/// </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,15 +102,51 @@ 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);
},
}
};
options.TokenValidationParameters = new TokenValidationParameters
{
Expand Down