Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/desktop login #448

Merged
merged 16 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions common/ASC.FederatedLogin/Helpers/OAuth20TokenHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public OAuth20TokenHelper(IHttpContextAccessor httpContextAccessor, ConsumerFact
ConsumerFactory = consumerFactory;
}

public string RequestCode<T>(string scope = null, Dictionary<string, string> additionalArgs = null) where T : Consumer, IOAuthProvider, new()
public string RequestCode<T>(string scope = null, IDictionary<string, string> additionalArgs = null, IDictionary<string, string> additionalStateArgs = null) where T : Consumer, IOAuthProvider, new()
{
var loginProvider = ConsumerFactory.Get<T>();
var requestUrl = loginProvider.CodeUrl;
Expand All @@ -67,7 +67,20 @@ public OAuth20TokenHelper(IHttpContextAccessor httpContextAccessor, ConsumerFact
if (!string.IsNullOrEmpty(scope)) query += $"&scope={HttpUtility.UrlEncode(scope)}";

var u = HttpContextAccessor.HttpContext.Request.GetUrlRewriter();
var state = HttpUtility.UrlEncode(new UriBuilder(u.Scheme, u.Host, u.Port, $"thirdparty/{loginProvider.Name.ToLower()}/code").Uri.AbsoluteUri);

var stateUriBuilder = new UriBuilder(u.Scheme, u.Host, u.Port, $"thirdparty/{loginProvider.Name.ToLower()}/code");

if (additionalStateArgs != null && additionalStateArgs.Any())
{
var stateQuery = "";
stateQuery = additionalStateArgs.Keys
.Where(a => a != null)
.Aggregate(stateQuery, (current, a) => a != null ? $"{current}&{a.Trim()}={additionalStateArgs[a] ?? "".Trim()}" : null);

stateUriBuilder.Query = stateQuery.Substring(1);
}

var state = HttpUtility.UrlEncode(stateUriBuilder.Uri.AbsoluteUri);
query += $"&state={state}";

if (additionalArgs != null)
Expand Down
63 changes: 22 additions & 41 deletions common/ASC.FederatedLogin/Login.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading;
Expand Down Expand Up @@ -104,10 +105,22 @@ public async Task Invoke(HttpContext context)
{
try
{
var profile = ProviderManager.Process(Auth, context, _params);
var desktop = _params.ContainsKey("desktop") && _params["desktop"] == "true";
IDictionary<string, string> additionalStateArgs = null;

if (desktop)
{
additionalStateArgs = context.Request.Query.ToDictionary(r => r.Key, r => r.Value.FirstOrDefault());
if (!additionalStateArgs.ContainsKey("desktop"))
{
additionalStateArgs.Add("desktop", "true");
}
}

var profile = ProviderManager.Process(Auth, context, null, additionalStateArgs);
if (profile != null)
{
await SendClientData(context, profile);
await SendJsCallback(context, profile);
}
}
catch (ThreadAbortException)
Expand All @@ -116,7 +129,7 @@ public async Task Invoke(HttpContext context)
}
catch (Exception ex)
{
await SendClientData(context, LoginProfile.FromError(Signature, InstanceCrypto, ex));
await SendJsCallback(context, LoginProfile.FromError(Signature, InstanceCrypto, ex));
}
}
else
Expand Down Expand Up @@ -179,48 +192,16 @@ public bool IsReusable
get { return false; }
}

private async Task SendClientData(HttpContext context, LoginProfile profile)
{
switch (Mode)
{
case LoginMode.Redirect:
await RedirectToReturnUrl(context, profile);
break;
case LoginMode.Popup:
await SendJsCallback(context, profile);
break;
}
}

private async Task SendJsCallback(HttpContext context, LoginProfile profile)
{
//Render a page
context.Response.ContentType = "text/html";
await context.Response.WriteAsync(JsCallbackHelper.GetCallbackPage().Replace("%PROFILE%", profile.ToJson()).Replace("%CALLBACK%", Callback));
}

