Implemented OIDC

This commit is contained in:
Mia Rose Winter 2024-03-11 15:26:03 +01:00
parent 116dd93da2
commit a9269ece30
Signed by: miawinter
GPG key ID: 4B6F6A83178F595E
28 changed files with 1842 additions and 497 deletions

View file

@ -19,6 +19,7 @@
<BoardComponent CenterContent="true">
<BoardCardComponent Heading="@Localizer["Title"]">
<StatusMessage Message="@Message" />
<a class="btn btn-primary w-full mt-3" href="/Account/Login">@Localizer["Login_Label"]</a>
</BoardCardComponent>
</BoardComponent>

View file

@ -4,45 +4,45 @@
@using System.Security.Claims
@using System.Text
@using System.Text.Encodings.Web
@using Humanizer
@using Microsoft.AspNetCore.Identity
@using Microsoft.AspNetCore.WebUtilities
@using Wave.Data
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@inject RoleManager<IdentityRole> RoleManager
@inject IUserStore<ApplicationUser> UserStore
@inject IEmailSender<ApplicationUser> EmailSender
@inject NavigationManager NavigationManager
@inject IdentityRedirectManager RedirectManager
@inject ILogger<ExternalLogin> Logger
@inject IStringLocalizer<ExternalLogin> Localizer
<PageTitle>Register</PageTitle>
<PageTitle>@Localizer["Title"]</PageTitle>
<StatusMessage Message="@message" />
<h1>Register</h1>
<h2>Associate your @ProviderDisplayName account.</h2>
<hr />
<div class="alert alert-info">
You've successfully authenticated with <strong>@ProviderDisplayName</strong>.
Please enter an email address for this site below and click the Register button to finish
logging in.
</div>
<div class="row">
<div class="col-md-4">
<BoardComponent CenterContent="true">
<BoardCardComponent Heading="@Localizer["Title"]">
<Alert CanRemove="false" Type="Alert.MessageType.Information">
<p>
@string.Format(Localizer["Message"], ProviderDisplayName)
</p>
</Alert>
<EditForm Model="Input" OnValidSubmit="OnValidSubmitAsync" FormName="confirmation" method="post">
<DataAnnotationsValidator />
<ValidationSummary class="text-danger" role="alert" />
<div class="form-floating mb-3">
<InputText @bind-Value="Input.Email" class="form-control" autocomplete="email" placeholder="Please enter your email." />
<label for="email" class="form-label">Email</label>
<ValidationMessage For="() => Input.Email" />
</div>
<button type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
<DataAnnotationsValidator/>
<InputLabelComponent LabelText="@Localizer["Email_Label"]" For="() => Input.Email">
<InputText @bind-Value="Input.Email" class="input input-bordered w-full"
autocomplete="email" placeholder="@Localizer["Email_Placeholder"]" />
</InputLabelComponent>
<button type="submit" class="btn btn-primary w-full">
@Localizer["Submit"]
</button>
</EditForm>
</div>
</div>
</BoardCardComponent>
</BoardComponent>
@code {
public const string LoginCallbackAction = "LoginCallback";
@ -67,25 +67,20 @@
private string? ProviderDisplayName => externalLoginInfo.ProviderDisplayName;
protected override async Task OnInitializedAsync()
{
if (RemoteError is not null)
{
protected override async Task OnInitializedAsync() {
if (RemoteError is not null) {
RedirectManager.RedirectToWithStatus("Account/Login", $"Error from external provider: {RemoteError}", HttpContext);
}
var info = await SignInManager.GetExternalLoginInfoAsync();
if (info is null)
{
if (info is null) {
RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information.", HttpContext);
}
externalLoginInfo = info;
if (HttpMethods.IsGet(HttpContext.Request.Method))
{
if (Action == LoginCallbackAction)
{
if (HttpMethods.IsGet(HttpContext.Request.Method)) {
if (Action == LoginCallbackAction) {
await OnLoginCallbackAsync();
return;
}
@ -96,8 +91,7 @@
}
}
private async Task OnLoginCallbackAsync()
{
private async Task OnLoginCallbackAsync() {
// Sign in the user with this external login provider if the user already has a login.
var result = await SignInManager.ExternalLoginSignInAsync(
externalLoginInfo.LoginProvider,
@ -105,54 +99,71 @@
isPersistent: false,
bypassTwoFactor: true);
if (result.Succeeded)
{
if (result.Succeeded) {
Logger.LogInformation(
"{Name} logged in with {LoginProvider} provider.",
externalLoginInfo.Principal.Identity?.Name,
externalLoginInfo.LoginProvider);
RedirectManager.RedirectTo(ReturnUrl);
}
else if (result.IsLockedOut)
{
} else if (result.IsLockedOut) {
RedirectManager.RedirectTo("Account/Lockout");
}
// If the user does not have an account, then ask the user to create an account.
if (externalLoginInfo.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
{
Input.Email = externalLoginInfo.Principal.FindFirstValue(ClaimTypes.Email) ?? "";
if (externalLoginInfo.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.Email or "email") is {} claim) {
Input.Email = claim.Value;
}
}
private async Task OnValidSubmitAsync()
{
private async Task OnValidSubmitAsync() {
var emailStore = GetEmailStore();
var user = CreateUser();
await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var info = await SignInManager.GetExternalLoginInfoAsync();
if (info?.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.Name or ClaimTypes.GivenName or "name") is {} nameClaim) {
user.FullName = nameClaim.Value;
}
var result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
if (result.Succeeded) {
result = await UserManager.AddLoginAsync(user, externalLoginInfo);
if (result.Succeeded)
{
if (result.Succeeded) {
Logger.LogInformation("User created an account using {Name} provider.", externalLoginInfo.LoginProvider);
var userId = await UserManager.GetUserIdAsync(user);
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
string userId = await UserManager.GetUserIdAsync(user);
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = NavigationManager.GetUriWithQueryParameters(
string callbackUrl = NavigationManager.GetUriWithQueryParameters(
NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code });
new Dictionary<string, object?> {["userId"] = userId, ["code"] = code});
await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl));
if (info?.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.Role or "roles") is { } roleClaim) {
var roles = roleClaim.Value.Split(",").Select(s => s.Trim());
foreach (string role in roles) {
try {
string r = role.Titleize();
if (r is "Author" or "Reviewer" or "Moderator" or "Admin" &&
!await RoleManager.RoleExistsAsync(r) &&
!(await RoleManager.CreateAsync(new IdentityRole(r))).Succeeded) {
Logger.LogError("Failed to create role {role}. Failed to assign it to user {name}.", r, user.UserName);
continue;
}
await UserManager.AddToRoleAsync(user, r);
} catch (Exception ex) {
Logger.LogWarning(ex, "Failed to add newly created user {name} to role {role}.", user.UserName, role);
}
}
}
// If account confirmation is required, we need to show the link if we don't have a real email sender
if (UserManager.Options.SignIn.RequireConfirmedAccount)
{
if (UserManager.Options.SignIn.RequireConfirmedAccount) {
RedirectManager.RedirectTo("Account/RegisterConfirmation", new() { ["email"] = Input.Email });
}
@ -164,32 +175,26 @@
message = $"Error: {string.Join(",", result.Errors.Select(error => error.Description))}";
}
private ApplicationUser CreateUser()
{
try
{
private ApplicationUser CreateUser() {
try {
return Activator.CreateInstance<ApplicationUser>();
}
catch
{
} catch {
throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " +
$"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor");
}
}
private IUserEmailStore<ApplicationUser> GetEmailStore()
{
if (!UserManager.SupportsUserEmail)
{
private IUserEmailStore<ApplicationUser> GetEmailStore() {
if (!UserManager.SupportsUserEmail) {
throw new NotSupportedException("The default UI requires a user store with email support.");
}
return (IUserEmailStore<ApplicationUser>)UserStore;
return (IUserEmailStore<ApplicationUser>) UserStore;
}
private sealed class InputModel
{
[Required]
[EmailAddress]
private sealed class InputModel {
[Required] [EmailAddress]
public string Email { get; set; } = "";
}
}

View file

@ -61,13 +61,8 @@
</li>
</ul>
</BoardCardComponent>
<!--
<section class="w-80 max-w-xs">
<h2 class="text-2xl lg:text-4xl mb-3">Use another service to log in.</h2>
<hr />
<ExternalLoginPicker />
</section>
-->
</BoardComponent>
@code {

View file

@ -3,6 +3,7 @@
@using System.ComponentModel.DataAnnotations
@using Microsoft.AspNetCore.Identity
@using Wave.Data
@using Wave.Utilities
@inject UserManager<ApplicationUser> UserManager
@inject SignInManager<ApplicationUser> SignInManager
@ -10,25 +11,26 @@
@inject IdentityRedirectManager RedirectManager
@inject ILogger<DeletePersonalData> Logger
@inject IStringLocalizer<DeletePersonalData> Localizer
@inject IMessageDisplay Message
<PageTitle>@Localizer["Title"]</PageTitle>
<StatusMessage Message="@message" />
<BoardComponent>
<BoardCardComponent Heading="@Localizer["Title"]">
<Alert Type="Alert.MessageType.Warning" CanRemove="false">
<strong class="font-bold">@Localizer["Delete_FinalWarning"]</strong>
</Alert>
<EditForm Model="Input" FormName="delete-user" OnValidSubmit="OnValidSubmitAsync" method="post" class="w-full">
<DataAnnotationsValidator />
<ValidationSummary class="text-error" role="alert" />
@if (requirePassword) {
<EditForm Model="Input" FormName="delete-user" OnValidSubmit="OnValidSubmitAsync" method="post" class="w-full mt-3">
<DataAnnotationsValidator/>
@if (RequirePassword) {
<InputLabelComponent LabelText="@Localizer["Delete_Password_Label"]" For="() => Input.Password">
<InputText type="password" @bind-Value="Input.Password" class="input input-bordered w-full" required
autocomplete="current-password" placeholder="@Localizer["Delete_Password_Placeholder"]" />
autocomplete="current-password" placeholder="@Localizer["Delete_Password_Placeholder"]"/>
</InputLabelComponent>
} else {
<!-- our model binding fails without zero bound properties -->
<InputText type="hidden" @bind-Value="Input.Password" />
}
<button class="btn btn-lg btn-error w-full" type="submit">@Localizer["Delete_FinalSubmit"]</button>
</EditForm>
@ -36,40 +38,42 @@
</BoardComponent>
@code {
private string? message;
private ApplicationUser user = default!;
private bool requirePassword;
private ApplicationUser User { get; set; } = default!;
private bool RequirePassword { get; set; }
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
[SupplyParameterFromForm]
[CascadingParameter] private HttpContext HttpContext { get; set; } = default!;
[SupplyParameterFromForm(FormName = "delete-user")]
private InputModel Input { get; set; } = new();
protected override async Task OnInitializedAsync() {
user = await UserAccessor.GetRequiredUserAsync(HttpContext);
requirePassword = await UserManager.HasPasswordAsync(user);
User = await UserAccessor.GetRequiredUserAsync(HttpContext);
RequirePassword = await UserManager.HasPasswordAsync(User);
}
private async Task OnValidSubmitAsync() {
if (requirePassword && !await UserManager.CheckPasswordAsync(user, Input.Password)) {
message = Localizer["Delete_ErrorWrongPassword"];
if (RequirePassword && !await UserManager.CheckPasswordAsync(User, Input.Password)) {
Message.ShowError(Localizer["Delete_ErrorWrongPassword"]);
return;
}
var result = await UserManager.DeleteAsync(user);
var result = await UserManager.DeleteAsync(User);
if (!result.Succeeded) {
throw new InvalidOperationException(Localizer["Delete_ErrorUnknown"]);
}
await SignInManager.SignOutAsync();
string userId = await UserManager.GetUserIdAsync(user);
string userId = await UserManager.GetUserIdAsync(User);
Logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
RedirectManager.RedirectToWithStatus("/", Localizer["Delete_Success"], HttpContext);
Message.ShowSuccess(Localizer["Delete_Success"]);
RedirectManager.RedirectTo("/");
}
private sealed class InputModel {
[DataType(DataType.Password)] public string Password { get; set; } = "";
[DataType(DataType.Password)]
public string Password { get; set; } = "";
}
}

View file

@ -9,33 +9,38 @@
@inject IdentityUserAccessor UserAccessor
@inject IUserStore<ApplicationUser> UserStore
@inject IdentityRedirectManager RedirectManager
@inject IStringLocalizer<ExternalLogin> Localizer
<PageTitle>Manage your external logins</PageTitle>
<PageTitle>@Localizer["Title"]</PageTitle>
<StatusMessage />
@if (currentLogins?.Count > 0)
{
<h3>Registered Logins</h3>
<StatusMessage/>
@if (CurrentLogins?.Count > 0) {
<BoardComponent>
<BoardCardComponent Heading="@Localizer["Title"]">
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th>Provider</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var login in currentLogins)
{
@foreach (var login in CurrentLogins) {
<tr>
<td>@login.ProviderDisplayName</td>
<td>
@if (showRemoveButton)
{
@if (ShowRemoveButton) {
<form @formname="@($"remove-login-{login.LoginProvider}")" @onsubmit="OnSubmitAsync" method="post">
<AntiforgeryToken />
<AntiforgeryToken/>
<div>
<input type="hidden" name="@nameof(LoginProvider)" value="@login.LoginProvider" />
<input type="hidden" name="@nameof(ProviderKey)" value="@login.ProviderKey" />
<input type="hidden" name="@nameof(LoginProvider)" value="@login.LoginProvider"/>
<input type="hidden" name="@nameof(ProviderKey)" value="@login.ProviderKey"/>
<button type="submit" class="btn btn-primary" title="Remove this @login.ProviderDisplayName login from your account">Remove</button>
</div>
</form>
}
else
{
} else {
@: &nbsp;
}
</td>
@ -43,17 +48,15 @@
}
</tbody>
</table>
}
@if (otherLogins?.Count > 0)
{
<h4>Add another service to log in.</h4>
<hr />
</div>
</BoardCardComponent>
@if (OtherLogins?.Count > 0) {
<BoardCardComponent Heading="Add another service to log in.">
<form class="form-horizontal" action="Account/Manage/LinkExternalLogin" method="post">
<AntiforgeryToken />
<AntiforgeryToken/>
<div>
<p>
@foreach (var provider in otherLogins)
{
@foreach (var provider in OtherLogins) {
<button type="submit" class="btn btn-primary" name="Provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">
@provider.DisplayName
</button>
@ -61,74 +64,67 @@
</p>
</div>
</form>
</BoardCardComponent>
}
</BoardComponent>
}
@code {
public const string LinkLoginCallbackAction = "LinkLoginCallback";
private ApplicationUser user = default!;
private IList<UserLoginInfo>? currentLogins;
private IList<AuthenticationScheme>? otherLogins;
private bool showRemoveButton;
private ApplicationUser User { get; set; } = default!;
private IList<UserLoginInfo>? CurrentLogins { get; set; }
private IList<AuthenticationScheme>? OtherLogins { get; set; }
private bool ShowRemoveButton { get; set; }
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
[SupplyParameterFromForm]
private string? LoginProvider { get; set; }
[SupplyParameterFromForm]
private string? ProviderKey { get; set; }
[SupplyParameterFromQuery]
private string? Action { get; set; }
protected override async Task OnInitializedAsync()
{
user = await UserAccessor.GetRequiredUserAsync(HttpContext);
currentLogins = await UserManager.GetLoginsAsync(user);
otherLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync())
.Where(auth => currentLogins.All(ul => auth.Name != ul.LoginProvider))
protected override async Task OnInitializedAsync() {
User = await UserAccessor.GetRequiredUserAsync(HttpContext);
CurrentLogins = await UserManager.GetLoginsAsync(User);
OtherLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync())
.Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider))
.ToList();
string? passwordHash = null;
if (UserStore is IUserPasswordStore<ApplicationUser> userPasswordStore)
{
passwordHash = await userPasswordStore.GetPasswordHashAsync(user, HttpContext.RequestAborted);
if (UserStore is IUserPasswordStore<ApplicationUser> userPasswordStore) {
passwordHash = await userPasswordStore.GetPasswordHashAsync(User, HttpContext.RequestAborted);
}
showRemoveButton = passwordHash is not null || currentLogins.Count > 1;
ShowRemoveButton = passwordHash is not null || CurrentLogins.Count > 1;
if (HttpMethods.IsGet(HttpContext.Request.Method) && Action == LinkLoginCallbackAction)
{
if (HttpMethods.IsGet(HttpContext.Request.Method) && Action == LinkLoginCallbackAction) {
await OnGetLinkLoginCallbackAsync();
}
}
private async Task OnSubmitAsync()
{
var result = await UserManager.RemoveLoginAsync(user, LoginProvider!, ProviderKey!);
if (!result.Succeeded)
{
private async Task OnSubmitAsync() {
var result = await UserManager.RemoveLoginAsync(User, LoginProvider!, ProviderKey!);
if (!result.Succeeded) {
RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not removed.", HttpContext);
}
await SignInManager.RefreshSignInAsync(user);
await SignInManager.RefreshSignInAsync(User);
RedirectManager.RedirectToCurrentPageWithStatus("The external login was removed.", HttpContext);
}
private async Task OnGetLinkLoginCallbackAsync()
{
var userId = await UserManager.GetUserIdAsync(user);
private async Task OnGetLinkLoginCallbackAsync() {
var userId = await UserManager.GetUserIdAsync(User);
var info = await SignInManager.GetExternalLoginInfoAsync(userId);
if (info is null)
{
if (info is null) {
RedirectManager.RedirectToCurrentPageWithStatus("Error: Could not load external login info.", HttpContext);
}
var result = await UserManager.AddLoginAsync(user, info);
if (!result.Succeeded)
{
var result = await UserManager.AddLoginAsync(User, info);
if (!result.Succeeded) {
RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not added. External logins can only be associated with one account.", HttpContext);
}
@ -137,4 +133,5 @@
RedirectManager.RedirectToCurrentPageWithStatus("The external login was added.", HttpContext);
}
}

View file

@ -8,71 +8,62 @@
@inject SignInManager<ApplicationUser> SignInManager
@inject IdentityUserAccessor UserAccessor
@inject IdentityRedirectManager RedirectManager
@inject IStringLocalizer<SetPassword> Localizer
<PageTitle>Set password</PageTitle>
<PageTitle>@Localizer["Title"]</PageTitle>
<h3>Set your password</h3>
<StatusMessage Message="@message" />
<p class="text-info">
You do not have a local username/password for this site. Add a local
account so you can log in without an external login.
</p>
<div class="row">
<div class="col-md-6">
<StatusMessage Message="@Message"/>
<BoardComponent>
<BoardCardComponent Heading="@Localizer["Title"]">
<Alert CanRemove="false" Type="Alert.MessageType.Information">
<p>@Localizer["Message"]</p>
</Alert>
<EditForm Model="Input" FormName="set-password" OnValidSubmit="OnValidSubmitAsync" method="post">
<DataAnnotationsValidator />
<ValidationSummary class="text-danger" role="alert" />
<div class="form-floating mb-3">
<InputText type="password" @bind-Value="Input.NewPassword" class="form-control" autocomplete="new-password" placeholder="Please enter your new password." />
<label for="new-password" class="form-label">New password</label>
<ValidationMessage For="() => Input.NewPassword" class="text-danger" />
</div>
<div class="form-floating mb-3">
<InputText type="password" @bind-Value="Input.ConfirmPassword" class="form-control" autocomplete="new-password" placeholder="Please confirm your new password." />
<label for="confirm-password" class="form-label">Confirm password</label>
<ValidationMessage For="() => Input.ConfirmPassword" class="text-danger" />
</div>
<button type="submit" class="w-100 btn btn-lg btn-primary">Set password</button>
<DataAnnotationsValidator/>
<InputLabelComponent LabelText="@Localizer["NewPassword_Label"]" For="() => Input.NewPassword">
<InputText type="password" @bind-Value="Input.NewPassword" class="input input-bordered w-full" autocomplete="new-password"
required aria-required="true" placeholder="@Localizer["NewPassword_Placeholder"]"/>
</InputLabelComponent>
<InputLabelComponent LabelText="@Localizer["ConfirmPassword_Label"]" For="() => Input.ConfirmPassword">
<InputText type="password" @bind-Value="Input.ConfirmPassword" class="input input-bordered w-full" autocomplete="new-password"
required aria-required="true" placeholder="@Localizer["ConfirmPassword_Placeholder"]"/>
</InputLabelComponent>
<button type="submit" class="btn btn-primary w-full">
@Localizer["ChangePassword_Submit"]
</button>
</EditForm>
</div>
</div>
</BoardCardComponent>
</BoardComponent>
@code {
private string? message;
private ApplicationUser user = default!;
private string? Message { get; set; }
private ApplicationUser User { get; set; } = default!;
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
[CascadingParameter] private HttpContext HttpContext { get; set; } = default!;
[SupplyParameterFromForm]
private InputModel Input { get; set; } = new();
[SupplyParameterFromForm] private InputModel Input { get; set; } = new();
protected override async Task OnInitializedAsync()
{
user = await UserAccessor.GetRequiredUserAsync(HttpContext);
protected override async Task OnInitializedAsync() {
User = await UserAccessor.GetRequiredUserAsync(HttpContext);
var hasPassword = await UserManager.HasPasswordAsync(user);
if (hasPassword)
{
bool hasPassword = await UserManager.HasPasswordAsync(User);
if (hasPassword) {
RedirectManager.RedirectTo("Account/Manage/ChangePassword");
}
}
private async Task OnValidSubmitAsync()
{
var addPasswordResult = await UserManager.AddPasswordAsync(user, Input.NewPassword!);
if (!addPasswordResult.Succeeded)
{
message = $"Error: {string.Join(",", addPasswordResult.Errors.Select(error => error.Description))}";
private async Task OnValidSubmitAsync() {
var addPasswordResult = await UserManager.AddPasswordAsync(User, Input.NewPassword!);
if (!addPasswordResult.Succeeded) {
Message = $"Error: {string.Join(",", addPasswordResult.Errors.Select(error => error.Description))}";
return;
}
await SignInManager.RefreshSignInAsync(user);
await SignInManager.RefreshSignInAsync(User);
RedirectManager.RedirectToCurrentPageWithStatus("Your password has been set.", HttpContext);
}
private sealed class InputModel
{
private sealed class InputModel {
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
@ -84,4 +75,5 @@
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string? ConfirmPassword { get; set; }
}
}

View file

@ -9,64 +9,59 @@
@inject IEmailSender<ApplicationUser> EmailSender
@inject NavigationManager NavigationManager
@inject IdentityRedirectManager RedirectManager
@inject IStringLocalizer<RegisterConfirmation> Localizer
<HeadContent>
<meta name="robots" content="noindex,nofollow">
</HeadContent>
<PageTitle>Register confirmation</PageTitle>
<PageTitle>@Localizer["Title"] </PageTitle>
<h1>Register confirmation</h1>
<StatusMessage Message="@statusMessage"/>
<StatusMessage Message="@statusMessage" />
@if (emailConfirmationLink is not null)
{
<BoardComponent CenterContent="true">
<BoardCardComponent Heading="@Localizer["Title"]">
<Alert CanRemove="false" Type="Alert.MessageType.Success">
@if (emailConfirmationLink is not null) {
<p>
This app does not currently have a real email sender registered, see <a href="https://aka.ms/aspaccountconf">these docs</a> for how to configure a real email sender.
Normally this would be emailed: <a href="@emailConfirmationLink">Click here to confirm your account</a>
</p>
}
else
{
<p>Please check your email to confirm your account.</p>
}
} else {
<p>@Localizer["Message"]</p>
}
</Alert>
</BoardCardComponent>
</BoardComponent>
@code {
private string? emailConfirmationLink;
private string? statusMessage;
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
[CascadingParameter] private HttpContext HttpContext { get; set; } = default!;
[SupplyParameterFromQuery]
private string? Email { get; set; }
[SupplyParameterFromQuery] private string? Email { get; set; }
[SupplyParameterFromQuery]
private string? ReturnUrl { get; set; }
[SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
protected override async Task OnInitializedAsync()
{
if (Email is null)
{
protected override async Task OnInitializedAsync() {
if (Email is null) {
RedirectManager.RedirectTo("");
}
var user = await UserManager.FindByEmailAsync(Email);
if (user is null)
{
if (user is null) {
HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
statusMessage = "Error finding user for unspecified email";
}
else if (EmailSender is IdentityNoOpEmailSender)
{
} else if (EmailSender is IdentityNoOpEmailSender) {
// Once you add a real email sender, you should remove this code that lets you confirm the account
var userId = await UserManager.GetUserIdAsync(user);
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
emailConfirmationLink = NavigationManager.GetUriWithQueryParameters(
NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl });
new Dictionary<string, object?> {["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl});
}
}
}

View file

@ -4,40 +4,42 @@
@inject SignInManager<ApplicationUser> SignInManager
@inject IdentityRedirectManager RedirectManager
@inject IStringLocalizer<ExternalLoginPicker> Localizer
@if (externalLogins.Length == 0)
{
<div>
<p>
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
about setting up this ASP.NET application to support logging in via external services</a>.
</p>
</div>
}
else
{
@if (externalLogins.Length > 0) {
<BoardCardComponent Heading="@Localizer["Title"]">
<form class="form-horizontal" action="Account/PerformExternalLogin" method="post">
<div>
<AntiforgeryToken />
<input type="hidden" name="ReturnUrl" value="@ReturnUrl" />
<div class="flex flex-col space-y-3">
<AntiforgeryToken/>
<input type="hidden" name="ReturnUrl" value="@ReturnUrl"/>
<p>
@foreach (var provider in externalLogins)
{
<button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
@foreach (var provider in externalLogins) {
if (provider.Name is "OpenIdConnect") {
<button type="submit" class="btn btn-wide btn-primary" name="provider" value="@provider.Name" title="@Localizer["OpenId_Tooltip"]">
@Localizer["OpenId_Label"]
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9" />
</svg>
</button>
} else {
<button type="submit" class="btn btn-wide btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">
@provider.DisplayName
</button>
}
}
</p>
</div>
</form>
</BoardCardComponent>
}
@code {
private AuthenticationScheme[] externalLogins = [];
[SupplyParameterFromQuery]
private string? ReturnUrl { get; set; }
[SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
protected override async Task OnInitializedAsync()
{
protected override async Task OnInitializedAsync() {
externalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToArray();
}
}

View file

@ -0,0 +1,7 @@
namespace Wave.Data;
public class OidcConfiguration {
public string Authority { get; set; } = string.Empty;
public string ClientId { get; set; } = string.Empty;
public string ClientSecret { get; set; } = string.Empty;
}

View file

@ -11,7 +11,7 @@ public class UserClaimsFactory(
: UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>(userManager, roleManager, options) {
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user) {
var principal = await base.GenerateClaimsAsync(user);
principal.AddClaim(new Claim("Id", user.Id));
// principal.AddClaim(new Claim("Id", user.Id));
principal.AddClaim(new Claim("FullName", user.Name));
return principal;
}

View file

@ -5,7 +5,6 @@
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
@ -19,6 +18,7 @@
using Wave.Data;
using Wave.Services;
using Wave.Utilities;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
string humanReadableVersion = Assembly.GetEntryAssembly()?
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
@ -103,6 +103,30 @@
options.Realm = "Wave API";
})
.AddIdentityCookies();
if (builder.Configuration.GetSection("Oidc").Get<OidcConfiguration>() is {} oidc && !string.IsNullOrWhiteSpace(oidc.Authority)) {
builder.Services.AddAuthentication(options => {
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
}).AddOpenIdConnect(options => {
options.SignInScheme = IdentityConstants.ExternalScheme;
options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
options.Scope.Add(OpenIdConnectScope.OfflineAccess);
options.Authority = oidc.Authority;
options.ClientId = oidc.ClientId;
options.ClientSecret = oidc.ClientSecret;
options.ResponseType = OpenIdConnectResponseType.Code;
options.MapInboundClaims = false;
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "role";
options.CallbackPath = new PathString("/signin-oidc");
options.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
options.RemoteSignOutPath = new PathString("/signout-oidc");
});
}
#endregion
@ -114,7 +138,10 @@
options.UseNpgsql(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
builder.Services.AddIdentityCore<ApplicationUser>(options => {
options.SignIn.RequireConfirmedAccount = true;
options.ClaimsIdentity.UserIdClaimType = "Id";
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()

View file

@ -110,4 +110,7 @@
<data name="Success" xml:space="preserve">
<value>Vielen Dank fürs Bestätigen ihrer E-Mail Adresse.</value>
</data>
<data name="Login_Label" xml:space="preserve">
<value>Anmelden</value>
</data>
</root>

View file

@ -110,4 +110,7 @@
<data name="Success" xml:space="preserve">
<value>Thank you for confirming your email.</value>
</data>
<data name="Login_Label" xml:space="preserve">
<value>Log In</value>
</data>
</root>

View file

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Message" xml:space="preserve">
<value>Sie haben sich erfolgreich mit {0} angemeldet.
Bitte geben Sie nun eine E-Mail Addresse für diese Webseite ein.</value>
</data>
<data name="Email_Label" xml:space="preserve">
<value>E-Mail</value>
</data>
<data name="Email_Placeholder" xml:space="preserve">
<value>name@example.de</value>
</data>
<data name="Submit" xml:space="preserve">
<value>Registrieren</value>
</data>
<data name="Title" xml:space="preserve">
<value>Benutzerkonto Erstellen</value>
</data>
</root>

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Title" xml:space="preserve">
<value>Create Account</value>
</data>
<data name="Message" xml:space="preserve">
<value>You've successfully authenticated with {0}.
Please enter an email address for this site below.</value>
</data>
<data name="Email_Label" xml:space="preserve">
<value>Email</value>
</data>
<data name="Email_Placeholder" xml:space="preserve">
<value>name@example.com</value>
</data>
<data name="Submit" xml:space="preserve">
<value>Register</value>
</data>
</root>

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Title" xml:space="preserve">
<value>Passwort Erstellen</value>
</data>
<data name="Message" xml:space="preserve">
<value>Sie haben momentan kein lokales Benutzerkonto/Passwort für diese Seite. Fügen Sie ein lokales Konto hinzu um sich ohne externen Dienst anmelden zu können.</value>
</data>
<data name="ConfirmPassword_Label" xml:space="preserve">
<value>Neues Passwort wiederholen</value>
</data>
<data name="ConfirmPassword_Placeholder" xml:space="preserve">
<value>Bestätigen Sie ihr neues Passwort</value>
</data>
<data name="NewPassword_Label" xml:space="preserve">
<value>Neues Passwort</value>
</data>
<data name="NewPassword_Placeholder" xml:space="preserve">
<value>Geben Sie ihr neues Passwort ein</value>
</data>
<data name="ChangePassword_Submit" xml:space="preserve">
<value>Passwort Ändern</value>
</data>
</root>

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Title" xml:space="preserve">
<value>Create Password</value>
</data>
<data name="Message" xml:space="preserve">
<value>You do not have a local username/password for this site. Add a local account, so you can log in without an external login.</value>
</data>
<data name="NewPassword_Label" xml:space="preserve">
<value>New password</value>
</data>
<data name="NewPassword_Placeholder" xml:space="preserve">
<value>Enter your new password</value>
</data>
<data name="ConfirmPassword_Label" xml:space="preserve">
<value>Confirm new password</value>
</data>
<data name="ConfirmPassword_Placeholder" xml:space="preserve">
<value>Confirm your new Password</value>
</data>
<data name="ChangePassword_Submit" xml:space="preserve">
<value>Change Password</value>
</data>
</root>

View file

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Title" xml:space="preserve">
<value>Registrierung Bestätigen</value>
</data>
<data name="Message" xml:space="preserve">
<value>Bitte öffnen Sie Ihre E-Mails und bestätigen Sie ihr Benutzerkonto. Sie können sich nicht Anmelden bevor Ihr Konto bestätigt wurde.</value>
</data>
</root>

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Title" xml:space="preserve">
<value>Confirm Registration</value>
</data>
<data name="Message" xml:space="preserve">
<value>Please check your email to confirm your account. You cannot log in before your account has been confirmed.</value>
</data>
</root>

View file

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Title" xml:space="preserve">
<value>Anmelden über externen Dienst</value>
</data>
<data name="OpenId_Tooltip" xml:space="preserve">
<value>Melden Sie sich über ihren OpenID Connect Dienst an</value>
</data>
</root>

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Title" xml:space="preserve">
<value>Log In using external service</value>
</data>
<data name="OpenId_Label" xml:space="preserve">
<value>OpenID Connect</value>
</data>
<data name="OpenId_Tooltip" xml:space="preserve">
<value>Log In using OpenID Connect provider</value>
</data>
</root>

View file

@ -17,6 +17,7 @@
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.5.0" />
<PackageReference Include="MailKit" Version="4.3.0" />
<PackageReference Include="Markdig" Version="0.34.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />

File diff suppressed because one or more lines are too long