Skip to content

Commit f3f5b2f

Browse files
dyzymartincostello
authored andcommitted
Odnoklassniki auth provider implementation and test
1 parent fe1709a commit f3f5b2f

10 files changed

+408
-0
lines changed

AspNet.Security.OAuth.Providers.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Zalo"
157157
EndProject
158158
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.MailRu", "src\AspNet.Security.OAuth.MailRu\AspNet.Security.OAuth.MailRu.csproj", "{CC1E9908-075D-4138-852A-6EA3B5F32E88}"
159159
EndProject
160+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Odnoklassniki", "src\AspNet.Security.OAuth.Odnoklassniki\AspNet.Security.OAuth.Odnoklassniki.csproj", "{27DB335F-5012-4276-98C5-EAEA335C8C7B}"
161+
EndProject
160162
Global
161163
GlobalSection(SolutionConfigurationPlatforms) = preSolution
162164
Debug|Any CPU = Debug|Any CPU
@@ -371,6 +373,10 @@ Global
371373
{CC1E9908-075D-4138-852A-6EA3B5F32E88}.Debug|Any CPU.Build.0 = Debug|Any CPU
372374
{CC1E9908-075D-4138-852A-6EA3B5F32E88}.Release|Any CPU.ActiveCfg = Release|Any CPU
373375
{CC1E9908-075D-4138-852A-6EA3B5F32E88}.Release|Any CPU.Build.0 = Release|Any CPU
376+
{27DB335F-5012-4276-98C5-EAEA335C8C7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
377+
{27DB335F-5012-4276-98C5-EAEA335C8C7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
378+
{27DB335F-5012-4276-98C5-EAEA335C8C7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
379+
{27DB335F-5012-4276-98C5-EAEA335C8C7B}.Release|Any CPU.Build.0 = Release|Any CPU
374380
EndGlobalSection
375381
GlobalSection(SolutionProperties) = preSolution
376382
HideSolutionNode = FALSE
@@ -430,6 +436,7 @@ Global
430436
{E256B219-13E7-468D-8D33-5E93263BC36D} = {3FA3F7B5-5373-4E43-8F45-8EC18249E526}
431437
{20A668AB-CFBB-44B0-B76B-42705625E06D} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
432438
{CC1E9908-075D-4138-852A-6EA3B5F32E88} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
439+
{27DB335F-5012-4276-98C5-EAEA335C8C7B} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
433440
EndGlobalSection
434441
GlobalSection(ExtensibilityGlobals) = postSolution
435442
SolutionGuid = {C7B54DE2-6407-4802-AD9C-CE54BF414C8C}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ If a provider you're looking for does not exist, consider making a PR to add one
129129
| 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") |
130130
| 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") |
131131
| 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") |
132+
| 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") |
132133
| 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 |
133134
| 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") |
134135
| Paypal | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Paypal?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Paypal/ "Download AspNet.Security.OAuth.Paypal from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Paypal?includePreReleases=false)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Paypal "Download AspNet.Security.OAuth.Paypal from MyGet.org") | [Documentation](https://developer.paypal.com/docs/api-basics/#oauth-20-authorization-protocol "Paypal 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 Odnoklassniki authentication.</Description>
11+
<Authors>Dmitry Vostryakov</Authors>
12+
<PackageTags>aspnetcore;authentication;odnoklassniki;ok;oauth;security</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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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.Odnoklassniki
8+
{
9+
/// <summary>
10+
/// Contains constants specific to the <see cref="OdnoklassnikiAuthenticationHandler"/>.
11+
/// </summary>
12+
public static class OdnoklassnikiAuthenticationConstants
13+
{
14+
public static class Claims
15+
{
16+
public const string ImageUrl = "urn:ok:profileimage";
17+
}
18+
}
19+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
using Microsoft.AspNetCore.Authentication.OAuth;
9+
10+
namespace AspNet.Security.OAuth.Odnoklassniki
11+
{
12+
/// <summary>
13+
/// Default values used by the Odnoklassniki authentication middleware.
14+
/// </summary>
15+
public static class OdnoklassnikiAuthenticationDefaults
16+
{
17+
/// <summary>
18+
/// Default value for <see cref="AuthenticationScheme.Name"/>.
19+
/// </summary>
20+
public const string AuthenticationScheme = "Odnoklassniki";
21+
22+
/// <summary>
23+
/// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
24+
/// </summary>
25+
public const string DisplayName = "Odnoklassniki";
26+
27+
/// <summary>
28+
/// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
29+
/// </summary>
30+
public const string Issuer = "Odnoklassniki";
31+
32+
/// <summary>
33+
/// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
34+
/// </summary>
35+
public const string CallbackPath = "/signin-odnoklassniki";
36+
37+
/// <summary>
38+
/// Default value for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
39+
/// </summary>
40+
public const string AuthorizationEndpoint = "https://connect.ok.ru/oauth/authorize";
41+
42+
/// <summary>
43+
/// Default value for <see cref="OAuthOptions.TokenEndpoint"/>.
44+
/// </summary>
45+
public const string TokenEndpoint = "https://api.ok.ru/oauth/token.do";
46+
47+
/// <summary>
48+
/// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
49+
/// </summary>
50+
public const string UserInformationEndpoint = "https://api.ok.ru/fb.do";
51+
52+
}
53+
}
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.Odnoklassniki;
9+
using JetBrains.Annotations;
10+
using Microsoft.AspNetCore.Authentication;
11+
12+
namespace Microsoft.Extensions.DependencyInjection
13+
{
14+
/// <summary>
15+
/// Extension methods to add Odnoklassniki authentication capabilities to an HTTP application pipeline.
16+
/// </summary>
17+
public static class OdnoklassnikiAuthenticationExtensions
18+
{
19+
/// <summary>
20+
/// Adds <see cref="OdnoklassnikiAuthenticationHandler"/> to the specified
21+
/// <see cref="AuthenticationBuilder"/>, which enables Odnoklassniki authentication capabilities.
22+
/// </summary>
23+
/// <param name="builder">The authentication builder.</param>
24+
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
25+
public static AuthenticationBuilder AddOdnoklassniki([NotNull] this AuthenticationBuilder builder)
26+
{
27+
return builder.AddOdnoklassniki(OdnoklassnikiAuthenticationDefaults.AuthenticationScheme, options => { });
28+
}
29+
30+
/// <summary>
31+
/// Adds <see cref="OdnoklassnikiAuthenticationHandler"/> to the specified
32+
/// <see cref="AuthenticationBuilder"/>, which enables Odnoklassniki 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>The <see cref="AuthenticationBuilder"/>.</returns>
37+
public static AuthenticationBuilder AddOdnoklassniki(
38+
[NotNull] this AuthenticationBuilder builder,
39+
[NotNull] Action<OdnoklassnikiAuthenticationOptions> configuration)
40+
{
41+
return builder.AddOdnoklassniki(OdnoklassnikiAuthenticationDefaults.AuthenticationScheme, configuration);
42+
}
43+
44+
/// <summary>
45+
/// Adds <see cref="OdnoklassnikiAuthenticationHandler"/> to the specified
46+
/// <see cref="AuthenticationBuilder"/>, which enables Odnoklassniki 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 Odnoklassniki options.</param>
51+
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
52+
public static AuthenticationBuilder AddOdnoklassniki(
53+
[NotNull] this AuthenticationBuilder builder, [NotNull] string scheme,
54+
[NotNull] Action<OdnoklassnikiAuthenticationOptions> configuration)
55+
{
56+
return builder.AddOdnoklassniki(scheme, OdnoklassnikiAuthenticationDefaults.DisplayName, configuration);
57+
}
58+
59+
/// <summary>
60+
/// Adds <see cref="OdnoklassnikiAuthenticationHandler"/> to the specified
61+
/// <see cref="AuthenticationBuilder"/>, which enables Odnoklassniki 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 Odnoklassniki options.</param>
67+
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
68+
public static AuthenticationBuilder AddOdnoklassniki(
69+
[NotNull] this AuthenticationBuilder builder,
70+
[NotNull] string scheme, [CanBeNull] string caption,
71+
[NotNull] Action<OdnoklassnikiAuthenticationOptions> configuration)
72+
{
73+
return builder.AddOAuth<OdnoklassnikiAuthenticationOptions, OdnoklassnikiAuthenticationHandler>(scheme, caption, configuration);
74+
}
75+
}
76+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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.Collections.Generic;
8+
using System.Linq;
9+
using System.Net.Http;
10+
using System.Net.Http.Headers;
11+
using System.Security.Claims;
12+
using System.Security.Cryptography;
13+
using System.Text;
14+
using System.Text.Encodings.Web;
15+
using System.Threading.Tasks;
16+
using JetBrains.Annotations;
17+
using Microsoft.AspNetCore.Authentication;
18+
using Microsoft.AspNetCore.Authentication.OAuth;
19+
using Microsoft.AspNetCore.WebUtilities;
20+
using Microsoft.Extensions.Logging;
21+
using Microsoft.Extensions.Options;
22+
using Newtonsoft.Json.Linq;
23+
24+
namespace AspNet.Security.OAuth.Odnoklassniki
25+
{
26+
public class OdnoklassnikiAuthenticationHandler : OAuthHandler<OdnoklassnikiAuthenticationOptions>
27+
{
28+
public OdnoklassnikiAuthenticationHandler(
29+
[NotNull] IOptionsMonitor<OdnoklassnikiAuthenticationOptions> options,
30+
[NotNull] ILoggerFactory logger,
31+
[NotNull] UrlEncoder encoder,
32+
[NotNull] ISystemClock clock)
33+
: base(options, logger, encoder, clock)
34+
{
35+
}
36+
37+
protected override async Task<AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity,
38+
[NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
39+
{
40+
string sign;
41+
using (var md5Hash = MD5.Create())
42+
{
43+
var accessSecret = GetMd5Hash(md5Hash, tokens.AccessToken + Options.ClientSecret);
44+
sign = GetMd5Hash(md5Hash,$"application_key={Options.PublicSecret}format=jsonmethod=users.getCurrentUser{accessSecret}");
45+
}
46+
47+
var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary<string, string>
48+
{
49+
["application_key"] = Options.PublicSecret,
50+
["format"] = "json",
51+
["method"] = "users.getCurrentUser",
52+
["sig"] = sign,
53+
["access_token"] = tokens.AccessToken
54+
});
55+
56+
var request = new HttpRequestMessage(HttpMethod.Get, address);
57+
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
58+
59+
var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);
60+
if (!response.IsSuccessStatusCode)
61+
{
62+
Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
63+
"returned a {Status} response with the following payload: {Headers} {Body}.",
64+
/* Status: */ response.StatusCode,
65+
/* Headers: */ response.Headers.ToString(),
66+
/* Body: */ await response.Content.ReadAsStringAsync());
67+
68+
throw new HttpRequestException("An error occurred while retrieving the user profile.");
69+
}
70+
71+
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
72+
73+
var principal = new ClaimsPrincipal(identity);
74+
var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);
75+
context.RunClaimActions(payload);
76+
77+
await Options.Events.CreatingTicket(context);
78+
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
79+
}
80+
81+
private static string GetMd5Hash(HashAlgorithm hashAlgorithm, string input)
82+
{
83+
var sBuilder = new StringBuilder();
84+
foreach (var t in hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(input)))
85+
sBuilder.Append(t.ToString("x2"));
86+
return sBuilder.ToString();
87+
}
88+
}
89+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.Security.Claims;
8+
using Microsoft.AspNetCore.Authentication;
9+
using Microsoft.AspNetCore.Authentication.OAuth;
10+
using Microsoft.AspNetCore.Http;
11+
using static AspNet.Security.OAuth.Odnoklassniki.OdnoklassnikiAuthenticationConstants;
12+
13+
namespace AspNet.Security.OAuth.Odnoklassniki
14+
{
15+
/// <summary>
16+
/// Defines a set of options used by <see cref="OdnoklassnikiAuthenticationHandler"/>.
17+
/// </summary>
18+
public class OdnoklassnikiAuthenticationOptions : OAuthOptions
19+
{
20+
public string PublicSecret { get; set; }
21+
22+
public OdnoklassnikiAuthenticationOptions()
23+
{
24+
ClaimsIssuer = OdnoklassnikiAuthenticationDefaults.Issuer;
25+
26+
CallbackPath = new PathString(OdnoklassnikiAuthenticationDefaults.CallbackPath);
27+
28+
AuthorizationEndpoint = OdnoklassnikiAuthenticationDefaults.AuthorizationEndpoint;
29+
TokenEndpoint = OdnoklassnikiAuthenticationDefaults.TokenEndpoint;
30+
UserInformationEndpoint = OdnoklassnikiAuthenticationDefaults.UserInformationEndpoint;
31+
32+
Scope.Add("GET_EMAIL");
33+
34+
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "uid");
35+
ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
36+
ClaimActions.MapJsonKey(ClaimTypes.Gender, "gender");
37+
ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
38+
ClaimActions.MapJsonKey(ClaimTypes.Surname, "last_name");
39+
ClaimActions.MapJsonKey(ClaimTypes.GivenName, "first_name");
40+
ClaimActions.MapJsonKey(Claims.ImageUrl, "pic_2");
41+
}
42+
43+
}
44+
}

0 commit comments

Comments
 (0)