Skip to content

Commit 38a0128

Browse files
zAfLumartincostello
authored andcommitted
Add Nextcloud provider (#325)
* Add Nextcloud provider and unit test * updated according to suggestions * Change ClaimTypes.Name to Claims.Username
1 parent 15078f4 commit 38a0128

10 files changed

+420
-0
lines changed

AspNet.Security.OAuth.Providers.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Odnok
159159
EndProject
160160
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Trakt", "src\AspNet.Security.OAuth.Trakt\AspNet.Security.OAuth.Trakt.csproj", "{5325536E-8E3A-4611-AB92-B03369493354}"
161161
EndProject
162+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Nextcloud", "src\AspNet.Security.OAuth.Nextcloud\AspNet.Security.OAuth.Nextcloud.csproj", "{92AE059B-928C-404F-BFC6-9CA57712AB90}"
163+
EndProject
162164
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Shopify", "src\AspNet.Security.OAuth.Shopify\AspNet.Security.OAuth.Shopify.csproj", "{112F6B50-0FD3-45AA-992E-72D7B873BF00}"
163165
EndProject
164166
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.Client", "samples\Mvc.Client\Mvc.Client.csproj", "{140A6CAD-FC84-4A09-A939-8A5692DF0C7C}"
@@ -385,6 +387,10 @@ Global
385387
{5325536E-8E3A-4611-AB92-B03369493354}.Debug|Any CPU.Build.0 = Debug|Any CPU
386388
{5325536E-8E3A-4611-AB92-B03369493354}.Release|Any CPU.ActiveCfg = Release|Any CPU
387389
{5325536E-8E3A-4611-AB92-B03369493354}.Release|Any CPU.Build.0 = Release|Any CPU
390+
{92AE059B-928C-404F-BFC6-9CA57712AB90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
391+
{92AE059B-928C-404F-BFC6-9CA57712AB90}.Debug|Any CPU.Build.0 = Debug|Any CPU
392+
{92AE059B-928C-404F-BFC6-9CA57712AB90}.Release|Any CPU.ActiveCfg = Release|Any CPU
393+
{92AE059B-928C-404F-BFC6-9CA57712AB90}.Release|Any CPU.Build.0 = Release|Any CPU
388394
{112F6B50-0FD3-45AA-992E-72D7B873BF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
389395
{112F6B50-0FD3-45AA-992E-72D7B873BF00}.Debug|Any CPU.Build.0 = Debug|Any CPU
390396
{112F6B50-0FD3-45AA-992E-72D7B873BF00}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -461,6 +467,7 @@ Global
461467
{CC1E9908-075D-4138-852A-6EA3B5F32E88} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
462468
{27DB335F-5012-4276-98C5-EAEA335C8C7B} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
463469
{5325536E-8E3A-4611-AB92-B03369493354} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
470+
{92AE059B-928C-404F-BFC6-9CA57712AB90} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
464471
{112F6B50-0FD3-45AA-992E-72D7B873BF00} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
465472
{140A6CAD-FC84-4A09-A939-8A5692DF0C7C} = {BAC7067D-88FE-4385-8AC9-1A325FFBDE69}
466473
{FACB1C2F-3BF7-4DD3-958B-E471E69E214A} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ If a provider you're looking for does not exist, consider making a PR to add one
133133
| MailChimp | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.MailChimp?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.MailChimp/ "Download AspNet.Security.OAuth.MailChimp from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.MailChimp?includePreReleases=false)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.MailChimp "Download AspNet.Security.OAuth.MailChimp from MyGet.org") | [Documentation](https://developer.mailchimp.com/documentation/mailchimp/guides/how-to-use-oauth2/ "MailChimp developer documentation") |
134134
| MailRu | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.MailRu?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.MailRu/ "Download AspNet.Security.OAuth.MailRu from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.MailRu?includePreReleases=false)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.MailRu "Download AspNet.Security.OAuth.MailRu from MyGet.org") | [Documentation](https://o2.mail.ru/docs#web "MailRu developer documentation") |
135135
| Myob | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Myob?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Myob/ "Download AspNet.Security.OAuth.Myob from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Myob?includePreReleases=false)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Myob "Download AspNet.Security.OAuth.Myob from MyGet.org") | [Documentation](https://developer.myob.com/api/accountright/api-overview/authentication/ "Myob developer documentation") |
136+
| Nextcloud | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Nextcloud?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Nextcloud/ "Download AspNet.Security.OAuth.Nextcloud from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Nextcloud?includePreReleases=false)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Nextcloud "Download AspNet.Security.OAuth.Nextcloud from MyGet.org") | [Documentation](https://docs.nextcloud.com/server/14/admin_manual/configuration_server/oauth2.html "Nextcloud developer documentation") [User EndPoint Documentation](https://docs.nextcloud.com/server/15/developer_manual/client_apis/OCS/index.html#user-metadata "Nextcloud developer documentation") |
136137
| Odnoklassniki | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Odnoklassniki?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Odnoklassniki/ "Download AspNet.Security.OAuth.Odnoklassniki from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Odnoklassniki?includePreReleases=false)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Odnoklassniki "Download AspNet.Security.OAuth.Odnoklassniki from MyGet.org") | [Documentation](https://apiok.ru/ext/oauth/server "Odnoklassniki developer documentation") |
137138
| Onshape | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Onshape?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Onshape/ "Download AspNet.Security.OAuth.Onshape from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Onshape?includePreReleases=false)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Onshape "Download AspNet.Security.OAuth.Onshape from MyGet.org") | N/A |
138139
| Patreon | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Patreon?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Patreon/ "Download AspNet.Security.OAuth.Patreon from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Patreon?includePreReleases=false)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Patreon "Download AspNet.Security.OAuth.Patreon from MyGet.org") | [Documentation](https://docs.patreon.com/#oauth "Patreon developer documentation") |
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Import Project="..\..\build\packages.props" />
4+
5+
<PropertyGroup>
6+
<TargetFramework>netstandard2.0</TargetFramework>
7+
</PropertyGroup>
8+
9+
<PropertyGroup>
10+
<Description>ASP.NET Core security middleware enabling Nextcloud authentication.</Description>
11+
<Authors>Dennis "zAfLu" Everts</Authors>
12+
<PackageTags>aspnetcore;authentication;oauth;security;nextcloud</PackageTags>
13+
</PropertyGroup>
14+
15+
<ItemGroup>
16+
<PackageReference Include="JetBrains.Annotations" Version="$(JetBrainsVersion)" PrivateAssets="All" />
17+
<PackageReference Include="Microsoft.AspNetCore.Authentication.OAuth" Version="$(AspNetCoreVersion)" />
18+
</ItemGroup>
19+
20+
</Project>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
namespace AspNet.Security.OAuth.Nextcloud
8+
{
9+
/// <summary>
10+
/// Contains constants specific to the <see cref="NextcloudAuthenticationHandler"/>.
11+
/// </summary>
12+
public static class NextcloudAuthenticationConstants
13+
{
14+
public static class Claims
15+
{
16+
public const string Groups = "urn:nextcloud:groups";
17+
public const string Username = "urn:nextcloud:username";
18+
public const string DisplayName = "urn:nextcloud:displayname";
19+
public const string IsEnabled = "urn:nextcloud:enabled";
20+
public const string Language = "urn:nextcloud:language";
21+
public const string Locale = "urn:nextcloud:locale";
22+
}
23+
}
24+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using Microsoft.AspNetCore.Authentication;
8+
9+
namespace AspNet.Security.OAuth.Nextcloud
10+
{
11+
/// <summary>
12+
/// Default values used by the Nextcloud authentication middleware.
13+
/// </summary>
14+
public static class NextcloudAuthenticationDefaults
15+
{
16+
/// <summary>
17+
/// Default value for <see cref="AuthenticationScheme.Name"/>.
18+
/// </summary>
19+
public const string AuthenticationScheme = "Nextcloud";
20+
21+
/// <summary>
22+
/// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
23+
/// </summary>
24+
public const string DisplayName = "Nextcloud";
25+
26+
/// <summary>
27+
/// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
28+
/// </summary>
29+
public const string Issuer = "Nextcloud";
30+
31+
/// <summary>
32+
/// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
33+
/// </summary>
34+
public const string CallbackPath = "/signin-nextcloud";
35+
}
36+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System;
8+
using AspNet.Security.OAuth.Nextcloud;
9+
using JetBrains.Annotations;
10+
using Microsoft.AspNetCore.Authentication;
11+
12+
namespace Microsoft.Extensions.DependencyInjection
13+
{
14+
/// <summary>
15+
/// Extension methods to add Nextcloud authentication capabilities to an HTTP application pipeline.
16+
/// </summary>
17+
public static class NextcloudAuthenticationExtensions
18+
{
19+
/// <summary>
20+
/// Adds <see cref="NextcloudAuthenticationHandler"/> to the specified
21+
/// <see cref="AuthenticationBuilder"/>, which enables Nextcloud authentication capabilities.
22+
/// </summary>
23+
/// <param name="builder">The authentication builder.</param>
24+
/// <returns>A reference to this instance after the operation has completed.</returns>
25+
public static AuthenticationBuilder AddNextcloud([NotNull] this AuthenticationBuilder builder)
26+
{
27+
return builder.AddNextcloud(NextcloudAuthenticationDefaults.AuthenticationScheme, options => { });
28+
}
29+
30+
/// <summary>
31+
/// Adds <see cref="NextcloudAuthenticationHandler"/> to the specified
32+
/// <see cref="AuthenticationBuilder"/>, which enables Nextcloud authentication capabilities.
33+
/// </summary>
34+
/// <param name="builder">The authentication builder.</param>
35+
/// <param name="configuration">The delegate used to configure the OpenID 2.0 options.</param>
36+
/// <returns>A reference to this instance after the operation has completed.</returns>
37+
public static AuthenticationBuilder AddNextcloud(
38+
[NotNull] this AuthenticationBuilder builder,
39+
[NotNull] Action<NextcloudAuthenticationOptions> configuration)
40+
{
41+
return builder.AddNextcloud(NextcloudAuthenticationDefaults.AuthenticationScheme, configuration);
42+
}
43+
44+
/// <summary>
45+
/// Adds <see cref="NextcloudAuthenticationHandler"/> to the specified
46+
/// <see cref="AuthenticationBuilder"/>, which enables Nextcloud authentication capabilities.
47+
/// </summary>
48+
/// <param name="builder">The authentication builder.</param>
49+
/// <param name="scheme">The authentication scheme associated with this instance.</param>
50+
/// <param name="configuration">The delegate used to configure the Nextcloud options.</param>
51+
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
52+
public static AuthenticationBuilder AddNextcloud(
53+
[NotNull] this AuthenticationBuilder builder, [NotNull] string scheme,
54+
[NotNull] Action<NextcloudAuthenticationOptions> configuration)
55+
{
56+
return builder.AddNextcloud(scheme, NextcloudAuthenticationDefaults.DisplayName, configuration);
57+
}
58+
59+
/// <summary>
60+
/// Adds <see cref="NextcloudAuthenticationHandler"/> to the specified
61+
/// <see cref="AuthenticationBuilder"/>, which enables Nextcloud authentication capabilities.
62+
/// </summary>
63+
/// <param name="builder">The authentication builder.</param>
64+
/// <param name="scheme">The authentication scheme associated with this instance.</param>
65+
/// <param name="caption">The optional display name associated with this instance.</param>
66+
/// <param name="configuration">The delegate used to configure the Nextcloud options.</param>
67+
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
68+
public static AuthenticationBuilder AddNextcloud(
69+
[NotNull] this AuthenticationBuilder builder,
70+
[NotNull] string scheme, [CanBeNull] string caption,
71+
[NotNull] Action<NextcloudAuthenticationOptions> configuration)
72+
{
73+
return builder.AddOAuth<NextcloudAuthenticationOptions, NextcloudAuthenticationHandler>(scheme, caption, configuration);
74+
}
75+
}
76+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System;
8+
using System.Net.Http;
9+
using System.Net.Http.Headers;
10+
using System.Security.Claims;
11+
using System.Text.Encodings.Web;
12+
using System.Threading.Tasks;
13+
using JetBrains.Annotations;
14+
using Microsoft.AspNetCore.Authentication;
15+
using Microsoft.AspNetCore.Authentication.OAuth;
16+
using Microsoft.Extensions.Logging;
17+
using Microsoft.Extensions.Options;
18+
using Newtonsoft.Json.Linq;
19+
20+
namespace AspNet.Security.OAuth.Nextcloud
21+
{
22+
public class NextcloudAuthenticationHandler : OAuthHandler<NextcloudAuthenticationOptions>
23+
{
24+
public NextcloudAuthenticationHandler(
25+
[NotNull] IOptionsMonitor<NextcloudAuthenticationOptions> options,
26+
[NotNull] ILoggerFactory logger,
27+
[NotNull] UrlEncoder encoder,
28+
[NotNull] ISystemClock clock)
29+
: base(options, logger, encoder, clock)
30+
{
31+
}
32+
33+
protected override async Task<AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity,
34+
[NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
35+
{
36+
string userId = tokens.Response.Value<string>("user_id");
37+
string userEndpoint = Options.UserInformationEndpoint.TrimEnd('/');
38+
userEndpoint += $"/{Uri.EscapeUriString(userId)}";
39+
40+
var request = new HttpRequestMessage(HttpMethod.Get, userEndpoint);
41+
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
42+
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
43+
44+
var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);
45+
if (!response.IsSuccessStatusCode)
46+
{
47+
Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
48+
"returned a {Status} response with the following payload: {Headers} {Body}.",
49+
/* Status: */ response.StatusCode,
50+
/* Headers: */ response.Headers.ToString(),
51+
/* Body: */ await response.Content.ReadAsStringAsync());
52+
53+
throw new HttpRequestException("An error occurred while retrieving the user profile.");
54+
}
55+
56+
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
57+
58+
var principal = new ClaimsPrincipal(identity);
59+
var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);
60+
context.RunClaimActions(payload);
61+
62+
await Options.Events.CreatingTicket(context);
63+
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
64+
}
65+
}
66+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System.Linq;
8+
using System.Security.Claims;
9+
using Microsoft.AspNetCore.Authentication;
10+
using Microsoft.AspNetCore.Authentication.OAuth;
11+
using Microsoft.AspNetCore.Http;
12+
using Newtonsoft.Json.Linq;
13+
using static AspNet.Security.OAuth.Nextcloud.NextcloudAuthenticationConstants;
14+
15+
namespace AspNet.Security.OAuth.Nextcloud
16+
{
17+
public class NextcloudAuthenticationOptions : OAuthOptions
18+
{
19+
public NextcloudAuthenticationOptions()
20+
{
21+
ClaimsIssuer = NextcloudAuthenticationDefaults.Issuer;
22+
CallbackPath = NextcloudAuthenticationDefaults.CallbackPath;
23+
24+
ClaimActions.MapCustomJson(ClaimTypes.NameIdentifier, user =>
25+
{
26+
return user["ocs"]?["data"]?.Value<string>("id");
27+
});
28+
29+
ClaimActions.MapCustomJson(Claims.Username, user =>
30+
{
31+
return user["ocs"]?["data"]?.Value<string>("id");
32+
});
33+
34+
ClaimActions.MapCustomJson(Claims.DisplayName, user =>
35+
{
36+
return user["ocs"]?["data"]?.Value<string>("displayname");
37+
});
38+
39+
ClaimActions.MapCustomJson(ClaimTypes.Email, user =>
40+
{
41+
return user["ocs"]?["data"]?.Value<string>("email");
42+
});
43+
44+
ClaimActions.MapCustomJson(Claims.Groups, user =>
45+
{
46+
var groups = (JArray)user["ocs"]?["data"]?["groups"];
47+
return string.Join(",", groups.ToList());
48+
});
49+
50+
ClaimActions.MapCustomJson(Claims.IsEnabled, user =>
51+
{
52+
return user["ocs"]?["data"]?.Value<string>("enabled");
53+
});
54+
55+
ClaimActions.MapCustomJson(Claims.Language, user =>
56+
{
57+
return user["ocs"]?["data"]?.Value<string>("language");
58+
});
59+
60+
ClaimActions.MapCustomJson(Claims.Locale, user =>
61+
{
62+
return user["ocs"]?["data"]?.Value<string>("locale");
63+
});
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)