Browse Source

Fix github login.

pull/344/head
Sebastian Stehle 8 years ago
parent
commit
116d570f05
  1. 42
      src/Squidex/Areas/IdentityServer/Controllers/Error/ErrorController.cs
  2. 16
      src/Squidex/Areas/IdentityServer/Controllers/Error/ErrorViewModel.cs
  3. 6
      src/Squidex/Areas/IdentityServer/Startup.cs
  4. 1
      src/Squidex/Areas/IdentityServer/Views/Account/AccessDenied.cshtml
  5. 123
      src/Squidex/Areas/IdentityServer/Views/Account/Consent.cshtml
  6. 147
      src/Squidex/Areas/IdentityServer/Views/Account/Login.cshtml
  7. 1
      src/Squidex/Areas/IdentityServer/Views/Account/LogoutCompleted.cshtml
  8. 18
      src/Squidex/Areas/IdentityServer/Views/Error/Error.cshtml
  9. 359
      src/Squidex/Areas/IdentityServer/Views/Profile/Profile.cshtml
  10. 10
      src/Squidex/Areas/IdentityServer/Views/_Layout.cshtml
  11. 11
      src/Squidex/Config/Authentication/GithubHandler.cs

42
src/Squidex/Areas/IdentityServer/Controllers/Error/ErrorController.cs

@ -5,16 +5,54 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Squidex.Infrastructure;
namespace Squidex.Areas.IdentityServer.Controllers.Error namespace Squidex.Areas.IdentityServer.Controllers.Error
{ {
public sealed class ErrorController : IdentityServerController public sealed class ErrorController : IdentityServerController
{ {
private readonly IIdentityServerInteractionService interaction;
public ErrorController(IIdentityServerInteractionService interaction)
{
this.interaction = interaction;
}
[Route("error/")] [Route("error/")]
public IActionResult Error() public async Task<IActionResult> Error(string errorId = null)
{ {
return View(); var vm = new ErrorViewModel();
if (!string.IsNullOrWhiteSpace(errorId))
{
var message = await interaction.GetErrorContextAsync(errorId);
if (message != null)
{
vm.Error = message;
}
}
if (vm.Error == null)
{
var error = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error;
if (error is DomainException exception)
{
vm.Error = new ErrorMessage { ErrorDescription = exception.Message };
}
else if (error?.InnerException is DomainException exception2)
{
vm.Error = new ErrorMessage { ErrorDescription = exception2.Message };
}
}
return View("Error", vm);
} }
} }
} }

16
src/Squidex/Areas/IdentityServer/Controllers/Error/ErrorViewModel.cs

@ -0,0 +1,16 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using IdentityServer4.Models;
namespace Squidex.Areas.IdentityServer.Controllers.Error
{
public class ErrorViewModel
{
public ErrorMessage Error { get; set; }
}
}

6
src/Squidex/Areas/IdentityServer/Startup.cs

@ -23,9 +23,7 @@ namespace Squidex.Areas.IdentityServer
app.Map(Constants.IdentityServerPrefix, identityApp => app.Map(Constants.IdentityServerPrefix, identityApp =>
{ {
identityApp.UseMyIdentityServer(); if (!environment.IsDevelopment())
if (environment.IsDevelopment())
{ {
identityApp.UseDeveloperExceptionPage(); identityApp.UseDeveloperExceptionPage();
} }
@ -34,6 +32,8 @@ namespace Squidex.Areas.IdentityServer
identityApp.UseExceptionHandler("/error"); identityApp.UseExceptionHandler("/error");
} }
identityApp.UseMyIdentityServer();
identityApp.UseStaticFiles(); identityApp.UseStaticFiles();
identityApp.UseMvc(); identityApp.UseMvc();
}); });

1
src/Squidex/Areas/IdentityServer/Views/Account/AccessDenied.cshtml

@ -1,4 +1,5 @@
@{ @{
ViewBag.Theme = "white";
ViewBag.Title = "Account locked"; ViewBag.Title = "Account locked";
} }

123
src/Squidex/Areas/IdentityServer/Views/Account/Consent.cshtml

