Files
SharepointToolbox-Web/Infrastructure/Auth/LoginPageRenderer.cs
T

81 lines
3.4 KiB
C#

using System.Net;
using Microsoft.AspNetCore.Antiforgery;
namespace SharepointToolbox.Web.Infrastructure.Auth;
/// <summary>Renders the combined login page (Microsoft / Entra button + local credential form)
/// as a self-contained static HTML response. Lives outside the interactive Blazor circuit so
/// the POST handler can issue the auth cookie directly on the HTTP request.</summary>
public static class LoginPageRenderer
{
public static string Build(
HttpContext ctx,
IAntiforgery antiforgery,
string? returnUrl,
bool showError,
bool showEntra = true,
bool showDevButton = false)
{
var tokens = antiforgery.GetAndStoreTokens(ctx);
var ru = WebUtility.HtmlEncode(returnUrl ?? "/");
var afField = WebUtility.HtmlEncode(tokens.FormFieldName);
var afToken = WebUtility.HtmlEncode(tokens.RequestToken);
var error = showError
? "<div class=\"alert alert-error\">Invalid email or password.</div>"
: string.Empty;
var entraButton = showEntra
? $"<a class=\"btn btn-secondary btn-block\" href=\"/account/login/entra?returnUrl={ru}\">Sign in with Microsoft</a><div class=\"login-divider\">or</div>"
: string.Empty;
var devButton = showDevButton
? $"<div class=\"login-divider\">dev</div><a class=\"btn btn-secondary btn-block\" href=\"/account/login/dev?returnUrl={ru}\">Quick sign in as Dev Admin</a>"
: string.Empty;
return $$"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sign in SharePoint Toolbox</title>
<link rel="stylesheet" href="/app.css" />
<style>
body { display:flex; align-items:center; justify-content:center; min-height:100vh; margin:0; background:var(--bg); }
.login-card { width:360px; max-width:92vw; }
.login-card h1 { font-size:20px; margin:0 0 4px; color:var(--text); }
.login-card .sub { font-size:13px; color:var(--text-muted); margin:0 0 18px; }
.login-divider { display:flex; align-items:center; gap:10px; color:var(--text-muted); font-size:12px; margin:18px 0; }
.login-divider::before, .login-divider::after { content:""; flex:1; height:1px; background:var(--border); }
.field { margin-bottom:12px; }
.btn-block { width:100%; justify-content:center; }
</style>
</head>
<body>
<div class="card login-card">
<h1>SharePoint Toolbox</h1>
<p class="sub">Sign in to continue</p>
{{error}}
{{entraButton}}
<form method="post" action="/account/local-login">
<input type="hidden" name="returnUrl" value="{{ru}}" />
<input type="hidden" name="{{afField}}" value="{{afToken}}" />
<div class="field">
<label class="form-label" for="email">Email</label>
<input class="form-input" id="email" name="email" type="email" autocomplete="username" required autofocus />
</div>
<div class="field">
<label class="form-label" for="password">Password</label>
<input class="form-input" id="password" name="password" type="password" autocomplete="current-password" required />
</div>
<button class="btn btn-primary btn-block" type="submit">Sign in</button>
</form>
{{devButton}}
</div>
</body>
</html>
""";
}
}