private async Task RedirectToReturnUrl(HttpContext context, LoginProfile profile)
{
var useMinimalProfile = Minimal;
if (useMinimalProfile)
profile = profile.GetMinimalProfile(); //Only id and provider

if (context.Session != null && !useMinimalProfile)
{
//Store in session
context.Response.Redirect(new Uri(ReturnUrl, UriKind.Absolute).AddProfileSession(profile, context).ToString(), true);
}
else if (MemoryCache != null && !useMinimalProfile)
{
context.Response.Redirect(new Uri(ReturnUrl, UriKind.Absolute).AddProfileCache(profile, MemoryCache).ToString(), true);
}
else
{
context.Response.Redirect(new Uri(ReturnUrl, UriKind.Absolute).AddProfile(profile).ToString(), true);
}

await context.Response.CompleteAsync();
return;
await context.Response.WriteAsync(
JsCallbackHelper.GetCallbackPage()
.Replace("%PROFILE%", $"\"{profile.Serialized}\"")
.Replace("%CALLBACK%", Callback)
.Replace("%DESKTOP%", (Mode == LoginMode.Redirect).ToString().ToLowerInvariant())
);
}
}

Expand Down
8 changes: 4 additions & 4 deletions common/ASC.FederatedLogin/LoginProviders/BaseLoginProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,11 @@ protected BaseLoginProvider(
InstanceCrypto = instanceCrypto;
}

public virtual LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params)
public virtual LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs = null)
{
try
{
var token = Auth(context, Scopes, out var redirect);
var token = Auth(context, Scopes, out var redirect, @params, additionalStateArgs);

if (redirect)
{
Expand All @@ -136,7 +136,7 @@ public virtual LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary
}
}

protected virtual OAuth20Token Auth(HttpContext context, string scopes, out bool redirect, Dictionary<string, string> additionalArgs = null)
protected virtual OAuth20Token Auth(HttpContext context, string scopes, out bool redirect, IDictionary<string, string> additionalArgs = null, IDictionary<string, string> additionalStateArgs = null)
{
var error = context.Request.Query["error"];
if (!string.IsNullOrEmpty(error))
Expand All @@ -151,7 +151,7 @@ protected virtual OAuth20Token Auth(HttpContext context, string scopes, out bool
var code = context.Request.Query["code"];
if (string.IsNullOrEmpty(code))
{
context.Response.Redirect(OAuth20TokenHelper.RequestCode<T>(scopes, additionalArgs));
context.Response.Redirect(OAuth20TokenHelper.RequestCode<T>(scopes, additionalArgs, additionalStateArgs));
redirect = true;
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public GosUslugiLoginProvider(
}


public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params)
public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs = null)
{
try
{
Expand Down Expand Up @@ -142,7 +142,7 @@ public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionar
}
}

protected override OAuth20Token Auth(HttpContext context, string scopes, out bool redirect, Dictionary<string, string> additionalArgs = null)
protected override OAuth20Token Auth(HttpContext context, string scopes, out bool redirect, IDictionary<string, string> additionalArgs = null, IDictionary<string, string> additionalStateArgs = null)
{
var error = context.Request.Query["error"];
if (!string.IsNullOrEmpty(error))
Expand Down
2 changes: 1 addition & 1 deletion common/ASC.FederatedLogin/LoginProviders/ILoginProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace ASC.FederatedLogin.LoginProviders
{
public interface ILoginProvider : IOAuthProvider
{
LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params);
LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs);

LoginProfile GetLoginProfile(string accessToken);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public MailRuLoginProvider(
{
}

public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params)
public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs = null)
{
try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public OpenIdLoginProvider(Signature signature, InstanceCrypto instanceCrypto, C
ConsumerFactory = consumerFactory;
}

public LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params)
public LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs = null)
{
var response = Openid.GetResponse();
if (response == null)
Expand Down
4 changes: 2 additions & 2 deletions common/ASC.FederatedLogin/LoginProviders/ProviderManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ public ILoginProvider GetLoginProvider(string providerType)
: ConsumerFactory.GetByKey(providerType) as ILoginProvider;
}

public LoginProfile Process(string providerType, HttpContext context, IDictionary<string, string> @params)
public LoginProfile Process(string providerType, HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs = null)
{
return GetLoginProvider(providerType).ProcessAuthoriztion(context, @params);
return GetLoginProvider(providerType).ProcessAuthoriztion(context, @params, additionalStateArgs);
}

public LoginProfile GetLoginProfile(string providerType, string accessToken)
Expand Down
4 changes: 2 additions & 2 deletions common/ASC.FederatedLogin/LoginProviders/VKLoginProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public VKLoginProvider(
}


public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params)
public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs = null)
{
try
{
Expand All @@ -111,7 +111,7 @@ public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionar
{
{ "revoke", "1" }
}
: null);
: null, additionalStateArgs);