@ -1,7 +1,8 @@
@model Squidex.Areas.IdentityServer.Controllers.Account.ConsentVM @model Squidex.Areas.IdentityServer.Controllers.Account.ConsentVM
@{ @{
ViewBag.Theme = "white"; ViewBag.Class = "profile-lg";
ViewBag.Title = "Consent"; ViewBag.Title = "Consent";
} }
@ -13,85 +14,81 @@
} }
<form asp-controller="Account" asp-action="Consent" asp-route-returnurl="@Model.ReturnUrl" method="post"> <form asp-controller="Account" asp-action="Consent" asp-route-returnurl="@Model.ReturnUrl" method="post">
<div class="profile profile-lg"> <h2>We need your consent</h2>
<img class="profile-logo" src="~/images/logo-small.png" />
<h2>We need your consent</h2>
<label for="consentToAutomatedEmails"> <label for="consentToAutomatedEmails">
<div class="card profile-section-sm @ErrorClass("ConsentToAutomatedEmails")"> <div class="card profile-section-sm @ErrorClass("ConsentToAutomatedEmails")">
<div class="card-body"> <div class="card-body">
<h4 class="card-title">Automated E-Mails (Optional)</h4> <h4 class="card-title">Automated E-Mails (Optional)</h4>
<div class="card-text row"> <div class="card-text row">
<div class="col col-auto"> <div class="col col-auto">
<input type="checkbox" id="consentToAutomatedEmails" name="consentToAutomatedEmails" value="True" /> <input type="checkbox" id="consentToAutomatedEmails" name="consentToAutomatedEmails" value="True" />
</div> </div>
<div class="col"> <div class="col">
I understand and agree that Squidex sends Emails to inform me about new features, breaking changes and down times. I understand and agree that Squidex sends Emails to inform me about new features, breaking changes and down times.
</div>
</div> </div>
</div> </div>
</div> </div>
</label> </div>
</label>
<label for="consentToCookies"> <label for="consentToCookies">
<div class="card profile-section-sm @ErrorClass("ConsentToCookies")"> <div class="card profile-section-sm @ErrorClass("ConsentToCookies")">
<div class="card-body"> <div class="card-body">
<h4 class="card-title">Cookies & Analytics</h4> <h4 class="card-title">Cookies & Analytics</h4>
<div class="card-text row"> <div class="card-text row">
<div class="col col-auto"> <div class="col col-auto">
<input type="checkbox" id="consentToCookies" name="consentToCookies" value="True" /> <input type="checkbox" id="consentToCookies" name="consentToCookies" value="True" />
</div> </div>
<div class="col"> <div class="col">
<p> <p>
I understand and agree that Squidex uses cookies to ensure you get the best experience on our platform and to store your login status. I understand and agree that Squidex uses cookies to ensure you get the best experience on our platform and to store your login status.
</p> </p>
<p> <p>
I understand and agree that Squidex has integrated Google Analytics (with the anonymizer function). Google Analytics is a web analytics service to gather and analyse data about the behavior of users. I understand and agree that Squidex has integrated Google Analytics (with the anonymizer function). Google Analytics is a web analytics service to gather and analyse data about the behavior of users.
</p> </p>
<p> <p>
I accept the <a href="@Model.PrivacyUrl" target="_blank" rel="noopener">privacy policies</a>. I accept the <a href="@Model.PrivacyUrl" target="_blank" rel="noopener">privacy policies</a>.
</p> </p>
</div>
</div> </div>
</div> </div>
</div> </div>
</label> </div>
</label>
<label for="consentToPersonalInformation"> <label for="consentToPersonalInformation">
<div class="card profile-section-sm @ErrorClass("ConsentToPersonalInformation")"> <div class="card profile-section-sm @ErrorClass("ConsentToPersonalInformation")">
<div class="card-body"> <div class="card-body">
<h4 class="card-title">Personal Information</h4> <h4 class="card-title">Personal Information</h4>
<div class="card-text row"> <div class="card-text row">
<div class="col col-auto"> <div class="col col-auto">
<input type="checkbox" id="consentToPersonalInformation" name="consentToPersonalInformation" value="True" /> <input type="checkbox" id="consentToPersonalInformation" name="consentToPersonalInformation" value="True" />
</div> </div>
<div class="col"> <div class="col">
I understand and agree that Squidex collects the following private information that are retrieved from external authentication providers such as Google, Microsoft or Github. I understand and agree that Squidex collects the following private information that are retrieved from external authentication providers such as Google, Microsoft or Github.
<ul class="personal-information"> <ul class="personal-information">
<li> <li>
Basic personal information (first name, last name and picture) are provided to all other users so that they can add you to their working space. Basic personal information (first name, last name and picture) are provided to all other users so that they can add you to their working space.
</li> </li>
<li> <li>
At anytime you have the option to change these information to anonymize your account. At anytime you have the option to change these information to anonymize your account.
</li> </li>
<li> <li>
Your user account has an unique identifier and for all your changes we track, that you made these changes and provide this information to other users. Your user account has an unique identifier and for all your changes we track, that you made these changes and provide this information to other users.
</li> </li>
</ul> </ul>
</div>
</div> </div>
</div> </div>
</div> </div>
</label>
<div class="profile-section-sm text-right">
<button type="submit" class="btn btn-success">I agree!</button>
</div> </div>
</label>
<div class="profile-section-sm text-right">
<button type="submit" class="btn btn-success">I agree!</button>
</div> </div>
</form> </form>

