commit
489a321763
|
@ -21,7 +21,7 @@
|
||||||
<div class="Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
<div class="Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
||||||
@if (_allowexternallogin)
|
@if (_allowexternallogin)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">Use @PageState.Site.Settings["OpenIdConnectOptions:Provider"]</button>
|
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">Use @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
}
|
}
|
||||||
@if (_allowsitelogin)
|
@if (_allowsitelogin)
|
||||||
|
@ -95,12 +95,12 @@
|
||||||
{
|
{
|
||||||
_togglepassword = Localizer["ShowPassword"];
|
_togglepassword = Localizer["ShowPassword"];
|
||||||
|
|
||||||
if (PageState.Site.Settings.ContainsKey("AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["AllowSiteLogin"]))
|
if (PageState.Site.Settings.ContainsKey("ExternalLogin:AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["ExternalLogin:AllowSiteLogin"]))
|
||||||
{
|
{
|
||||||
_allowsitelogin = bool.Parse(PageState.Site.Settings["AllowSiteLogin"]);
|
_allowsitelogin = bool.Parse(PageState.Site.Settings["ExternalLogin:AllowSiteLogin"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageState.Site.Settings.ContainsKey("OpenIdConnectOptions:Provider") && !string.IsNullOrEmpty(PageState.Site.Settings["OpenIdConnectOptions:Provider"]))
|
if (PageState.Site.Settings.ContainsKey("ExternalLogin:ProviderType") && !string.IsNullOrEmpty(PageState.Site.Settings["ExternalLogin:ProviderType"]))
|
||||||
{
|
{
|
||||||
_allowexternallogin = true;
|
_allowexternallogin = true;
|
||||||
}
|
}
|
||||||
|
@ -269,7 +269,7 @@
|
||||||
|
|
||||||
private void ExternalLogin()
|
private void ExternalLogin()
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/oidc?returnurl=" + _returnUrl), true);
|
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ else
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="allowregistration" HelpText="Do You Want To Allow Visitors To Be Able To Register For A User Account On This Site?" ResourceKey="AllowRegistration">Allow User Registration? </Label>
|
<Label Class="col-sm-3" For="allowregistration" HelpText="Do You Want To Allow Visitors To Be Able To Register For A User Account On This Site?" ResourceKey="AllowRegistration">Allow User Registration?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="allowregistration" class="form-select" @bind="@_allowregistration" required>
|
<select id="allowregistration" class="form-select" @bind="@_allowregistration" required>
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
@ -104,7 +104,7 @@ else
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="requirepunctuation" HelpText="Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)" ResourceKey="RequirePunctuation">Require Punctuation?</Label>
|
<Label Class="col-sm-3" For="requirepunctuation" HelpText="Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)" ResourceKey="RequirePunctuation">Require Punctuation?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
|
@ -131,62 +131,129 @@ else
|
||||||
</Section>
|
</Section>
|
||||||
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="provider" HelpText="The OpenID Connect Provider Name. This Name Will Be Displayed On The Login Page" ResourceKey="Provider">Provider:</Label>
|
<Label Class="col-sm-3" For="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="provider" class="form-control" @bind="@_provider" />
|
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
||||||
</div>
|
<option value="" selected>@Localizer["Not Specified"]</option>
|
||||||
</div>
|
<option value="oidc">@Localizer["OpenID Connect"]</option>
|
||||||
<div class="row mb-1 align-items-center">
|
<option value="oauth2">@Localizer["OAuth 2.0"]</option>
|
||||||
<Label Class="col-sm-3" For="authority" HelpText="The Authority Or Issuer URL Associated With The OpenID Connect Provider. " ResourceKey="Authority">Authority:</Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="authority" class="form-control" @bind="@_authority" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="clientid" HelpText="The OpenID Connect Client ID" ResourceKey="ClientID">Client ID:</Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="clientid" class="form-control" @bind="@_clientid" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="clientsecret" HelpText="The OpenID Connect Client Secret" ResourceKey="ClientSecret">Client Secret:</Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="clientsecret" class="form-control" @bind="@_clientsecret" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="redirecturl" HelpText="The Redirect Url (or Callback Url) Which May Need To Be Registered With The OpenID Connect Provider" ResourceKey="RedirectUrl">Redirect Url:</Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The Type Name For The Email Address Claim Provided By The OpenID Connect Provider" ResourceKey="EmailClaimType">Email Claim Type:</Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="metadata" HelpText="The Discovery Endpoint For Obtaining Metadata. Only Specify If The OpenID Connect Provider Does Not Use The Standard Approach (ie. /.well-known/openid-configuration)" ResourceKey="Metadata">Metadata Address:</Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="metadata" class="form-control" @bind="@_metadata" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="logouturl" HelpText="The Url For Logging Out The User From The OpenID Connect Provider. Only Specify If The OpenID Connect Provider Supports This Feature And You Do Not Want The User To Remain Signed In To The OpenID Connect Provider After Logging Out From The Site." ResourceKey="LogoutUrl">Logout Url:</Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="logouturl" class="form-control" @bind="@_logouturl" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do You Want To Allow Users To Sign In Using A Username And Password That Is Managed Locally On This Site? Note That You Should Only Disable This Option If You Have Already Sucessfully Configured An External Login Provider, Or Else You May Lock Yourself Out Of This Site." ResourceKey="AllowSiteLogin">Allow Site Login? </Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin" required>
|
|
||||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
|
||||||
<option value="false">@SharedLocalizer["No"]</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@if (_providertype != "")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="providername" HelpText="The external login provider name which will be displayed on the login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="providername" class="form-control" @bind="@_providername" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (_providertype == "oidc")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="authority" HelpText="The Authority Url or Issuer Url associated with the OpenID Connect provider" ResourceKey="Authority">Authority:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="authority" class="form-control" @bind="@_authority" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="metadataurl" HelpText="The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)" ResourceKey="MetadataUrl">Metadata Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="metadataurl" class="form-control" @bind="@_metadataurl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (_providertype == "oauth2")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="authorizationurl" HelpText="The endpoint for obtaining an Authorization Code" ResourceKey="AuthorizationUrl">Authorization Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="authorizationurl" class="form-control" @bind="@_authorizationurl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="tokenurl" HelpText="The endpoint for obtaining an Auth Token" ResourceKey="TokenUrl">Token Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="tokenurl" class="form-control" @bind="@_tokenurl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="userinfourl" HelpText="The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address." ResourceKey="UserInfoUrl">User Info Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="userinfourl" class="form-control" @bind="@_userinfourl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (_providertype != "")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="clientid" HelpText="The Client ID from the provider" ResourceKey="ClientID">Client ID:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="clientid" class="form-control" @bind="@_clientid" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="clientsecret" HelpText="The Client Secret from the provider" ResourceKey="ClientSecret">Client Secret:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="password" id="clientsecret" class="form-control" @bind="@_clientsecret" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="pkce" class="form-select" @bind="@_pkce" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="redirecturl" HelpText="The Redirect Url (or Callback Url) which usually needs to be registered with the provider" ResourceKey="RedirectUrl">Redirect Url:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (_providertype == "oidc")
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The type name for the email address claim provided by the provider" ResourceKey="EmailClaimType">Email Claim Type:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="domainfilter" class="form-control" @bind="@_domainfilter" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="createusers" HelpText="Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login." ResourceKey="CreateUsers">Create New Users?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="createusers" class="form-select" @bind="@_createusers">
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Site Login?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin">
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</Section>
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
@ -209,14 +276,22 @@ else
|
||||||
private string _requirepunctuation;
|
private string _requirepunctuation;
|
||||||
private string _maximumfailures;
|
private string _maximumfailures;
|
||||||
private string _lockoutduration;
|
private string _lockoutduration;
|
||||||
private string _provider;
|
|
||||||
|
private string _providertype;
|
||||||
|
private string _providername;
|
||||||
private string _authority;
|
private string _authority;
|
||||||
|
private string _metadataurl;
|
||||||
|
private string _authorizationurl;
|
||||||
|
private string _tokenurl;
|
||||||
|
private string _userinfourl;
|
||||||
private string _clientid;
|
private string _clientid;
|
||||||
private string _clientsecret;
|
private string _clientsecret;
|
||||||
|
private string _scopes;
|
||||||
|
private string _pkce;
|
||||||
private string _redirecturl;
|
private string _redirecturl;
|
||||||
private string _emailclaimtype;
|
private string _emailclaimtype;
|
||||||
private string _metadata;
|
private string _domainfilter;
|
||||||
private string _logouturl;
|
private string _createusers;
|
||||||
private string _allowsitelogin;
|
private string _allowsitelogin;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
@ -229,6 +304,7 @@ else
|
||||||
|
|
||||||
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
|
|
||||||
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
||||||
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
||||||
_requiredigit = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireDigit", "true");
|
_requiredigit = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireDigit", "true");
|
||||||
|
@ -237,15 +313,23 @@ else
|
||||||
_requirepunctuation = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", "true");
|
_requirepunctuation = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", "true");
|
||||||
_maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5");
|
_maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5");
|
||||||
_lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString();
|
_lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString();
|
||||||
_provider = SettingService.GetSetting(settings, "OpenIdConnectOptions:Provider", "");
|
|
||||||
_authority = SettingService.GetSetting(settings, "OpenIdConnectOptions:Authority", "");
|
_providertype = SettingService.GetSetting(settings, "ExternalLogin:ProviderType", "");
|
||||||
_clientid = SettingService.GetSetting(settings, "OpenIdConnectOptions:ClientId", "");
|
_providername = SettingService.GetSetting(settings, "ExternalLogin:ProviderName", "");
|
||||||
_clientsecret = SettingService.GetSetting(settings, "OpenIdConnectOptions:ClientSecret", "");
|
_authority = SettingService.GetSetting(settings, "ExternalLogin:Authority", "");
|
||||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-oidc";
|
_metadataurl = SettingService.GetSetting(settings, "ExternalLogin:MetadataUrl", "");
|
||||||
_emailclaimtype = SettingService.GetSetting(settings, "OpenIdConnectOptions:EmailClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
|
_authorizationurl = SettingService.GetSetting(settings, "ExternalLogin:AuthorizationUrl", "");
|
||||||
_metadata = SettingService.GetSetting(settings, "OpenIdConnectOptions:MetadataAddress", "");
|
_tokenurl = SettingService.GetSetting(settings, "ExternalLogin:TokenUrl", "");
|
||||||
_logouturl = SettingService.GetSetting(settings, "OpenIdConnectOptions:LogoutUrl", "");
|
_userinfourl = SettingService.GetSetting(settings, "ExternalLogin:UserInfoUrl", "");
|
||||||
_allowsitelogin = SettingService.GetSetting(settings, "AllowSiteLogin", "true");
|
_clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", "");
|
||||||
|
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||||
|
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||||
|
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||||
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
|
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
|
||||||
|
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
||||||
|
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
||||||
|
_allowsitelogin = SettingService.GetSetting(settings, "ExternalLogin:AllowSiteLogin", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<UserRole> Search(string search)
|
private List<UserRole> Search(string search)
|
||||||
|
@ -324,14 +408,23 @@ else
|
||||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", _requirepunctuation, true);
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", _requirepunctuation, true);
|
||||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, true);
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, true);
|
||||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true);
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true);
|
||||||
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:Provider", _provider, false);
|
|
||||||
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:Authority", _authority, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderType", _providertype, false);
|
||||||
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:ClientId", _clientid, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderName", _providername, false);
|
||||||
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:ClientSecret", _clientsecret, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Authority", _authority, true);
|
||||||
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:EmailClaimType", _emailclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:MetadataUrl", _metadataurl, true);
|
||||||
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:MetadataAddress", _metadata, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:AuthorizationUrl", _authorizationurl, true);
|
||||||
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:LogoutUrl", _logouturl, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:TokenUrl", _tokenurl, true);
|
||||||
settings = SettingService.SetSetting(settings, "AllowSiteLogin", _allowsitelogin, false);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:UserInfoUrl", _userinfourl, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:AllowSiteLogin", _allowsitelogin, false);
|
||||||
|
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||||
await SettingService.ClearSiteSettingsCacheAsync(site.SiteId);
|
await SettingService.ClearSiteSettingsCacheAsync(site.SiteId);
|
||||||
|
|
||||||
|
@ -343,4 +436,19 @@ else
|
||||||
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ProviderTypeChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
_providertype = (string)e.Value;
|
||||||
|
if (_providertype == "oidc")
|
||||||
|
{
|
||||||
|
_scopes = "openid,profile,email";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_scopes = "";
|
||||||
|
}
|
||||||
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,43 +154,43 @@
|
||||||
<value>Roles</value>
|
<value>Roles</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockoutDuration.HelpText" xml:space="preserve">
|
<data name="LockoutDuration.HelpText" xml:space="preserve">
|
||||||
<value>The Number Of Minutes A User Should Be Locked Out</value>
|
<value>The number of minutes a user should be locked out</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockoutDuration.Text" xml:space="preserve">
|
<data name="LockoutDuration.Text" xml:space="preserve">
|
||||||
<value>Lockout Duration:</value>
|
<value>Lockout Duration:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumFailures.HelpText" xml:space="preserve">
|
<data name="MaximumFailures.HelpText" xml:space="preserve">
|
||||||
<value>The Maximum Number Of Sign In Attempts Before A User Is Locked Out</value>
|
<value>The maximum number of sign in attempts before a user is locked out</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumFailures.Text" xml:space="preserve">
|
<data name="MaximumFailures.Text" xml:space="preserve">
|
||||||
<value>Maximum Failures:</value>
|
<value>Maximum Failures:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequireDigit.HelpText" xml:space="preserve">
|
<data name="RequireDigit.HelpText" xml:space="preserve">
|
||||||
<value>Indicate If Passwords Must Contain A Digit</value>
|
<value>Indicate if passwords must contain a digit</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequireDigit.Text" xml:space="preserve">
|
<data name="RequireDigit.Text" xml:space="preserve">
|
||||||
<value>Require Digit?</value>
|
<value>Require Digit?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequiredLength.HelpText" xml:space="preserve">
|
<data name="RequiredLength.HelpText" xml:space="preserve">
|
||||||
<value>The Minimum Length For A Password</value>
|
<value>The minimum length for a password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequiredLength.Text" xml:space="preserve">
|
<data name="RequiredLength.Text" xml:space="preserve">
|
||||||
<value>Minimum Length:</value>
|
<value>Minimum Length:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequireLower.HelpText" xml:space="preserve">
|
<data name="RequireLower.HelpText" xml:space="preserve">
|
||||||
<value>Indicate If Passwords Must Contain A Lower Case Character</value>
|
<value>Indicate if passwords must contain a lower case character</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequireLower.Text" xml:space="preserve">
|
<data name="RequireLower.Text" xml:space="preserve">
|
||||||
<value>Require Lowercase?</value>
|
<value>Require Lowercase?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequirePunctuation.HelpText" xml:space="preserve">
|
<data name="RequirePunctuation.HelpText" xml:space="preserve">
|
||||||
<value>Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)</value>
|
<value>Indicate if passwords must contain a non-alphanumeric character (ie. punctuation)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequirePunctuation.Text" xml:space="preserve">
|
<data name="RequirePunctuation.Text" xml:space="preserve">
|
||||||
<value>Require Punctuation?</value>
|
<value>Require Punctuation?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequireUpper.HelpText" xml:space="preserve">
|
<data name="RequireUpper.HelpText" xml:space="preserve">
|
||||||
<value>Indicate If Passwords Must Contain An Upper Case Character</value>
|
<value>Indicate if passwords must contain an upper case character</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RequireUpper.Text" xml:space="preserve">
|
<data name="RequireUpper.Text" xml:space="preserve">
|
||||||
<value>Require Uppercase?</value>
|
<value>Require Uppercase?</value>
|
||||||
|
@ -199,9 +199,114 @@
|
||||||
<value>Configuration Updated. Please Select Restart Application For These Changes To Be Activated.</value>
|
<value>Configuration Updated. Please Select Restart Application For These Changes To Be Activated.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UniqueCharacters.HelpText" xml:space="preserve">
|
<data name="UniqueCharacters.HelpText" xml:space="preserve">
|
||||||
<value>The Minimum Number Of Unique Characters Which A Password Must Contain</value>
|
<value>The minimum number of unique characters which a password must contain</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UniqueCharacters.Text" xml:space="preserve">
|
<data name="UniqueCharacters.Text" xml:space="preserve">
|
||||||
<value>Unique Characters:</value>
|
<value>Unique Characters:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AllowSiteLogin.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="AllowSiteLogin.Text" xml:space="preserve">
|
||||||
|
<value>Allow Site Login?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Authority.HelpText" xml:space="preserve">
|
||||||
|
<value>The Authority Url or Issuer Url associated with the OpenID Connect provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="Authority.Text" xml:space="preserve">
|
||||||
|
<value>Authority:</value>
|
||||||
|
</data>
|
||||||
|
<data name="AuthorizationUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The endpoint for obtaining an Authorization Code</value>
|
||||||
|
</data>
|
||||||
|
<data name="AuthorizationUrl.Text" xml:space="preserve">
|
||||||
|
<value>Authorization Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientID.HelpText" xml:space="preserve">
|
||||||
|
<value>The Client ID from the provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientID.Text" xml:space="preserve">
|
||||||
|
<value>Client ID:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientSecret.HelpText" xml:space="preserve">
|
||||||
|
<value>The Client Secret from the provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClientSecret.Text" xml:space="preserve">
|
||||||
|
<value>Client Secret:</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateUsers.HelpText" xml:space="preserve">
|
||||||
|
<value>Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateUsers.Text" xml:space="preserve">
|
||||||
|
<value>Create New Users?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DomainFilter.HelpText" xml:space="preserve">
|
||||||
|
<value>Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses.</value>
|
||||||
|
</data>
|
||||||
|
<data name="DomainFilter.Text" xml:space="preserve">
|
||||||
|
<value>Domain Filter:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
||||||
|
<value>The type name for the email address claim provided by the provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="EmailClaimType.Text" xml:space="preserve">
|
||||||
|
<value>Email Claim Type:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginSettings.Heading" xml:space="preserve">
|
||||||
|
<value>External Login Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="LockoutSettings.Heading" xml:space="preserve">
|
||||||
|
<value>Lockout Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="MetadataUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)</value>
|
||||||
|
</data>
|
||||||
|
<data name="MetadataUrl.Text" xml:space="preserve">
|
||||||
|
<value>Metadata Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordSettings.Heading" xml:space="preserve">
|
||||||
|
<value>Password Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="PKCE.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicate if the provider supports Proof Key for Code Exchange (PKCE)</value>
|
||||||
|
</data>
|
||||||
|
<data name="PKCE.Text" xml:space="preserve">
|
||||||
|
<value>Use PKCE?</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderName.HelpText" xml:space="preserve">
|
||||||
|
<value>The external login provider name which will be displayed on the login page</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderName.Text" xml:space="preserve">
|
||||||
|
<value>Provider Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderType.HelpText" xml:space="preserve">
|
||||||
|
<value>Select the external login provider type</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProviderType.Text" xml:space="preserve">
|
||||||
|
<value>Provider Type:</value>
|
||||||
|
</data>
|
||||||
|
<data name="RedirectUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The Redirect Url (or Callback Url) which usually needs to be registered with the provider</value>
|
||||||
|
</data>
|
||||||
|
<data name="RedirectUrl.Text" xml:space="preserve">
|
||||||
|
<value>Redirect Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Scopes.HelpText" xml:space="preserve">
|
||||||
|
<value>A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Scopes.Text" xml:space="preserve">
|
||||||
|
<value>Scopes:</value>
|
||||||
|
</data>
|
||||||
|
<data name="TokenUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The endpoint for obtaining an Auth Token</value>
|
||||||
|
</data>
|
||||||
|
<data name="TokenUrl.Text" xml:space="preserve">
|
||||||
|
<value>Token Url:</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserInfoUrl.HelpText" xml:space="preserve">
|
||||||
|
<value>The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address.</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserInfoUrl.Text" xml:space="preserve">
|
||||||
|
<value>User Info Url:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -15,6 +15,10 @@ using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Oqtane.Security;
|
using Oqtane.Security;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Oqtane.Extensions
|
namespace Oqtane.Extensions
|
||||||
{
|
{
|
||||||
|
@ -36,152 +40,236 @@ namespace Oqtane.Extensions
|
||||||
// site OpenIdConnect options
|
// site OpenIdConnect options
|
||||||
builder.AddSiteOptions<OpenIdConnectOptions>((options, alias) =>
|
builder.AddSiteOptions<OpenIdConnectOptions>((options, alias) =>
|
||||||
{
|
{
|
||||||
// default options
|
if (alias.SiteSettings.GetValue("ExternalLogin:ProviderType", "") == "oidc")
|
||||||
options.SignInScheme = Constants.AuthenticationScheme; // identity cookie
|
{
|
||||||
options.RequireHttpsMetadata = true;
|
// default options
|
||||||
options.SaveTokens = true;
|
options.SignInScheme = Constants.AuthenticationScheme; // identity cookie
|
||||||
options.GetClaimsFromUserInfoEndpoint = true;
|
options.RequireHttpsMetadata = true;
|
||||||
options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-oidc" : "/" + alias.Path + "/signin-oidc";
|
options.SaveTokens = true;
|
||||||
options.ResponseType = OpenIdConnectResponseType.Code; // authorization code flow
|
options.GetClaimsFromUserInfoEndpoint = true;
|
||||||
options.ResponseMode = OpenIdConnectResponseMode.FormPost; // recommended as most secure
|
options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-oidc" : "/" + alias.Path + "/signin-oidc";
|
||||||
options.UsePkce = true;
|
options.ResponseType = OpenIdConnectResponseType.Code; // authorization code flow
|
||||||
options.Scope.Add("openid"); // core claims
|
options.ResponseMode = OpenIdConnectResponseMode.FormPost; // recommended as most secure
|
||||||
options.Scope.Add("profile"); // name claims
|
|
||||||
options.Scope.Add("email"); // email claim
|
|
||||||
//options.Scope.Add("offline_access"); // refresh token
|
|
||||||
|
|
||||||
// cookie config is required to avoid Correlation Failed errors
|
// cookie config is required to avoid Correlation Failed errors
|
||||||
options.NonceCookie.SameSite = SameSiteMode.Unspecified;
|
options.NonceCookie.SameSite = SameSiteMode.Unspecified;
|
||||||
options.CorrelationCookie.SameSite = SameSiteMode.Unspecified;
|
options.CorrelationCookie.SameSite = SameSiteMode.Unspecified;
|
||||||
|
|
||||||
// site options
|
// site options
|
||||||
options.Authority = alias.SiteSettings.GetValue("OpenIdConnectOptions:Authority", options.Authority);
|
options.Authority = alias.SiteSettings.GetValue("ExternalLogin:Authority", "");
|
||||||
options.ClientId = alias.SiteSettings.GetValue("OpenIdConnectOptions:ClientId", options.ClientId);
|
options.MetadataAddress = alias.SiteSettings.GetValue("ExternalLogin:MetadataUrl", "");
|
||||||
options.ClientSecret = alias.SiteSettings.GetValue("OpenIdConnectOptions:ClientSecret", options.ClientSecret);
|
options.ClientId = alias.SiteSettings.GetValue("ExternalLogin:ClientId", "");
|
||||||
options.MetadataAddress = alias.SiteSettings.GetValue("OpenIdConnectOptions:MetadataAddress", options.MetadataAddress);
|
options.ClientSecret = alias.SiteSettings.GetValue("ExternalLogin:ClientSecret", "");
|
||||||
|
options.UsePkce = bool.Parse(alias.SiteSettings.GetValue("ExternalLogin:PKCE", "false"));
|
||||||
|
options.Scope.Clear();
|
||||||
|
foreach (var scope in alias.SiteSettings.GetValue("ExternalLogin:Scopes", "openid,profile,email").Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
options.Scope.Add(scope);
|
||||||
|
}
|
||||||
|
|
||||||
// openid connect events
|
// openid connect events
|
||||||
options.Events.OnTokenValidated = OnTokenValidated;
|
options.Events.OnTokenValidated = OnTokenValidated;
|
||||||
options.Events.OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut;
|
options.Events.OnAccessDenied = OnAccessDenied;
|
||||||
options.Events.OnAccessDenied = OnAccessDenied;
|
options.Events.OnRemoteFailure = OnRemoteFailure;
|
||||||
options.Events.OnRemoteFailure = OnRemoteFailure;
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// site ChallengeScheme options
|
// site OAuth2.0 options
|
||||||
builder.AddSiteOptions<AuthenticationOptions>((options, alias) =>
|
builder.AddSiteOptions<OAuthOptions>((options, alias) =>
|
||||||
{
|
{
|
||||||
if (alias.SiteSettings.GetValue("OpenIdConnectOptions:Authority", "") != "")
|
if (alias.SiteSettings.GetValue("ExternalLogin:ProviderType", "") == "oauth2")
|
||||||
{
|
{
|
||||||
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
|
// default options
|
||||||
|
options.SignInScheme = Constants.AuthenticationScheme; // identity cookie
|
||||||
|
options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-oauth2" : "/" + alias.Path + "/signin-oauth2";
|
||||||
|
options.SaveTokens = true;
|
||||||
|
|
||||||
|
// site options
|
||||||
|
options.AuthorizationEndpoint = alias.SiteSettings.GetValue("ExternalLogin:AuthorizationUrl", "");
|
||||||
|
options.TokenEndpoint = alias.SiteSettings.GetValue("ExternalLogin:TokenUrl", "");
|
||||||
|
options.UserInformationEndpoint = alias.SiteSettings.GetValue("ExternalLogin:UserInfoUrl", "");
|
||||||
|
options.ClientId = alias.SiteSettings.GetValue("ExternalLogin:ClientId", "");
|
||||||
|
options.ClientSecret = alias.SiteSettings.GetValue("ExternalLogin:ClientSecret", "");
|
||||||
|
options.UsePkce = bool.Parse(alias.SiteSettings.GetValue("ExternalLogin:PKCE", "false"));
|
||||||
|
options.Scope.Clear();
|
||||||
|
foreach (var scope in alias.SiteSettings.GetValue("ExternalLogin:Scopes", "").Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
options.Scope.Add(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cookie config is required to avoid Correlation Failed errors
|
||||||
|
options.CorrelationCookie.SameSite = SameSiteMode.Unspecified;
|
||||||
|
|
||||||
|
// oauth2 events
|
||||||
|
options.Events.OnCreatingTicket = OnCreatingTicket;
|
||||||
|
options.Events.OnAccessDenied = OnAccessDenied;
|
||||||
|
options.Events.OnRemoteFailure = OnRemoteFailure;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task OnCreatingTicket(OAuthCreatingTicketContext context)
|
||||||
|
{
|
||||||
|
// OAuth 2.0
|
||||||
|
var email = "";
|
||||||
|
if (context.Options.UserInformationEndpoint != "")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
|
||||||
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(Constants.PackageId, Constants.Version));
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
|
||||||
|
var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
var output = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// get email address using Regex on the raw output (could be json or html)
|
||||||
|
var regex = new Regex(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", RegexOptions.IgnoreCase);
|
||||||
|
foreach (Match match in regex.Matches(output))
|
||||||
|
{
|
||||||
|
if (EmailValid(match.Value, context.HttpContext.GetAlias().SiteSettings.GetValue("ExternalLogin:DomainFilter", "")))
|
||||||
|
{
|
||||||
|
email = match.Value.ToLower();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "An Error Occurred Accessing The User Info Endpoint - {Error}", ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// login user
|
||||||
|
await LoginUser(email, context.HttpContext, context.Principal);
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task OnTokenValidated(TokenValidatedContext context)
|
private static async Task OnTokenValidated(TokenValidatedContext context)
|
||||||
{
|
{
|
||||||
var providerKey = context.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
|
// OpenID Connect
|
||||||
var loginProvider = context.HttpContext.GetAlias().SiteSettings.GetValue("OpenIdConnectOptions:Authority", "");
|
var emailClaimType = context.HttpContext.GetAlias().SiteSettings.GetValue("ExternalLogin:EmailClaimType", "");
|
||||||
var emailClaimType = context.HttpContext.GetAlias().SiteSettings.GetValue("OpenIdConnectOptions:EmailClaimType", "");
|
|
||||||
if (string.IsNullOrEmpty(emailClaimType))
|
|
||||||
{
|
|
||||||
emailClaimType = ClaimTypes.Email;
|
|
||||||
}
|
|
||||||
var alias = context.HttpContext.GetAlias();
|
|
||||||
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogManager>();
|
|
||||||
|
|
||||||
// custom logic may be needed here to manipulate Principal sent by Provider - use interface similar to IClaimsTransformation
|
|
||||||
|
|
||||||
var email = context.Principal.FindFirstValue(emailClaimType);
|
var email = context.Principal.FindFirstValue(emailClaimType);
|
||||||
|
|
||||||
// validate email claim
|
// login user
|
||||||
if (email == null || !email.Contains("@") || !email.Contains("."))
|
await LoginUser(email, context.HttpContext, context.Principal);
|
||||||
{
|
}
|
||||||
var emailclaimtype = context.Principal.Claims.FirstOrDefault(item => item.Value.Contains("@") && item.Value.Contains("."));
|
|
||||||
if (emailclaimtype != null)
|
|
||||||
{
|
|
||||||
email = emailclaimtype.Value;
|
|
||||||
_logger.Log(LogLevel.Information, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "Please Update The Email Claim Type For The OpenID Connect Provider To {EmailClaimType} In Site Settings", emailclaimtype.Type);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
email = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (email != null)
|
private static Task OnAccessDenied(AccessDeniedContext context)
|
||||||
|
{
|
||||||
|
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||||
|
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External Login Access Denied - User May Have Cancelled Their External Login Attempt");
|
||||||
|
// redirect to login page
|
||||||
|
var alias = context.HttpContext.GetAlias();
|
||||||
|
context.Response.Redirect(alias.Path + "/login?returnurl=" + context.Properties.RedirectUri, true);
|
||||||
|
context.HandleResponse();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task OnRemoteFailure(RemoteFailureContext context)
|
||||||
|
{
|
||||||
|
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "External Login Remote Failure - {Error}", context.Failure.Message);
|
||||||
|
// redirect to login page
|
||||||
|
var alias = context.HttpContext.GetAlias();
|
||||||
|
context.Response.Redirect(alias.Path + "/login", true);
|
||||||
|
context.HandleResponse();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task LoginUser(string email, HttpContext httpContext, ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||||
|
var alias = httpContext.GetAlias();
|
||||||
|
|
||||||
|
if (EmailValid(email, alias.SiteSettings.GetValue("ExternalLogin:DomainFilter", "")))
|
||||||
{
|
{
|
||||||
var _identityUserManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
|
var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
|
||||||
var _users = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
|
var _users = httpContext.RequestServices.GetRequiredService<IUserRepository>();
|
||||||
var _userRoles = context.HttpContext.RequestServices.GetRequiredService<IUserRoleRepository>();
|
var _userRoles = httpContext.RequestServices.GetRequiredService<IUserRoleRepository>();
|
||||||
|
var providerType = httpContext.GetAlias().SiteSettings.GetValue("ExternalLogin:ProviderType", "");
|
||||||
|
var providerKey = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
if (providerKey == null)
|
||||||
|
{
|
||||||
|
providerKey = email; // OAuth2 does not pass claims
|
||||||
|
}
|
||||||
User user = null;
|
User user = null;
|
||||||
|
|
||||||
var identityuser = await _identityUserManager.FindByEmailAsync(email);
|
var identityuser = await _identityUserManager.FindByEmailAsync(email);
|
||||||
if (identityuser == null)
|
if (identityuser == null)
|
||||||
{
|
{
|
||||||
identityuser = new IdentityUser();
|
if (bool.Parse(alias.SiteSettings.GetValue("ExternalLogin:CreateUsers", "true")))
|
||||||
identityuser.UserName = email;
|
|
||||||
identityuser.Email = email;
|
|
||||||
identityuser.EmailConfirmed = true;
|
|
||||||
var result = await _identityUserManager.CreateAsync(identityuser, DateTime.UtcNow.ToString("yyyy-MMM-dd-HH-mm-ss"));
|
|
||||||
if (result.Succeeded)
|
|
||||||
{
|
{
|
||||||
// add user login
|
identityuser = new IdentityUser();
|
||||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(loginProvider, providerKey, email));
|
identityuser.UserName = email;
|
||||||
|
identityuser.Email = email;
|
||||||
user = new User();
|
identityuser.EmailConfirmed = true;
|
||||||
user.SiteId = alias.SiteId;
|
var result = await _identityUserManager.CreateAsync(identityuser, DateTime.UtcNow.ToString("yyyy-MMM-dd-HH-mm-ss"));
|
||||||
user.Username = email;
|
if (result.Succeeded)
|
||||||
user.DisplayName = email;
|
|
||||||
user.Email = email;
|
|
||||||
user.LastLoginOn = null;
|
|
||||||
user.LastIPAddress = "";
|
|
||||||
user = _users.AddUser(user);
|
|
||||||
|
|
||||||
// add folder for user
|
|
||||||
var _folders = context.HttpContext.RequestServices.GetRequiredService<IFolderRepository>();
|
|
||||||
Folder folder = _folders.GetFolder(user.SiteId, Utilities.PathCombine("Users", Path.DirectorySeparatorChar.ToString()));
|
|
||||||
if (folder != null)
|
|
||||||
{
|
{
|
||||||
_folders.AddFolder(new Folder
|
// add user login
|
||||||
|
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType, providerKey, ""));
|
||||||
|
|
||||||
|
user = new User();
|
||||||
|
user.SiteId = alias.SiteId;
|
||||||
|
user.Username = email;
|
||||||
|
user.DisplayName = email;
|
||||||
|
user.Email = email;
|
||||||
|
user.LastLoginOn = null;
|
||||||
|
user.LastIPAddress = "";
|
||||||
|
user = _users.AddUser(user);
|
||||||
|
|
||||||
|
// add folder for user
|
||||||
|
var _folders = httpContext.RequestServices.GetRequiredService<IFolderRepository>();
|
||||||
|
Folder folder = _folders.GetFolder(user.SiteId, Utilities.PathCombine("Users", Path.DirectorySeparatorChar.ToString()));
|
||||||
|
if (folder != null)
|
||||||
{
|
{
|
||||||
SiteId = folder.SiteId,
|
_folders.AddFolder(new Folder
|
||||||
ParentId = folder.FolderId,
|
|
||||||
Name = "My Folder",
|
|
||||||
Type = FolderTypes.Private,
|
|
||||||
Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()),
|
|
||||||
Order = 1,
|
|
||||||
ImageSizes = "",
|
|
||||||
Capacity = Constants.UserFolderCapacity,
|
|
||||||
IsSystem = true,
|
|
||||||
Permissions = new List<Permission>
|
|
||||||
{
|
{
|
||||||
new Permission(PermissionNames.Browse, user.UserId, true),
|
SiteId = folder.SiteId,
|
||||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
ParentId = folder.FolderId,
|
||||||
new Permission(PermissionNames.Edit, user.UserId, true)
|
Name = "My Folder",
|
||||||
}.EncodePermissions()
|
Type = FolderTypes.Private,
|
||||||
});
|
Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()),
|
||||||
}
|
Order = 1,
|
||||||
|
ImageSizes = "",
|
||||||
|
Capacity = Constants.UserFolderCapacity,
|
||||||
|
IsSystem = true,
|
||||||
|
Permissions = new List<Permission>
|
||||||
|
{
|
||||||
|
new Permission(PermissionNames.Browse, user.UserId, true),
|
||||||
|
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||||
|
new Permission(PermissionNames.Edit, user.UserId, true)
|
||||||
|
}.EncodePermissions()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// add auto assigned roles to user for site
|
// add auto assigned roles to user for site
|
||||||
var _roles = context.HttpContext.RequestServices.GetRequiredService<IRoleRepository>();
|
var _roles = httpContext.RequestServices.GetRequiredService<IRoleRepository>();
|
||||||
List<Role> roles = _roles.GetRoles(user.SiteId).Where(item => item.IsAutoAssigned).ToList();
|
List<Role> roles = _roles.GetRoles(user.SiteId).Where(item => item.IsAutoAssigned).ToList();
|
||||||
foreach (Role role in roles)
|
foreach (Role role in roles)
|
||||||
{
|
{
|
||||||
UserRole userrole = new UserRole();
|
UserRole userrole = new UserRole();
|
||||||
userrole.UserId = user.UserId;
|
userrole.UserId = user.UserId;
|
||||||
userrole.RoleId = role.RoleId;
|
userrole.RoleId = role.RoleId;
|
||||||
userrole.EffectiveDate = null;
|
userrole.EffectiveDate = null;
|
||||||
userrole.ExpiryDate = null;
|
userrole.ExpiryDate = null;
|
||||||
_userRoles.AddUserRole(userrole);
|
_userRoles.AddUserRole(userrole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Creation Of New Users Is Disabled. User With Email Address {Email} Will First Need To Be Registered On The Site.", email);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
||||||
var login = logins.FirstOrDefault(item => item.LoginProvider == loginProvider);
|
var login = logins.FirstOrDefault(item => item.LoginProvider == providerType);
|
||||||
if (login != null)
|
if (login != null)
|
||||||
{
|
{
|
||||||
if (login.ProviderKey == providerKey)
|
if (login.ProviderKey == providerKey)
|
||||||
|
@ -191,13 +279,13 @@ namespace Oqtane.Extensions
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// provider keys do not match
|
// provider keys do not match
|
||||||
_logger.Log(LogLevel.Error, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenId Connect Provider Key Does Not Match For User {Email}. Login Denied.", email);
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// add user login
|
// add user login
|
||||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(loginProvider, providerKey, identityuser.UserName));
|
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType, providerKey, ""));
|
||||||
user = _users.GetUser(identityuser.UserName);
|
user = _users.GetUser(identityuser.UserName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,75 +295,63 @@ namespace Oqtane.Extensions
|
||||||
{
|
{
|
||||||
// update user
|
// update user
|
||||||
user.LastLoginOn = DateTime.UtcNow;
|
user.LastLoginOn = DateTime.UtcNow;
|
||||||
user.LastIPAddress = context.HttpContext.Connection.RemoteIpAddress.ToString();
|
user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString();
|
||||||
_users.UpdateUser(user);
|
_users.UpdateUser(user);
|
||||||
_logger.Log(LogLevel.Information, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "User Login Successful {Username}", user.Username);
|
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "User Login Successful For {Username} Using Provider {Provider}", user.Username, providerType);
|
||||||
|
|
||||||
var principal = (ClaimsIdentity)context.Principal.Identity;
|
|
||||||
|
|
||||||
// remove the name claim if it exists in the principal
|
|
||||||
var nameclaim = principal.Claims.FirstOrDefault(item => item.Type == ClaimTypes.Name);
|
|
||||||
if (nameclaim != null)
|
|
||||||
{
|
|
||||||
principal.RemoveClaim(nameclaim);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add Oqtane claims
|
// add Oqtane claims
|
||||||
|
var principal = (ClaimsIdentity)claimsPrincipal.Identity;
|
||||||
|
UserSecurity.ResetClaimsIdentity(principal);
|
||||||
List<UserRole> userroles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList();
|
List<UserRole> userroles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList();
|
||||||
var identity = UserSecurity.CreateClaimsIdentity(alias, user, userroles);
|
var identity = UserSecurity.CreateClaimsIdentity(alias, user, userroles);
|
||||||
principal.AddClaims(identity.Claims);
|
principal.AddClaims(identity.Claims);
|
||||||
|
|
||||||
// add provider
|
|
||||||
principal.AddClaim(new Claim("Provider", context.HttpContext.GetAlias().SiteSettings["OpenIdConnectOptions:Authority"]));
|
|
||||||
}
|
}
|
||||||
}
|
else // user not logged in
|
||||||
else // no email claim
|
|
||||||
{
|
|
||||||
_logger.Log(LogLevel.Error, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenID Connect Provider Did Not Return An Email Claim To Uniquely Identify The User");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Task OnRedirectToIdentityProviderForSignOut(RedirectContext context)
|
|
||||||
{
|
|
||||||
var logoutUrl = context.HttpContext.GetAlias().SiteSettings.GetValue("OpenIdConnectOptions:LogoutUrl", "");
|
|
||||||
if (logoutUrl != "")
|
|
||||||
{
|
|
||||||
var postLogoutUri = context.Properties.RedirectUri;
|
|
||||||
if (!string.IsNullOrEmpty(postLogoutUri))
|
|
||||||
{
|
{
|
||||||
if (postLogoutUri.StartsWith("/"))
|
await httpContext.SignOutAsync();
|
||||||
{
|
|
||||||
var request = context.Request;
|
|
||||||
postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
|
|
||||||
}
|
|
||||||
logoutUrl += $"&returnTo={Uri.EscapeDataString(postLogoutUri)}";
|
|
||||||
}
|
}
|
||||||
context.Response.Redirect(logoutUrl);
|
|
||||||
context.HandleResponse();
|
|
||||||
}
|
}
|
||||||
return Task.CompletedTask;
|
else // email invalid
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(email))
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The Email Address {Email} Is Invalid Or Does Not Match The Domain Filter Criteria. Login Denied.", email);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var emailclaimtype = claimsPrincipal.Claims.FirstOrDefault(item => item.Value.Contains("@") && item.Value.Contains("."));
|
||||||
|
if (emailclaimtype != null)
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Please Verify If \"{ClaimType}\" Is A Valid Email Claim Type For The Provider And Update Your External Login Settings Accordingly", emailclaimtype.Type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email To Uniquely Identify The User.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await httpContext.SignOutAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task OnAccessDenied(AccessDeniedContext context)
|
private static bool EmailValid(string email, string domainfilter)
|
||||||
{
|
{
|
||||||
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogManager>();
|
if (!string.IsNullOrEmpty(email) && email.Contains("@") && email.Contains("."))
|
||||||
_logger.Log(LogLevel.Information, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenID Connect Access Denied - User May Have Cancelled Their External Login Attempt");
|
{
|
||||||
// redirect to login page
|
var domains = domainfilter.ToLower().Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||||
var alias = context.HttpContext.GetAlias();
|
foreach (var domain in domains)
|
||||||
context.Response.Redirect(alias.Path + "/login?returnurl=" + context.Properties.RedirectUri);
|
{
|
||||||
context.HandleResponse();
|
if (domain.StartsWith("!"))
|
||||||
return Task.CompletedTask;
|
{
|
||||||
}
|
if (email.ToLower().Contains(domain.Substring(1))) return false;
|
||||||
|
}
|
||||||
private static Task OnRemoteFailure(RemoteFailureContext context)
|
else
|
||||||
{
|
{
|
||||||
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogManager>();
|
if (!email.ToLower().Contains(domain)) return false;
|
||||||
_logger.Log(LogLevel.Error, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenID Connect Remote Failure - {Error}", context.Failure.Message);
|
}
|
||||||
// redirect to login page
|
}
|
||||||
var alias = context.HttpContext.GetAlias();
|
return true;
|
||||||
context.Response.Redirect(alias.Path + "/login?returnurl=" + context.Properties.RedirectUri);
|
}
|
||||||
context.HandleResponse();
|
return false;
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
Oqtane.Server/Pages/External.cshtml
Normal file
3
Oqtane.Server/Pages/External.cshtml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
@page "/pages/external"
|
||||||
|
@namespace Oqtane.Pages
|
||||||
|
@model Oqtane.Pages.ExternalModel
|
29
Oqtane.Server/Pages/External.cshtml.cs
Normal file
29
Oqtane.Server/Pages/External.cshtml.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Oqtane.Extensions;
|
||||||
|
|
||||||
|
namespace Oqtane.Pages
|
||||||
|
{
|
||||||
|
public class ExternalModel : PageModel
|
||||||
|
{
|
||||||
|
public IActionResult OnGetAsync(string returnurl)
|
||||||
|
{
|
||||||
|
returnurl = (returnurl == null) ? "/" : returnurl;
|
||||||
|
returnurl = (!returnurl.StartsWith("/")) ? "/" + returnurl : returnurl;
|
||||||
|
|
||||||
|
var providertype = HttpContext.GetAlias().SiteSettings.GetValue("ExternalLogin:ProviderType", "");
|
||||||
|
if (providertype != "")
|
||||||
|
{
|
||||||
|
return new ChallengeResult(providertype, new AuthenticationProperties { RedirectUri = returnurl });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
return new EmptyResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,18 +20,7 @@ namespace Oqtane.Pages
|
||||||
returnurl = (returnurl == null) ? "/" : returnurl;
|
returnurl = (returnurl == null) ? "/" : returnurl;
|
||||||
returnurl = (!returnurl.StartsWith("/")) ? "/" + returnurl : returnurl;
|
returnurl = (!returnurl.StartsWith("/")) ? "/" + returnurl : returnurl;
|
||||||
|
|
||||||
var provider = HttpContext.User.Claims.FirstOrDefault(item => item.Type == "Provider");
|
return LocalRedirect(Url.Content("~" + returnurl));
|
||||||
var authority = HttpContext.GetAlias().SiteSettings.GetValue("OpenIdConnectOptions:Authority", "");
|
|
||||||
var logoutUrl = HttpContext.GetAlias().SiteSettings.GetValue("OpenIdConnectOptions:LogoutUrl", "");
|
|
||||||
if (provider != null && provider.Value == authority && logoutUrl != "")
|
|
||||||
{
|
|
||||||
return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme,
|
|
||||||
new AuthenticationProperties { RedirectUri = returnurl });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return LocalRedirect(Url.Content("~" + returnurl));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
@page "/pages/oidc"
|
|
||||||
@namespace Oqtane.Pages
|
|
||||||
@model Oqtane.Pages.OIDCModel
|
|
|
@ -1,19 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
||||||
|
|
||||||
namespace Oqtane.Pages
|
|
||||||
{
|
|
||||||
public class OIDCModel : PageModel
|
|
||||||
{
|
|
||||||
public IActionResult OnGetAsync(string returnurl)
|
|
||||||
{
|
|
||||||
returnurl = (returnurl == null) ? "/" : returnurl;
|
|
||||||
returnurl = (!returnurl.StartsWith("/")) ? "/" + returnurl : returnurl;
|
|
||||||
|
|
||||||
return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme,
|
|
||||||
new AuthenticationProperties { RedirectUri = returnurl });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -115,7 +115,8 @@ namespace Oqtane
|
||||||
options.DefaultChallengeScheme = Constants.AuthenticationScheme;
|
options.DefaultChallengeScheme = Constants.AuthenticationScheme;
|
||||||
})
|
})
|
||||||
.AddCookie(Constants.AuthenticationScheme)
|
.AddCookie(Constants.AuthenticationScheme)
|
||||||
.AddOpenIdConnect();
|
.AddOpenIdConnect("oidc", options => { })
|
||||||
|
.AddOAuth("oauth2", options => { });
|
||||||
|
|
||||||
services.ConfigureOqtaneCookieOptions();
|
services.ConfigureOqtaneCookieOptions();
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace Oqtane.Security
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
authorized = IsAuthorized(-1, "", permissions); // user is not authenticated but may have access to resource
|
authorized = IsAuthorized(-1, "", permissions); // user is not authenticated but may have access to resource
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -152,5 +152,24 @@ namespace Oqtane.Security
|
||||||
}
|
}
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ResetClaimsIdentity(ClaimsIdentity identity)
|
||||||
|
{
|
||||||
|
var claim = identity.Claims.FirstOrDefault(item => item.Type == ClaimTypes.Name);
|
||||||
|
if (claim != null)
|
||||||
|
{
|
||||||
|
identity.RemoveClaim(claim);
|
||||||
|
}
|
||||||
|
claim = identity.Claims.FirstOrDefault(item => item.Type == ClaimTypes.PrimarySid);
|
||||||
|
if (claim != null)
|
||||||
|
{
|
||||||
|
identity.RemoveClaim(claim);
|
||||||
|
}
|
||||||
|
claim = identity.Claims.FirstOrDefault(item => item.Type == ClaimTypes.GroupSid);
|
||||||
|
if (claim != null)
|
||||||
|
{
|
||||||
|
identity.RemoveClaim(claim);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user