if (redirect)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public YandexLoginProvider(
{
}

public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params)
public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs = null)
{
try
{
Expand All @@ -103,7 +103,7 @@ public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionar
{
{ "force_confirm", "true" }
}
: null);
: null, additionalStateArgs);
if (redirect)
{
return null;
Expand Down
15 changes: 10 additions & 5 deletions common/ASC.FederatedLogin/callback.htm
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
<html>
<head>
<script language="javascript" type="text/javascript">
try {
window.opener.%CALLBACK%(%PROFILE%);
if (%DESKTOP%) {
localStorage.setItem("profile", %PROFILE%);
window.location.href = "/";
} else {
try {
window.opener.%CALLBACK%(%PROFILE%);
}
catch (ex) {
}
window.close();
}
catch (ex) {
}
window.close();
</script>
</head>
<body></body>
Expand Down
4 changes: 3 additions & 1 deletion packages/asc-web-common/store/AuthStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,11 @@ class AuthStore {

setWithCredentialsStatus(true);

this.reset();

this.init();

return Promise.resolve(true);
return Promise.resolve(this.settingsStore.defaultPage);
} catch (e) {
return Promise.reject(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class SectionBodyContent extends React.PureComponent {
};
loginCallback = (profile) => {
const { setProviders, t } = this.props;
linkOAuth(profile.Serialized).then((resp) => {
linkOAuth(profile).then((resp) => {
getAuthProviders().then((providers) => {
setProviders(providers);
toastr.success(t("ProviderSuccessfullyConnected"));
Expand Down
11 changes: 3 additions & 8 deletions products/ASC.People/Server/Controllers/PeopleController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1611,14 +1611,9 @@ public ICollection<AccountInfo> GetAuthProviders(bool inviteView, bool settingsV
{

var url = VirtualPathUtility.ToAbsolute("~/login.ashx") + $"?auth={provider}";
var mode = (settingsView || inviteView || (!MobileDetector.IsMobile() && !Request.DesktopApp())
? ("&mode=popup&callback=" + clientCallback)
: ("&mode=Redirect&returnurl="
+ HttpUtility.UrlEncode(new Uri(Request.GetUrlRewriter(),
"Auth.aspx"
+ (Request.DesktopApp() ? "?desktop=true" : "")
).ToString())
));
var mode = settingsView || inviteView || (!MobileDetector.IsMobile() && !Request.DesktopApp())
? $"&mode=popup&callback={clientCallback}"
: "&mode=Redirect&desktop=true";

infos.Add(new AccountInfo
{
Expand Down
16 changes: 12 additions & 4 deletions public/thirdparty/third-party.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@
const searchUrl = location.search.substring(1);
const object = JSON.parse(
'{"' +
decodeURIComponent(searchUrl)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
decodeURIComponent(
searchUrl
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')
) +
'"}'
);

Expand All @@ -70,6 +72,8 @@
const urlParams = getObjectByLocation(window.location);
const code = urlParams ? urlParams.code || null : null;
const error = urlParams ? urlParams.error || null : null;
const desktop = urlParams ? urlParams.desktop || false : false;
const p = urlParams ? urlParams.p || false : false;
</script>
</head>
<body id="third-party-body">
Expand All @@ -78,6 +82,10 @@
<script>
if (code) {
localStorage.setItem("code", code);

if (desktop && p) {
window.location.href = `/login.ashx?p=${p}&code=${code}&desktop=true`;
}
} else if (error) {
renderError(error);
}
Expand Down
Loading