147
src/Squidex/Areas/IdentityServer/Views/Account/Login.cshtml

@ -3,96 +3,91 @@
@{ @{
var type = Model.IsLogin ? "Login" : "Signup"; var type = Model.IsLogin ? "Login" : "Signup";
ViewBag.Theme = "white";
ViewBag.Title = type; ViewBag.Title = type;
} }
<div class="profile">
<img class="profile-logo" src="~/images/logo-small.png" />
<div class="container"> <div class="container">
<div class="row text-center"> <div class="row text-center">
<div class="btn-group profile-headline"> <div class="btn-group profile-headline">
@if (Model.IsLogin) @if (Model.IsLogin)
{ {
<a class="btn btn-toggle btn-primary" asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">Login</a> <a class="btn btn-toggle btn-primary" asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">Login</a>
} }
else else
{ {
<a class="btn btn-toggle" asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">Login</a> <a class="btn btn-toggle" asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">Login</a>
} }
@if (!Model.IsLogin) @if (!Model.IsLogin)
{ {
<a class="btn btn-toggle btn-primary" asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">Signup</a> <a class="btn btn-toggle btn-primary" asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">Signup</a>
} }
else else
{ {
<a class="btn btn-toggle" asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">Signup</a> <a class="btn btn-toggle" asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">Signup</a>
} }
</div>
</div> </div>
</div> </div>
</div>
<form asp-controller="Account" asp-action="External" asp-route-returnurl="@Model.ReturnUrl" method="post"> <form asp-controller="Account" asp-action="External" asp-route-returnurl="@Model.ReturnUrl" method="post">
@foreach (var provider in Model.ExternalProviders) @foreach (var provider in Model.ExternalProviders)
{
var schema = provider.AuthenticationScheme.ToLowerInvariant();
<div class="form-group">
<button class="btn external-button btn-block btn btn-@schema" type="submit" name="provider" value="@provider.AuthenticationScheme">
<i class="icon-@schema external-icon"></i> @type with <strong>@provider.AuthenticationScheme</strong>
</button>
</div>
}
</form>
@if (Model.HasPasswordAndExternal)
{ {
<div class="profile-separator"> var schema = provider.AuthenticationScheme.ToLowerInvariant();
<div class="profile-separator-text">OR</div>
<div class="form-group">
<button class="btn external-button btn-block btn btn-@schema" type="submit" name="provider" value="@provider.AuthenticationScheme">
<i class="icon-@schema external-icon"></i> @type with <strong>@provider.AuthenticationScheme</strong>
</button>
</div> </div>
} }
</form>
@if (Model.HasPasswordAuth) @if (Model.HasPasswordAndExternal)
{ {
if (Model.IsLogin) <div class="profile-separator">
{ <div class="profile-separator-text">OR</div>
if (Model.IsFailed) </div>
{ }
<div class="form-alert form-alert-error">Email or password not correct.</div>
}
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl" method="post"> @if (Model.HasPasswordAuth)
<div class="form-group"> {
<input type="email" class="form-control" name="email" id="email" placeholder="Enter Email" /> if (Model.IsLogin)
</div> {
if (Model.IsFailed)
{
<div class="form-alert form-alert-error">Email or password not correct.</div>
}
<div class="form-group"> <form asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl" method="post">
<input type="password" class="form-control" name="password" id="password" placeholder="Enter Password" /> <div class="form-group">
</div> <input type="email" class="form-control" name="email" id="email" placeholder="Enter Email" />
</div>
<button type="submit" class="btn btn-block btn-primary">@type</button> <div class="form-group">
</form> <input type="password" class="form-control" name="password" id="password" placeholder="Enter Password" />
} </div>
else
{
<div class="profile-password-signup text-center">Ask your administrator to create an account.</div>
}
}
@if (Model.IsLogin) <button type="submit" class="btn btn-block btn-primary">@type</button>
{ </form>
<p class="profile-footer"> }
No account yet? <a asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">Click here to signup</a> else
</p> {
} <div class="profile-password-signup text-center">Ask your administrator to create an account.</div>
else }
{ }
<p class="profile-footer">
Already registered? <a asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">Click here to login</a> @if (Model.IsLogin)
</p> {
} <p class="profile-footer">
</div> No account yet? <a asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">Click here to signup</a>
</p>
}
else
{
<p class="profile-footer">
Already registered? <a asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">Click here to login</a>
</p>
}
@if (!Model.HasPasswordAuth && Model.ExternalProviders.Count == 1) @if (!Model.HasPasswordAuth && Model.ExternalProviders.Count == 1)
{ {

1
src/Squidex/Areas/IdentityServer/Views/Account/LogoutCompleted.cshtml

@ -1,4 +1,5 @@
@{ @{
ViewBag.Theme = "white";
ViewBag.Title = "Logout"; ViewBag.Title = "Logout";
} }

18
src/Squidex/Areas/IdentityServer/Views/Error/Error.cshtml

@ -1,9 +1,19 @@
@{ @model Squidex.Areas.IdentityServer.Controllers.Error.ErrorViewModel
@{
ViewBag.Theme = "white";
ViewBag.Title = "Operation failed"; ViewBag.Title = "Operation failed";
} }
<h1 class="splash-h1">Operation failed</h1> <h1 class="splash-h1">Operation failed</h1>
<p class="splash-text"> <p class="splash-text">
We are really sorry that something went wrong. @if (Model.Error?.ErrorDescription != null)
</p> {
@Model.Error.ErrorDescription
}
else
{
<span>We are really sorry that something went wrong.</span>
}
</p>

359
src/Squidex/Areas/IdentityServer/Views/Profile/Profile.cshtml

@ -1,223 +1,218 @@
@model Squidex.Areas.IdentityServer.Controllers.Profile.ProfileVM @model Squidex.Areas.IdentityServer.Controllers.Profile.ProfileVM
@{ @{
ViewBag.Theme = "white";
ViewBag.Title = "Profile"; ViewBag.Title = "Profile";
} }
<div class="profile profile-lg">
<img class="profile-logo" src="~/images/logo-small.png" />
<h1>Edit Profile</h1> <h1>Edit Profile</h1>
<h2>Personal Information</h2> <h2>Personal Information</h2>
@if (!string.IsNullOrWhiteSpace(Model.SuccessMessage)) @if (!string.IsNullOrWhiteSpace(Model.SuccessMessage))
{ {
<div class="form-alert form-alert-success" id="success"> <div class="form-alert form-alert-success" id="success">
@Model.SuccessMessage @Model.SuccessMessage
</div> </div>
} }
@if (!string.IsNullOrWhiteSpace(Model.ErrorMessage)) @if (!string.IsNullOrWhiteSpace(Model.ErrorMessage))
{ {
<div class="form-alert form-alert-error"> <div class="form-alert form-alert-error">
@Model.ErrorMessage @Model.ErrorMessage
</div> </div>
} }
<div class="row profile-section"> <div class="row profile-section">
<div class="col profile-picture-col"> <div class="col profile-picture-col">
<img class="profile-picture" src="@Url.Content($"~/../api/users/{Model.Id}/picture/")" /> <img class="profile-picture" src="@Url.Content($"~/../api/users/{Model.Id}/picture/")" />
</div>
<div class="col">
<form id="pictureForm" class="profile-picture-form" asp-controller="Profile" asp-action="UploadPicture" method="post" enctype="multipart/form-data">
<span class="btn btn-secondary" id="pictureButton">
<span>Upload Picture</span>
<input class="profile-picture-input" name="file" type="file" id="pictureInput" />
</span>
</form>
</div>
</div> </div>
<div class="col">
<form id="pictureForm" class="profile-picture-form" asp-controller="Profile" asp-action="UploadPicture" method="post" enctype="multipart/form-data">
<span class="btn btn-secondary" id="pictureButton">
<span>Upload Picture</span>
<input class="profile-picture-input" name="file" type="file" id="pictureInput" />
</span>
</form>
</div>
</div>
<form class="profile-form profile-section" asp-controller="Profile" asp-action="UpdateProfile" method="post"> <form class="profile-form profile-section" asp-controller="Profile" asp-action="UpdateProfile" method="post">
<div class="form-group"> <div class="form-group">
<label for="email">Email</label> <label for="email">Email</label>
@if (ViewContext.ViewData.ModelState["Email"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid) @if (ViewContext.ViewData.ModelState["Email"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
{ {
<div class="errors-container"> <div class="errors-container">
<span asp-validation-for="Email" class="errors"></span> <span asp-validation-for="Email" class="errors"></span>
</div> </div>
} }
<input type="email" ap class="form-control" asp-for="Email" id="email" /> <input type="email" ap class="form-control" asp-for="Email" id="email" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="displayName">Display Name</label> <label for="displayName">Display Name</label>
@if (ViewContext.ViewData.ModelState["DisplayName"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid) @if (ViewContext.ViewData.ModelState["DisplayName"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
{ {
<div class="errors-container"> <div class="errors-container">
<span asp-validation-for="DisplayName" class="errors"></span> <span asp-validation-for="DisplayName" class="errors"></span>
</div> </div>
} }
<input type="text" class="form-control" asp-for="DisplayName" id="displayName"/> <input type="text" class="form-control" asp-for="DisplayName" id="displayName"/>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="IsHidden" id="hidden" /> <input type="checkbox" class="form-check-input" asp-for="IsHidden" id="hidden" />
<label class="form-check-label" for="hidden">Do not show my profile to other users</label> <label class="form-check-label" for="hidden">Do not show my profile to other users</label>
</div>
</div> </div>
</div>
<button type="submit" class="btn btn-primary">Save</button> <button type="submit" class="btn btn-primary">Save</button>
</form> </form>
@if (Model.ExternalProviders.Any())
{
<div class="profile-section">
<h2>Logins</h2>
<table class="table table-fixed table-lesspadding">
<colgroup>
<col style="width: 100px;"/>
<col style="width: 100%;"/>
<col style="width: 100px;"/>
</colgroup>
@foreach (var login in Model.ExternalLogins)
{
<tr>
<td>
<span>@login.LoginProvider</span>
</td>
<td>
<span class="truncate">@login.ProviderDisplayName</span>
</td>
<td class="text-right">
@if (Model.ExternalLogins.Count > 1 || Model.HasPassword)
{
<form asp-controller="Profile" asp-action="RemoveLogin" method="post">
<input type="hidden" value="@login.LoginProvider" name="LoginProvider"/>
<input type="hidden" value="@login.ProviderKey" name="ProviderKey"/>
<button type="submit" class="btn btn-text-danger btn-sm">
Remove
</button>
</form>
}
</td>
</tr>
}
</table>
<form asp-controller="Profile" asp-action="AddLogin" method="post">
@foreach (var provider in Model.ExternalProviders.Where(x => Model.ExternalLogins.All(y => x.AuthenticationScheme != y.LoginProvider)))
{
var schema = provider.AuthenticationScheme.ToLowerInvariant();
<button class="btn external-button-small btn-@schema" type="submit" name="provider" value="@provider.AuthenticationScheme">
<i class="icon-@schema external-icon"></i>
</button>
}
</form>
</div>
}
@if (Model.HasPasswordAuth) @if (Model.ExternalProviders.Any())
{ {
<div class="profile-section"> <div class="profile-section">
<h2>Password</h2> <h2>Logins</h2>
@if (Model.HasPassword) <table class="table table-fixed table-lesspadding">
<colgroup>
<col style="width: 100px;"/>
<col style="width: 100%;"/>
<col style="width: 100px;"/>
</colgroup>
@foreach (var login in Model.ExternalLogins)
{ {
<form class="profile-form" asp-controller="Profile" asp-action="ChangePassword" method="post"> <tr>
<div class="form-group"> <td>
<label for="oldPassword">Old Password</label> <span>@login.LoginProvider</span>
</td>
@if (ViewContext.ViewData.ModelState["OldPassword"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid) <td>
<span class="truncate">@login.ProviderDisplayName</span>
</td>
<td class="text-right">
@if (Model.ExternalLogins.Count > 1 || Model.HasPassword)
{ {
<div class="errors-container"> <form asp-controller="Profile" asp-action="RemoveLogin" method="post">
<span class="errors">@Html.ValidationMessage("OldPassword")</span> <input type="hidden" value="@login.LoginProvider" name="LoginProvider"/>
</div> <input type="hidden" value="@login.ProviderKey" name="ProviderKey"/>
<button type="submit" class="btn btn-text-danger btn-sm">
Remove
</button>
</form>
} }
</td>
</tr>
}
</table>
<input type="password" class="form-control" name="oldPassword" id="oldPassword" /> <form asp-controller="Profile" asp-action="AddLogin" method="post">
</div> @foreach (var provider in Model.ExternalProviders.Where(x => Model.ExternalLogins.All(y => x.AuthenticationScheme != y.LoginProvider)))
{
<div class="form-group"> var schema = provider.AuthenticationScheme.ToLowerInvariant();
<label for="password">Password</label>
@if (ViewContext.ViewData.ModelState["Password"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid) <button class="btn external-button-small btn-@schema" type="submit" name="provider" value="@provider.AuthenticationScheme">
{ <i class="icon-@schema external-icon"></i>
<div class="errors-container"> </button>
<span class="errors">@Html.ValidationMessage("Password")</span> }
</div> </form>
} </div>
}
@if (Model.HasPasswordAuth)
{
<div class="profile-section">
<h2>Password</h2>
@if (Model.HasPassword)
{
<form class="profile-form" asp-controller="Profile" asp-action="ChangePassword" method="post">
<div class="form-group">
<label for="oldPassword">Old Password</label>
@if (ViewContext.ViewData.ModelState["OldPassword"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
{
<div class="errors-container">
<span class="errors">@Html.ValidationMessage("OldPassword")</span>
</div>
}
<input type="password" class="form-control" name="oldPassword" id="oldPassword" />
</div>
<input type="password" class="form-control" name="password" id="password" /> <div class="form-group">
</div> <label for="password">Password</label>
<div class="form-group"> @if (ViewContext.ViewData.ModelState["Password"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
<label for="passwordConfirm">Confirm</label> {
<div class="errors-container">
<span class="errors">@Html.ValidationMessage("Password")</span>
</div>
}
@if (ViewContext.ViewData.ModelState["PasswordConfirm"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid) <input type="password" class="form-control" name="password" id="password" />
{ </div>
<div class="errors-container">
<span class="errors">@Html.ValidationMessage("PasswordConfirm")</span>
</div>
}
<input type="password" class="form-control" name="passwordConfirm" id="passwordConfirm" /> <div class="form-group">
</div> <label for="passwordConfirm">Confirm</label>
<div class="form-group"> @if (ViewContext.ViewData.ModelState["PasswordConfirm"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
<button type="submit" class="btn btn-primary">Change Password</button> {
</div> <div class="errors-container">
</form> <span class="errors">@Html.ValidationMessage("PasswordConfirm")</span>
} </div>
else }
{
<form class="profile-form" asp-controller="Profile" asp-action="SetPassword" method="post">
<div class="form-group">
<label for="password">Password</label>
@if (ViewContext.ViewData.ModelState["Password"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid) <input type="password" class="form-control" name="passwordConfirm" id="passwordConfirm" />
{ </div>
<div class="errors-container">
<span class="errors">@Html.ValidationMessage("Password")</span>
</div>
}
<input type="password" class="form-control" name="password" id="password" /> <div class="form-group">
</div> <button type="submit" class="btn btn-primary">Change Password</button>
</div>
</form>
}
else
{
<form class="profile-form" asp-controller="Profile" asp-action="SetPassword" method="post">
<div class="form-group">
<label for="password">Password</label>
@if (ViewContext.ViewData.ModelState["Password"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
{
<div class="errors-container">
<span class="errors">@Html.ValidationMessage("Password")</span>
</div>
}
<input type="password" class="form-control" name="password" id="password" />
</div>
<div class="form-group"> <div class="form-group">
<label for="passwordConfirm">Confirm</label> <label for="passwordConfirm">Confirm</label>
@if (ViewContext.ViewData.ModelState["PasswordConfirm"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid) @if (ViewContext.ViewData.ModelState["PasswordConfirm"]?.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
{ {
<div class="errors-container"> <div class="errors-container">
<span class="errors">@Html.ValidationMessage("PasswordConfirm")</span> <span class="errors">@Html.ValidationMessage("PasswordConfirm")</span>
</div> </div>
} }
<input type="password" class="form-control" name="passwordConfirm" id="passwordConfirm" /> <input type="password" class="form-control" name="passwordConfirm" id="passwordConfirm" />
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary">Set Password</button> <button type="submit" class="btn btn-primary">Set Password</button>
</div> </div>
</form> </form>
} }
</div> </div>
} }
</div>
<script> <script>
var pictureButton = document.getElementById('pictureButton'); var pictureButton = document.getElementById('pictureButton');

10
src/Squidex/Areas/IdentityServer/Views/_Layout.cshtml

@ -17,9 +17,13 @@
@RenderSection("header") @RenderSection("header")
} }
</head> </head>
<body class="@ViewBag.Theme"> <body class="white">
@RenderBody() <div class="profile @ViewBag.Class">
<img class="profile-logo" src="~/images/logo-small.png" />
@RenderBody()
</div>
<environment names="Development"> <environment names="Development">
<script type="text/javascript" src="http://localhost:3000/shims.js"></script> <script type="text/javascript" src="http://localhost:3000/shims.js"></script>
<script type="text/javascript" src="http://localhost:3000/app.js"></script> <script type="text/javascript" src="http://localhost:3000/app.js"></script>

11
src/Squidex/Config/Authentication/GithubHandler.cs

@ -6,14 +6,20 @@
// ========================================================================== // ==========================================================================
using System.Security.Claims; using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.Extensions.Options;
using Squidex.Infrastructure;
using Squidex.Shared.Identity; using Squidex.Shared.Identity;
namespace Squidex.Config.Authentication namespace Squidex.Config.Authentication
{ {
public sealed class GithubHandler : OAuthEvents public sealed class GithubHandler : OAuthEvents
{ {
private const string NoEmail = "Your email address is set to private in Github. Please set it to public to use Github login.";
public override Task CreatingTicket(OAuthCreatingTicketContext context) public override Task CreatingTicket(OAuthCreatingTicketContext context)
{ {
var nameClaim = context.Identity.FindFirst(ClaimTypes.Name)?.Value; var nameClaim = context.Identity.FindFirst(ClaimTypes.Name)?.Value;
@ -23,6 +29,11 @@ namespace Squidex.Config.Authentication
context.Identity.SetDisplayName(nameClaim); context.Identity.SetDisplayName(nameClaim);
} }
if (string.IsNullOrWhiteSpace(context.Identity.FindFirst(ClaimTypes.Email)?.Value))
{
throw new DomainException(NoEmail);
}
return base.CreatingTicket(context); return base.CreatingTicket(context);
} }
} }

Loading…
Cancel
Save