diff --git a/src/Security/Directory.Build.props b/src/Security/Directory.Build.props index f1986d9953..a73e31b984 100644 --- a/src/Security/Directory.Build.props +++ b/src/Security/Directory.Build.props @@ -14,7 +14,6 @@ $(MSBuildThisFileDirectory) $(MSBuildThisFileDirectory)build\Key.snk true - true true diff --git a/src/Security/Directory.Build.targets b/src/Security/Directory.Build.targets index 53b3f6e1da..78626b773e 100644 --- a/src/Security/Directory.Build.targets +++ b/src/Security/Directory.Build.targets @@ -1,7 +1,10 @@ - + $(MicrosoftNETCoreApp20PackageVersion) $(MicrosoftNETCoreApp21PackageVersion) + $(MicrosoftNETCoreApp22PackageVersion) $(NETStandardLibrary20PackageVersion) + + 99.9 diff --git a/src/Security/build/dependencies.props b/src/Security/build/dependencies.props index 828f9c7ab2..0d1a80ebb6 100644 --- a/src/Security/build/dependencies.props +++ b/src/Security/build/dependencies.props @@ -2,15 +2,40 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - 2.1.3-rtm-15802 + + 2.2.0-preview2-20181004.6 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 + 2.2.0-preview3-35425 3.14.2 5.2.0 5.2.0 - 2.0.0 - 2.1.2 + 2.0.9 + 2.1.3 + 2.2.0-preview3-27001-02 15.6.1 3.0.1 3.0.1 @@ -18,41 +43,10 @@ 2.0.3 11.0.2 5.2.0 - 0.8.0 + 0.10.0 2.3.1 - 2.4.0-beta.1.build3945 + 2.4.0 - - - - - - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.2 - 2.1.2 - 2.1.1 - 2.1.1 - 2.1.0 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - 2.1.1 - - \ No newline at end of file + + diff --git a/src/Security/build/repo.props b/src/Security/build/repo.props index a4f86fb2f6..077c753e69 100644 --- a/src/Security/build/repo.props +++ b/src/Security/build/repo.props @@ -7,12 +7,13 @@ Internal.AspNetCore.Universe.Lineup - 2.1.0-rc1-* + 2.2.0-* https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json + diff --git a/src/Security/samples/CookiePolicySample/CookiePolicySample.csproj b/src/Security/samples/CookiePolicySample/CookiePolicySample.csproj index fb2e7d9172..48227eb07c 100644 --- a/src/Security/samples/CookiePolicySample/CookiePolicySample.csproj +++ b/src/Security/samples/CookiePolicySample/CookiePolicySample.csproj @@ -1,7 +1,7 @@ - + - net461;netcoreapp2.1 + net461;netcoreapp2.2 diff --git a/src/Security/samples/CookiePolicySample/Startup.cs b/src/Security/samples/CookiePolicySample/Startup.cs index 7ce9c2d2d2..4dcc6d4dc3 100644 --- a/src/Security/samples/CookiePolicySample/Startup.cs +++ b/src/Security/samples/CookiePolicySample/Startup.cs @@ -58,6 +58,13 @@ namespace CookiePolicySample case "/RemoveTempCookie": context.Response.Cookies.Delete("Temp"); break; + case "/CreateEssentialCookie": + context.Response.Cookies.Append("EssentialCookie", "2", + new CookieOptions() { IsEssential = true }); + break; + case "/RemoveEssentialCookie": + context.Response.Cookies.Delete("EssentialCookie"); + break; case "/GrantConsent": context.Features.Get().GrantConsent(); break; @@ -84,6 +91,8 @@ namespace CookiePolicySample await response.WriteAsync($"Logout
\r\n"); await response.WriteAsync($"Create Temp Cookie
\r\n"); await response.WriteAsync($"Remove Temp Cookie
\r\n"); + await response.WriteAsync($"Create Essential Cookie
\r\n"); + await response.WriteAsync($"Remove Essential Cookie
\r\n"); await response.WriteAsync($"Grant Consent
\r\n"); await response.WriteAsync($"Withdraw Consent
\r\n"); await response.WriteAsync("
\r\n"); diff --git a/src/Security/samples/CookieSample/CookieSample.csproj b/src/Security/samples/CookieSample/CookieSample.csproj index 193137b861..1029f18193 100644 --- a/src/Security/samples/CookieSample/CookieSample.csproj +++ b/src/Security/samples/CookieSample/CookieSample.csproj @@ -1,7 +1,7 @@ - + - net461;netcoreapp2.1 + net461;netcoreapp2.2 diff --git a/src/Security/samples/CookieSessionSample/CookieSessionSample.csproj b/src/Security/samples/CookieSessionSample/CookieSessionSample.csproj index 6241edd667..19bd043746 100644 --- a/src/Security/samples/CookieSessionSample/CookieSessionSample.csproj +++ b/src/Security/samples/CookieSessionSample/CookieSessionSample.csproj @@ -1,7 +1,7 @@ - + - net461;netcoreapp2.1 + net461;netcoreapp2.2 diff --git a/src/Security/samples/JwtBearerSample/JwtBearerSample.csproj b/src/Security/samples/JwtBearerSample/JwtBearerSample.csproj index 84b436581a..3e0192a233 100644 --- a/src/Security/samples/JwtBearerSample/JwtBearerSample.csproj +++ b/src/Security/samples/JwtBearerSample/JwtBearerSample.csproj @@ -1,7 +1,7 @@ - + - net461;netcoreapp2.1 + net461;netcoreapp2.2 aspnet5-JwtBearerSample-20151210102827 diff --git a/src/Security/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj b/src/Security/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj index b14b9590f5..2abd57cd89 100644 --- a/src/Security/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj +++ b/src/Security/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj @@ -1,7 +1,7 @@ - + - net461;netcoreapp2.1 + net461;netcoreapp2.2 aspnet5-OpenIdConnectSample-20151210110318 diff --git a/src/Security/samples/OpenIdConnectSample/OpenIdConnectSample.csproj b/src/Security/samples/OpenIdConnectSample/OpenIdConnectSample.csproj index 23e87d4f2a..bb02ef1595 100644 --- a/src/Security/samples/OpenIdConnectSample/OpenIdConnectSample.csproj +++ b/src/Security/samples/OpenIdConnectSample/OpenIdConnectSample.csproj @@ -1,7 +1,7 @@ - + - net461;netcoreapp2.1 + net461;netcoreapp2.2 aspnet5-OpenIdConnectSample-20151210110318 diff --git a/src/Security/samples/SocialSample/SocialSample.csproj b/src/Security/samples/SocialSample/SocialSample.csproj index a423ae21a3..a08b9799d1 100644 --- a/src/Security/samples/SocialSample/SocialSample.csproj +++ b/src/Security/samples/SocialSample/SocialSample.csproj @@ -1,7 +1,7 @@ - + - net461;netcoreapp2.1 + net461;netcoreapp2.2 aspnet5-SocialSample-20151210111056 diff --git a/src/Security/samples/SocialSample/Startup.cs b/src/Security/samples/SocialSample/Startup.cs index 35896e84b1..b5032072bd 100644 --- a/src/Security/samples/SocialSample/Startup.cs +++ b/src/Security/samples/SocialSample/Startup.cs @@ -59,6 +59,7 @@ namespace SocialSample .AddCookie(o => o.LoginPath = new PathString("/login")) // You must first create an app with Facebook and add its ID and Secret to your user-secrets. // https://developers.facebook.com/apps/ + // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login .AddFacebook(o => { o.AppId = Configuration["facebook:appid"]; @@ -74,6 +75,8 @@ namespace SocialSample }) // You must first create an app with Google and add its ID and Secret to your user-secrets. // https://console.developers.google.com/project + // https://developers.google.com/identity/protocols/OAuth2WebServer + // https://developers.google.com/+/web/people/ .AddOAuth("Google-AccessToken", "Google AccessToken only", o => { o.ClientId = Configuration["google:clientid"]; @@ -92,6 +95,8 @@ namespace SocialSample }) // You must first create an app with Google and add its ID and Secret to your user-secrets. // https://console.developers.google.com/project + // https://developers.google.com/identity/protocols/OAuth2WebServer + // https://developers.google.com/+/web/people/ .AddGoogle(o => { o.ClientId = Configuration["google:clientid"]; @@ -108,6 +113,7 @@ namespace SocialSample }) // You must first create an app with Twitter and add its key and Secret to your user-secrets. // https://apps.twitter.com/ + // https://developer.twitter.com/en/docs/basics/authentication/api-reference/access_token .AddTwitter(o => { o.ConsumerKey = Configuration["twitter:consumerkey"]; diff --git a/src/Security/samples/WsFedSample/WsFedSample.csproj b/src/Security/samples/WsFedSample/WsFedSample.csproj index bc3a59f10e..4fb38fa3e3 100644 --- a/src/Security/samples/WsFedSample/WsFedSample.csproj +++ b/src/Security/samples/WsFedSample/WsFedSample.csproj @@ -1,7 +1,7 @@ - net461;netcoreapp2.0 + net461 diff --git a/src/Security/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookDefaults.cs b/src/Security/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookDefaults.cs index 92d1d003e6..ff9bb40bf1 100644 --- a/src/Security/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookDefaults.cs +++ b/src/Security/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookDefaults.cs @@ -9,10 +9,11 @@ namespace Microsoft.AspNetCore.Authentication.Facebook public static readonly string DisplayName = "Facebook"; - public static readonly string AuthorizationEndpoint = "https://www.facebook.com/v2.12/dialog/oauth"; + // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login + public static readonly string AuthorizationEndpoint = "https://www.facebook.com/v3.1/dialog/oauth"; - public static readonly string TokenEndpoint = "https://graph.facebook.com/v2.12/oauth/access_token"; + public static readonly string TokenEndpoint = "https://graph.facebook.com/v3.1/oauth/access_token"; - public static readonly string UserInformationEndpoint = "https://graph.facebook.com/v2.12/me"; + public static readonly string UserInformationEndpoint = "https://graph.facebook.com/v3.1/me"; } } diff --git a/src/Security/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookOptions.cs b/src/Security/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookOptions.cs index 7010bb20aa..c2078a017b 100644 --- a/src/Security/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookOptions.cs +++ b/src/Security/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookOptions.cs @@ -26,7 +26,6 @@ namespace Microsoft.AspNetCore.Authentication.Facebook AuthorizationEndpoint = FacebookDefaults.AuthorizationEndpoint; TokenEndpoint = FacebookDefaults.TokenEndpoint; UserInformationEndpoint = FacebookDefaults.UserInformationEndpoint; - Scope.Add("public_profile"); Scope.Add("email"); Fields.Add("name"); Fields.Add("email"); diff --git a/src/Security/src/Microsoft.AspNetCore.Authentication.Google/GoogleDefaults.cs b/src/Security/src/Microsoft.AspNetCore.Authentication.Google/GoogleDefaults.cs index 0428703180..26b3b8f01c 100644 --- a/src/Security/src/Microsoft.AspNetCore.Authentication.Google/GoogleDefaults.cs +++ b/src/Security/src/Microsoft.AspNetCore.Authentication.Google/GoogleDefaults.cs @@ -12,10 +12,12 @@ namespace Microsoft.AspNetCore.Authentication.Google public static readonly string DisplayName = "Google"; + // https://developers.google.com/identity/protocols/OAuth2WebServer public static readonly string AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth"; public static readonly string TokenEndpoint = "https://www.googleapis.com/oauth2/v4/token"; + // https://developers.google.com/+/web/people/ public static readonly string UserInformationEndpoint = "https://www.googleapis.com/plus/v1/people/me"; } } diff --git a/src/Security/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs b/src/Security/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs index 6d5c7f5f5e..452d9639f4 100644 --- a/src/Security/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs +++ b/src/Security/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs @@ -32,8 +32,8 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer /// protected new JwtBearerEvents Events { - get { return (JwtBearerEvents)base.Events; } - set { base.Events = value; } + get => (JwtBearerEvents)base.Events; + set => base.Events = value; } protected override Task CreateEventsAsync() => Task.FromResult(new JwtBearerEvents()); @@ -267,9 +267,8 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer private static string CreateErrorDescription(Exception authFailure) { IEnumerable exceptions; - if (authFailure is AggregateException) + if (authFailure is AggregateException agEx) { - var agEx = authFailure as AggregateException; exceptions = agEx.InnerExceptions; } else @@ -283,37 +282,32 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer { // Order sensitive, some of these exceptions derive from others // and we want to display the most specific message possible. - if (ex is SecurityTokenInvalidAudienceException) + switch (ex) { - messages.Add("The audience is invalid"); - } - else if (ex is SecurityTokenInvalidIssuerException) - { - messages.Add("The issuer is invalid"); - } - else if (ex is SecurityTokenNoExpirationException) - { - messages.Add("The token has no expiration"); - } - else if (ex is SecurityTokenInvalidLifetimeException) - { - messages.Add("The token lifetime is invalid"); - } - else if (ex is SecurityTokenNotYetValidException) - { - messages.Add("The token is not valid yet"); - } - else if (ex is SecurityTokenExpiredException) - { - messages.Add("The token is expired"); - } - else if (ex is SecurityTokenSignatureKeyNotFoundException) - { - messages.Add("The signature key was not found"); - } - else if (ex is SecurityTokenInvalidSignatureException) - { - messages.Add("The signature is invalid"); + case SecurityTokenInvalidAudienceException _: + messages.Add("The audience is invalid"); + break; + case SecurityTokenInvalidIssuerException _: + messages.Add("The issuer is invalid"); + break; + case SecurityTokenNoExpirationException _: + messages.Add("The token has no expiration"); + break; + case SecurityTokenInvalidLifetimeException _: + messages.Add("The token lifetime is invalid"); + break; + case SecurityTokenNotYetValidException _: + messages.Add("The token is not valid yet"); + break; + case SecurityTokenExpiredException _: + messages.Add("The token is expired"); + break; + case SecurityTokenSignatureKeyNotFoundException _: + messages.Add("The signature key was not found"); + break; + case SecurityTokenInvalidSignatureException _: + messages.Add("The signature is invalid"); + break; } } diff --git a/src/Security/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountDefaults.cs b/src/Security/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountDefaults.cs index 1b0859c5b7..0421fa14b4 100644 --- a/src/Security/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountDefaults.cs +++ b/src/Security/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountDefaults.cs @@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount public static readonly string DisplayName = "Microsoft"; + // https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_user public static readonly string AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"; public static readonly string TokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token"; diff --git a/src/Security/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterDefaults.cs b/src/Security/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterDefaults.cs index a39a3f0367..bdab80e59d 100644 --- a/src/Security/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterDefaults.cs +++ b/src/Security/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterDefaults.cs @@ -8,5 +8,14 @@ namespace Microsoft.AspNetCore.Authentication.Twitter public const string AuthenticationScheme = "Twitter"; public static readonly string DisplayName = "Twitter"; + + // https://developer.twitter.com/en/docs/basics/authentication/api-reference/request_token + internal const string RequestTokenEndpoint = "https://api.twitter.com/oauth/request_token"; + + // https://developer.twitter.com/en/docs/basics/authentication/api-reference/authenticate + internal const string AuthenticationEndpoint = "https://api.twitter.com/oauth/authenticate?oauth_token="; + + // https://developer.twitter.com/en/docs/basics/authentication/api-reference/access_token + internal const string AccessTokenEndpoint = "https://api.twitter.com/oauth/access_token"; } } diff --git a/src/Security/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs b/src/Security/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs index 670e76f7e3..51dabbd99c 100644 --- a/src/Security/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs +++ b/src/Security/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs @@ -22,9 +22,6 @@ namespace Microsoft.AspNetCore.Authentication.Twitter public class TwitterHandler : RemoteAuthenticationHandler { private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - private const string RequestTokenEndpoint = "https://api.twitter.com/oauth/request_token"; - private const string AuthenticationEndpoint = "https://api.twitter.com/oauth/authenticate?oauth_token="; - private const string AccessTokenEndpoint = "https://api.twitter.com/oauth/access_token"; private HttpClient Backchannel => Options.Backchannel; @@ -138,7 +135,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter // If CallbackConfirmed is false, this will throw var requestToken = await ObtainRequestTokenAsync(BuildRedirectUri(Options.CallbackPath), properties); - var twitterAuthenticationEndpoint = AuthenticationEndpoint + requestToken.Token; + var twitterAuthenticationEndpoint = TwitterDefaults.AuthenticationEndpoint + requestToken.Token; var cookieOptions = Options.StateCookie.Build(Context, Clock.UtcNow); @@ -148,53 +145,92 @@ namespace Microsoft.AspNetCore.Authentication.Twitter await Events.RedirectToAuthorizationEndpoint(redirectContext); } - private async Task ObtainRequestTokenAsync(string callBackUri, AuthenticationProperties properties) + private async Task ExecuteRequestAsync(string url, HttpMethod httpMethod, RequestToken accessToken = null, Dictionary extraOAuthPairs = null, Dictionary queryParameters = null, Dictionary formData = null) { - Logger.ObtainRequestToken(); - - var nonce = Guid.NewGuid().ToString("N"); - - var authorizationParts = new SortedDictionary + var authorizationParts = new SortedDictionary(extraOAuthPairs ?? new Dictionary()) { - { "oauth_callback", callBackUri }, { "oauth_consumer_key", Options.ConsumerKey }, - { "oauth_nonce", nonce }, + { "oauth_nonce", Guid.NewGuid().ToString("N") }, { "oauth_signature_method", "HMAC-SHA1" }, { "oauth_timestamp", GenerateTimeStamp() }, { "oauth_version", "1.0" } }; - var parameterBuilder = new StringBuilder(); - foreach (var authorizationKey in authorizationParts) + if (accessToken != null) { - parameterBuilder.AppendFormat("{0}={1}&", UrlEncoder.Encode(authorizationKey.Key), UrlEncoder.Encode(authorizationKey.Value)); + authorizationParts.Add("oauth_token", accessToken.Token); + } + + var signatureParts = new SortedDictionary(authorizationParts); + if (queryParameters != null) + { + foreach (var queryParameter in queryParameters) + { + signatureParts.Add(queryParameter.Key, queryParameter.Value); + } + } + if (formData != null) + { + foreach (var formItem in formData) + { + signatureParts.Add(formItem.Key, formItem.Value); + } + } + + var parameterBuilder = new StringBuilder(); + foreach (var signaturePart in signatureParts) + { + parameterBuilder.AppendFormat("{0}={1}&", Uri.EscapeDataString(signaturePart.Key), Uri.EscapeDataString(signaturePart.Value)); } parameterBuilder.Length--; var parameterString = parameterBuilder.ToString(); var canonicalizedRequestBuilder = new StringBuilder(); - canonicalizedRequestBuilder.Append(HttpMethod.Post.Method); + canonicalizedRequestBuilder.Append(httpMethod.Method); canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(RequestTokenEndpoint)); + canonicalizedRequestBuilder.Append(Uri.EscapeDataString(url)); canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(parameterString)); + canonicalizedRequestBuilder.Append(Uri.EscapeDataString(parameterString)); - var signature = ComputeSignature(Options.ConsumerSecret, null, canonicalizedRequestBuilder.ToString()); + var signature = ComputeSignature(Options.ConsumerSecret, accessToken?.TokenSecret, canonicalizedRequestBuilder.ToString()); authorizationParts.Add("oauth_signature", signature); + var queryString = ""; + if (queryParameters != null) + { + var queryStringBuilder = new StringBuilder("?"); + foreach (var queryParam in queryParameters) + { + queryStringBuilder.AppendFormat("{0}={1}&", queryParam.Key, queryParam.Value); + } + queryStringBuilder.Length--; + queryString = queryStringBuilder.ToString(); + } + var authorizationHeaderBuilder = new StringBuilder(); authorizationHeaderBuilder.Append("OAuth "); foreach (var authorizationPart in authorizationParts) { - authorizationHeaderBuilder.AppendFormat( - "{0}=\"{1}\", ", authorizationPart.Key, UrlEncoder.Encode(authorizationPart.Value)); + authorizationHeaderBuilder.AppendFormat("{0}=\"{1}\",", authorizationPart.Key, Uri.EscapeDataString(authorizationPart.Value)); } - authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2; + authorizationHeaderBuilder.Length--; - var request = new HttpRequestMessage(HttpMethod.Post, RequestTokenEndpoint); + var request = new HttpRequestMessage(httpMethod, url + queryString); request.Headers.Add("Authorization", authorizationHeaderBuilder.ToString()); - var response = await Backchannel.SendAsync(request, Context.RequestAborted); + if (formData != null) + { + request.Content = new FormUrlEncodedContent(formData); + } + + return await Backchannel.SendAsync(request, Context.RequestAborted); + } + + private async Task ObtainRequestTokenAsync(string callBackUri, AuthenticationProperties properties) + { + Logger.ObtainRequestToken(); + + var response = await ExecuteRequestAsync(TwitterDefaults.RequestTokenEndpoint, HttpMethod.Post, extraOAuthPairs: new Dictionary() { { "oauth_callback", callBackUri } }); response.EnsureSuccessStatusCode(); var responseText = await response.Content.ReadAsStringAsync(); @@ -213,58 +249,8 @@ namespace Microsoft.AspNetCore.Authentication.Twitter Logger.ObtainAccessToken(); - var nonce = Guid.NewGuid().ToString("N"); - - var authorizationParts = new SortedDictionary - { - { "oauth_consumer_key", Options.ConsumerKey }, - { "oauth_nonce", nonce }, - { "oauth_signature_method", "HMAC-SHA1" }, - { "oauth_token", token.Token }, - { "oauth_timestamp", GenerateTimeStamp() }, - { "oauth_verifier", verifier }, - { "oauth_version", "1.0" }, - }; - - var parameterBuilder = new StringBuilder(); - foreach (var authorizationKey in authorizationParts) - { - parameterBuilder.AppendFormat("{0}={1}&", UrlEncoder.Encode(authorizationKey.Key), UrlEncoder.Encode(authorizationKey.Value)); - } - parameterBuilder.Length--; - var parameterString = parameterBuilder.ToString(); - - var canonicalizedRequestBuilder = new StringBuilder(); - canonicalizedRequestBuilder.Append(HttpMethod.Post.Method); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(AccessTokenEndpoint)); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(parameterString)); - - var signature = ComputeSignature(Options.ConsumerSecret, token.TokenSecret, canonicalizedRequestBuilder.ToString()); - authorizationParts.Add("oauth_signature", signature); - authorizationParts.Remove("oauth_verifier"); - - var authorizationHeaderBuilder = new StringBuilder(); - authorizationHeaderBuilder.Append("OAuth "); - foreach (var authorizationPart in authorizationParts) - { - authorizationHeaderBuilder.AppendFormat( - "{0}=\"{1}\", ", authorizationPart.Key, UrlEncoder.Encode(authorizationPart.Value)); - } - authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2; - - var request = new HttpRequestMessage(HttpMethod.Post, AccessTokenEndpoint); - request.Headers.Add("Authorization", authorizationHeaderBuilder.ToString()); - - var formPairs = new Dictionary() - { - { "oauth_verifier", verifier }, - }; - - request.Content = new FormUrlEncodedContent(formPairs); - - var response = await Backchannel.SendAsync(request, Context.RequestAborted); + var formPost = new Dictionary { { "oauth_verifier", verifier } }; + var response = await ExecuteRequestAsync(TwitterDefaults.AccessTokenEndpoint, HttpMethod.Post, token, formData: formPost); if (!response.IsSuccessStatusCode) { @@ -289,53 +275,8 @@ namespace Microsoft.AspNetCore.Authentication.Twitter { Logger.RetrieveUserDetails(); - var nonce = Guid.NewGuid().ToString("N"); + var response = await ExecuteRequestAsync("https://api.twitter.com/1.1/account/verify_credentials.json", HttpMethod.Get, accessToken, queryParameters: new Dictionary() { { "include_email", "true" } }); - var authorizationParts = new SortedDictionary - { - { "oauth_consumer_key", Options.ConsumerKey }, - { "oauth_nonce", nonce }, - { "oauth_signature_method", "HMAC-SHA1" }, - { "oauth_timestamp", GenerateTimeStamp() }, - { "oauth_token", accessToken.Token }, - { "oauth_version", "1.0" } - }; - - var parameterBuilder = new StringBuilder(); - foreach (var authorizationKey in authorizationParts) - { - parameterBuilder.AppendFormat("{0}={1}&", UrlEncoder.Encode(authorizationKey.Key), UrlEncoder.Encode(authorizationKey.Value)); - } - parameterBuilder.Length--; - var parameterString = parameterBuilder.ToString(); - - var resource_url = "https://api.twitter.com/1.1/account/verify_credentials.json"; - var resource_query = "include_email=true"; - var canonicalizedRequestBuilder = new StringBuilder(); - canonicalizedRequestBuilder.Append(HttpMethod.Get.Method); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(resource_url)); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(resource_query)); - canonicalizedRequestBuilder.Append("%26"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(parameterString)); - - var signature = ComputeSignature(Options.ConsumerSecret, accessToken.TokenSecret, canonicalizedRequestBuilder.ToString()); - authorizationParts.Add("oauth_signature", signature); - - var authorizationHeaderBuilder = new StringBuilder(); - authorizationHeaderBuilder.Append("OAuth "); - foreach (var authorizationPart in authorizationParts) - { - authorizationHeaderBuilder.AppendFormat( - "{0}=\"{1}\", ", authorizationPart.Key, UrlEncoder.Encode(authorizationPart.Value)); - } - authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2; - - var request = new HttpRequestMessage(HttpMethod.Get, resource_url + "?include_email=true"); - request.Headers.Add("Authorization", authorizationHeaderBuilder.ToString()); - - var response = await Backchannel.SendAsync(request, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("Email request failed with a status code of " + response.StatusCode); @@ -361,8 +302,8 @@ namespace Microsoft.AspNetCore.Authentication.Twitter algorithm.Key = Encoding.ASCII.GetBytes( string.Format(CultureInfo.InvariantCulture, "{0}&{1}", - UrlEncoder.Encode(consumerSecret), - string.IsNullOrEmpty(tokenSecret) ? string.Empty : UrlEncoder.Encode(tokenSecret))); + Uri.EscapeDataString(consumerSecret), + string.IsNullOrEmpty(tokenSecret) ? string.Empty : Uri.EscapeDataString(tokenSecret))); var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(signatureData)); return Convert.ToBase64String(hash); } diff --git a/src/Security/test/Directory.Build.props b/src/Security/test/Directory.Build.props index b842a48317..e3d2d9f143 100644 --- a/src/Security/test/Directory.Build.props +++ b/src/Security/test/Directory.Build.props @@ -1,10 +1,10 @@ - + - netcoreapp2.1 + netcoreapp2.2 $(DeveloperBuildTestTfms) - $(StandardTestTfms);netcoreapp2.0 + $(StandardTestTfms) $(StandardTestTfms);net461 diff --git a/src/Security/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs b/src/Security/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs index b909be9fdc..0a5440e405 100644 --- a/src/Security/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs +++ b/src/Security/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs @@ -673,7 +673,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook var transaction = await server.SendAsync("http://example.com/base/login"); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); var location = transaction.Response.Headers.Location.AbsoluteUri; - Assert.Contains("https://www.facebook.com/v2.12/dialog/oauth", location); + Assert.Contains("https://www.facebook.com/v3.1/dialog/oauth", location); Assert.Contains("response_type=code", location); Assert.Contains("client_id=", location); Assert.Contains("redirect_uri=" + UrlEncoder.Default.Encode("http://example.com/base/signin-facebook"), location); @@ -705,7 +705,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook var transaction = await server.SendAsync("http://example.com/login"); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); var location = transaction.Response.Headers.Location.AbsoluteUri; - Assert.Contains("https://www.facebook.com/v2.12/dialog/oauth", location); + Assert.Contains("https://www.facebook.com/v3.1/dialog/oauth", location); Assert.Contains("response_type=code", location); Assert.Contains("client_id=", location); Assert.Contains("redirect_uri=" + UrlEncoder.Default.Encode("http://example.com/signin-facebook"), location); @@ -739,7 +739,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook var transaction = await server.SendAsync("http://example.com/challenge"); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); var location = transaction.Response.Headers.Location.AbsoluteUri; - Assert.Contains("https://www.facebook.com/v2.12/dialog/oauth", location); + Assert.Contains("https://www.facebook.com/v3.1/dialog/oauth", location); Assert.Contains("response_type=code", location); Assert.Contains("client_id=", location); Assert.Contains("redirect_uri=", location); diff --git a/src/Security/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs b/src/Security/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs index a1e0233f3a..eb045b4381 100644 --- a/src/Security/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs +++ b/src/Security/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs @@ -195,7 +195,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect ValidateState(actualValues, errors, htmlEncoded); break; case OpenIdConnectParameterNames.SkuTelemetry: - ValidateSkuTelemetry(actualValues, errors, htmlEncoded); + ValidateSkuTelemetry(actualValues, errors); break; case OpenIdConnectParameterNames.VersionTelemetry: ValidateVersionTelemetry(actualValues, errors, htmlEncoded); @@ -258,14 +258,13 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect private void ValidateState(IDictionary actualParams, ICollection errors, bool htmlEncoded) => ValidateParameter(OpenIdConnectParameterNames.State, ExpectedState, actualParams, errors, htmlEncoded); - private void ValidateSkuTelemetry(IDictionary actualParams, ICollection errors, bool htmlEncoded) => -#if NETCOREAPP2_0 || NETCOREAPP2_1 - ValidateParameter(OpenIdConnectParameterNames.SkuTelemetry, "ID_NETSTANDARD1_4", actualParams, errors, htmlEncoded); -#elif NET461 - ValidateParameter(OpenIdConnectParameterNames.SkuTelemetry, "ID_NET451", actualParams, errors, htmlEncoded); -#else -#error Invalid target framework. -#endif + private static void ValidateSkuTelemetry(IDictionary actualParams, ICollection errors) + { + if (!actualParams.ContainsKey(OpenIdConnectParameterNames.SkuTelemetry)) + { + errors.Add($"Parameter {OpenIdConnectParameterNames.SkuTelemetry} is missing"); + } + } private void ValidateVersionTelemetry(IDictionary actualParams, ICollection errors, bool htmlEncoded) => ValidateParameter(OpenIdConnectParameterNames.VersionTelemetry, typeof(OpenIdConnectMessage).GetTypeInfo().Assembly.GetName().Version.ToString(), actualParams, errors, htmlEncoded); diff --git a/src/Security/version.props b/src/Security/version.props index 478dfd16ed..4889a26987 100644 --- a/src/Security/version.props +++ b/src/Security/version.props @@ -1,6 +1,6 @@ - + - 2.1.2 + 2.2.0 rtm $(VersionPrefix) $(VersionPrefix)-$(VersionSuffix)-final