Replace JObject with JsonDocument in Authentication (#7105)

This commit is contained in:
Chris Ross 2019-02-05 13:37:20 -08:00 committed by GitHub
parent d630a20d1a
commit 67037a0039
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 521 additions and 340 deletions

View File

@ -21,7 +21,7 @@ namespace MusicStore.Mocks.Facebook
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == "Id", ""); Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == "Id", "");
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst("urn:facebook:link")?.Value == "https://www.facebook.com/myLink", ""); Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst("urn:facebook:link")?.Value == "https://www.facebook.com/myLink", "");
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Name)?.Value == "AspnetvnextTest AspnetvnextTest", ""); Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Name)?.Value == "AspnetvnextTest AspnetvnextTest", "");
Helpers.ThrowIfConditionFailed(() => context.User.SelectToken("id").ToString() == context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value, ""); Helpers.ThrowIfConditionFailed(() => context.User.GetString("id") == context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value, "");
Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(100), ""); Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(100), "");
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "ValidAccessToken", ""); Helpers.ThrowIfConditionFailed(() => context.AccessToken == "ValidAccessToken", "");
context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false")); context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false"));

View File

@ -23,7 +23,6 @@ namespace MusicStore.Mocks.Google
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Surname)?.Value == "AspnetvnextTest", "FamilyName is not valid"); Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Surname)?.Value == "AspnetvnextTest", "FamilyName is not valid");
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Name)?.Value == "AspnetvnextTest AspnetvnextTest", "Name is not valid"); Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Name)?.Value == "AspnetvnextTest AspnetvnextTest", "Name is not valid");
Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(1200), "ExpiresIn is not valid"); Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(1200), "ExpiresIn is not valid");
Helpers.ThrowIfConditionFailed(() => context.User != null, "User object is not valid");
context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false")); context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false"));
} }

View File

@ -23,8 +23,7 @@ namespace MusicStore.Mocks.MicrosoftAccount
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == "fccf9a24999f4f4f", "Id is not valid"); Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == "fccf9a24999f4f4f", "Id is not valid");
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Name)?.Value == "AspnetvnextTest AspnetvnextTest", "Name is not valid"); Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Name)?.Value == "AspnetvnextTest AspnetvnextTest", "Name is not valid");
Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(3600), "ExpiresIn is not valid"); Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(3600), "ExpiresIn is not valid");
Helpers.ThrowIfConditionFailed(() => context.User != null, "User object is not valid"); Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == context.User.GetString("id"), "User id is not valid");
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == context.User.SelectToken("id").ToString(), "User id is not valid");
context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false")); context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false"));
} }

View File

@ -0,0 +1,2 @@
#launchSettings.json files are required for the auth samples. The ports must not change.
!launchSettings.json

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:1780/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"CookieSample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:1782/"
}
}
}

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:1771/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"CookieSessionSample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:1776/"
}
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication
{
public static class JsonDocumentAuthExtensions
{
public static string GetString(this JsonElement element, string key) =>
element.TryGetProperty(key, out var property)
? property.ToString() : null;
}
}

View File

@ -8,12 +8,12 @@ using System.Security.Claims;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.Facebook namespace Microsoft.AspNetCore.Authentication.Facebook
{ {
@ -41,15 +41,13 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
throw new HttpRequestException($"An error occurred when retrieving Facebook user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Facebook Graph API is enabled."); throw new HttpRequestException($"An error occurred when retrieving Facebook user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Facebook Graph API is enabled.");
} }
var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); using (var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()))
{
var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);
context.RunClaimActions(); context.RunClaimActions();
await Events.CreatingTicket(context);
await Events.CreatingTicket(context); return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
}
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
} }
private string GenerateAppSecretProof(string accessToken) private string GenerateAppSecretProof(string accessToken)

View File

@ -7,12 +7,12 @@ using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.Google namespace Microsoft.AspNetCore.Authentication.Google
{ {
@ -37,13 +37,13 @@ namespace Microsoft.AspNetCore.Authentication.Google
throw new HttpRequestException($"An error occurred when retrieving Google user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Google+ API is enabled."); throw new HttpRequestException($"An error occurred when retrieving Google user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Google+ API is enabled.");
} }
var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); using (var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()))
{
var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);
context.RunClaimActions(); context.RunClaimActions();
await Events.CreatingTicket(context);
await Events.CreatingTicket(context); return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); }
} }
// TODO: Abstract this properties override pattern into the base class? // TODO: Abstract this properties override pattern into the base class?

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:44318/",
"sslPort": 44318
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"SocialSample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:44318/"
}
}
}

View File

@ -1,7 +1,11 @@
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Pipelines;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
@ -10,7 +14,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Newtonsoft.Json.Linq;
namespace JwtBearerSample namespace JwtBearerSample
{ {
@ -93,19 +96,37 @@ namespace JwtBearerSample
{ {
var reader = new StreamReader(context.Request.Body); var reader = new StreamReader(context.Request.Body);
var body = await reader.ReadToEndAsync(); var body = await reader.ReadToEndAsync();
var obj = JObject.Parse(body); using (var json = JsonDocument.Parse(body))
var todo = new Todo() { Description = obj["Description"].Value<string>(), Owner = context.User.Identity.Name }; {
Todos.Add(todo); var obj = json.RootElement;
var todo = new Todo() { Description = obj.GetProperty("Description").GetString(), Owner = context.User.Identity.Name };
Todos.Add(todo);
}
} }
else else
{ {
response.ContentType = "application/json"; response.ContentType = "application/json";
response.Headers[HeaderNames.CacheControl] = "no-cache"; response.Headers[HeaderNames.CacheControl] = "no-cache";
var json = JToken.FromObject(Todos); Serialize(Todos, response.BodyPipe);
await response.WriteAsync(json.ToString()); await response.BodyPipe.FlushAsync();
} }
}); });
}); });
} }
private void Serialize(IList<Todo> todos, IBufferWriter<byte> output)
{
var writer = new Utf8JsonWriter(output);
writer.WriteStartArray();
foreach (var todo in todos)
{
writer.WriteStartObject();
writer.WriteString("Description", todo.Description);
writer.WriteString("Owner", todo.Owner);
writer.WriteEndObject();
}
writer.WriteEndArray();
writer.Flush();
}
} }
} }

View File

@ -5,11 +5,11 @@ using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
{ {
@ -30,13 +30,13 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
throw new HttpRequestException($"An error occurred when retrieving Microsoft user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Microsoft Account API is enabled."); throw new HttpRequestException($"An error occurred when retrieving Microsoft user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Microsoft Account API is enabled.");
} }
var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); using (var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()))
{
var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);
context.RunClaimActions(); context.RunClaimActions();
await Events.CreatingTicket(context);
await Events.CreatingTicket(context); return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); }
} }
} }
} }

View File

@ -2,10 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
{ {
@ -29,7 +27,7 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
ClaimActions.MapJsonKey(ClaimTypes.Name, "displayName"); ClaimActions.MapJsonKey(ClaimTypes.Name, "displayName");
ClaimActions.MapJsonKey(ClaimTypes.GivenName, "givenName"); ClaimActions.MapJsonKey(ClaimTypes.GivenName, "givenName");
ClaimActions.MapJsonKey(ClaimTypes.Surname, "surname"); ClaimActions.MapJsonKey(ClaimTypes.Surname, "surname");
ClaimActions.MapCustomJson(ClaimTypes.Email, user => user.Value<string>("mail") ?? user.Value<string>("userPrincipalName")); ClaimActions.MapCustomJson(ClaimTypes.Email, user => user.GetString("mail") ?? user.GetString("userPrincipalName"));
} }
} }
} }

View File

@ -1,8 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Claims; using System.Security.Claims;
using Newtonsoft.Json.Linq; using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication.OAuth.Claims namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
{ {
@ -37,6 +37,6 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
/// <param name="userData">The source data to examine. This value may be null.</param> /// <param name="userData">The source data to examine. This value may be null.</param>
/// <param name="identity">The identity to add Claims to.</param> /// <param name="identity">The identity to add Claims to.</param>
/// <param name="issuer">The value to use for Claim.Issuer when creating a Claim.</param> /// <param name="issuer">The value to use for Claim.Issuer when creating a Claim.</param>
public abstract void Run(JObject userData, ClaimsIdentity identity, string issuer); public abstract void Run(JsonElement userData, ClaimsIdentity identity, string issuer);
} }
} }

View File

@ -1,10 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Authentication.OAuth.Claims;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication namespace Microsoft.AspNetCore.Authentication
{ {
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="collection"></param> /// <param name="collection"></param>
/// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param> /// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param>
/// <param name="resolver">The Func that will be called to select value from the given json user data.</param> /// <param name="resolver">The Func that will be called to select value from the given json user data.</param>
public static void MapCustomJson(this ClaimActionCollection collection, string claimType, Func<JObject, string> resolver) public static void MapCustomJson(this ClaimActionCollection collection, string claimType, Func<JsonElement, string> resolver)
{ {
collection.MapCustomJson(claimType, ClaimValueTypes.String, resolver); collection.MapCustomJson(claimType, ClaimValueTypes.String, resolver);
} }
@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param> /// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param>
/// <param name="valueType">The value to use for Claim.ValueType when creating a Claim.</param> /// <param name="valueType">The value to use for Claim.ValueType when creating a Claim.</param>
/// <param name="resolver">The Func that will be called to select value from the given json user data.</param> /// <param name="resolver">The Func that will be called to select value from the given json user data.</param>
public static void MapCustomJson(this ClaimActionCollection collection, string claimType, string valueType, Func<JObject, string> resolver) public static void MapCustomJson(this ClaimActionCollection collection, string claimType, string valueType, Func<JsonElement, string> resolver)
{ {
collection.Add(new CustomJsonClaimAction(claimType, valueType, resolver)); collection.Add(new CustomJsonClaimAction(claimType, valueType, resolver));
} }

View File

@ -1,9 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Security.Claims; using System.Security.Claims;
using Newtonsoft.Json.Linq; using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication.OAuth.Claims namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
{ {
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
/// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param> /// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param>
/// <param name="valueType">The value to use for Claim.ValueType when creating a Claim.</param> /// <param name="valueType">The value to use for Claim.ValueType when creating a Claim.</param>
/// <param name="resolver">The Func that will be called to select value from the given json user data.</param> /// <param name="resolver">The Func that will be called to select value from the given json user data.</param>
public CustomJsonClaimAction(string claimType, string valueType, Func<JObject, string> resolver) public CustomJsonClaimAction(string claimType, string valueType, Func<JsonElement, string> resolver)
: base(claimType, valueType) : base(claimType, valueType)
{ {
Resolver = resolver; Resolver = resolver;
@ -27,15 +27,11 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
/// <summary> /// <summary>
/// The Func that will be called to select value from the given json user data. /// The Func that will be called to select value from the given json user data.
/// </summary> /// </summary>
public Func<JObject, string> Resolver { get; } public Func<JsonElement, string> Resolver { get; }
/// <inheritdoc /> /// <inheritdoc />
public override void Run(JObject userData, ClaimsIdentity identity, string issuer) public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer)
{ {
if (userData == null)
{
return;
}
var value = Resolver(userData); var value = Resolver(userData);
if (!string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(value))
{ {

View File

@ -1,9 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using Newtonsoft.Json.Linq; using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication.OAuth.Claims namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
{ {
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Run(JObject userData, ClaimsIdentity identity, string issuer) public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer)
{ {
foreach (var claim in identity.FindAll(ClaimType).ToList()) foreach (var claim in identity.FindAll(ClaimType).ToList())
{ {

View File

@ -5,8 +5,8 @@ using System;
using System.Globalization; using System.Globalization;
using System.Net.Http; using System.Net.Http;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.OAuth namespace Microsoft.AspNetCore.Authentication.OAuth
{ {
@ -15,27 +15,6 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// </summary> /// </summary>
public class OAuthCreatingTicketContext : ResultContext<OAuthOptions> public class OAuthCreatingTicketContext : ResultContext<OAuthOptions>
{ {
/// <summary>
/// Initializes a new <see cref="OAuthCreatingTicketContext"/>.
/// </summary>
/// <param name="principal">The <see cref="ClaimsPrincipal"/>.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="context">The HTTP environment.</param>
/// <param name="scheme">The authentication scheme.</param>
/// <param name="options">The options used by the authentication middleware.</param>
/// <param name="backchannel">The HTTP client used by the authentication middleware</param>
/// <param name="tokens">The tokens returned from the token endpoint.</param>
public OAuthCreatingTicketContext(
ClaimsPrincipal principal,
AuthenticationProperties properties,
HttpContext context,
AuthenticationScheme scheme,
OAuthOptions options,
HttpClient backchannel,
OAuthTokenResponse tokens)
: this(principal, properties, context, scheme, options, backchannel, tokens, user: new JObject())
{ }
/// <summary> /// <summary>
/// Initializes a new <see cref="OAuthCreatingTicketContext"/>. /// Initializes a new <see cref="OAuthCreatingTicketContext"/>.
/// </summary> /// </summary>
@ -55,7 +34,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
OAuthOptions options, OAuthOptions options,
HttpClient backchannel, HttpClient backchannel,
OAuthTokenResponse tokens, OAuthTokenResponse tokens,
JObject user) JsonElement user)
: base(context, scheme, options) : base(context, scheme, options)
{ {
if (backchannel == null) if (backchannel == null)
@ -68,11 +47,6 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
throw new ArgumentNullException(nameof(tokens)); throw new ArgumentNullException(nameof(tokens));
} }
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
TokenResponse = tokens; TokenResponse = tokens;
Backchannel = backchannel; Backchannel = backchannel;
User = user; User = user;
@ -82,9 +56,9 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// <summary> /// <summary>
/// Gets the JSON-serialized user or an empty /// Gets the JSON-serialized user or an empty
/// <see cref="JObject"/> if it is not available. /// <see cref="JsonElement"/> if it is not available.
/// </summary> /// </summary>
public JObject User { get; } public JsonElement User { get; }
/// <summary> /// <summary>
/// Gets the token response returned by the authentication service. /// Gets the token response returned by the authentication service.
@ -136,17 +110,12 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
public void RunClaimActions() => RunClaimActions(User); public void RunClaimActions() => RunClaimActions(User);
public void RunClaimActions(JObject userData) public void RunClaimActions(JsonElement userData)
{ {
if (userData == null)
{
throw new ArgumentNullException(nameof(userData));
}
foreach (var action in Options.ClaimActions) foreach (var action in Options.ClaimActions)
{ {
action.Run(userData, Identity, Options.ClaimsIssuer ?? Scheme.Name); action.Run(userData, Identity, Options.ClaimsIssuer ?? Scheme.Name);
} }
} }
} }
} }

View File

@ -1,8 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Claims; using System.Security.Claims;
using Newtonsoft.Json.Linq; using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication.OAuth.Claims namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
{ {
@ -30,20 +30,27 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
public string JsonKey { get; } public string JsonKey { get; }
/// <inheritdoc /> /// <inheritdoc />
public override void Run(JObject userData, ClaimsIdentity identity, string issuer) public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer)
{ {
var value = userData?[JsonKey]; if (!userData.TryGetProperty(JsonKey, out var value))
if (value is JValue)
{ {
AddClaim(value?.ToString(), identity, issuer); return;
} }
else if (value is JArray) if (value.Type == JsonValueType.Array)
{ {
foreach (var v in value) foreach (var v in value.EnumerateArray())
{ {
AddClaim(v?.ToString(), identity, issuer); AddClaim(v.ToString(), identity, issuer);
} }
} }
else if (value.Type == JsonValueType.Object || value.Type == JsonValueType.Undefined)
{
// Skip, because they were previously skipped
}
else
{
AddClaim(value.ToString(), identity, issuer);
}
} }
private void AddClaim(string value, ClaimsIdentity identity, string issuer) private void AddClaim(string value, ClaimsIdentity identity, string issuer)

View File

@ -1,8 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Newtonsoft.Json.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication.OAuth.Claims namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
{ {
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
public string SubKey { get; } public string SubKey { get; }
/// <inheritdoc /> /// <inheritdoc />
public override void Run(JObject userData, ClaimsIdentity identity, string issuer) public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer)
{ {
var value = GetValue(userData, JsonKey, SubKey); var value = GetValue(userData, JsonKey, SubKey);
if (!string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(value))
@ -42,15 +42,12 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
} }
// Get the given subProperty from a property. // Get the given subProperty from a property.
private static string GetValue(JObject userData, string propertyName, string subProperty) private static string GetValue(JsonElement userData, string propertyName, string subProperty)
{ {
if (userData != null && userData.TryGetValue(propertyName, out var value)) if (userData.TryGetProperty(propertyName, out var value)
&& value.Type == JsonValueType.Object && value.TryGetProperty(subProperty, out value))
{ {
var subObject = JObject.Parse(value.ToString()); return value.ToString();
if (subObject != null && subObject.TryGetValue(subProperty, out value))
{
return value.ToString();
}
} }
return null; return null;
} }

View File

@ -1,9 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Security.Claims; using System.Security.Claims;
using Newtonsoft.Json.Linq; using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication.OAuth.Claims namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
{ {
@ -17,24 +17,20 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
{ {
} }
public override void Run(JObject userData, ClaimsIdentity identity, string issuer) public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer)
{ {
if (userData == null) foreach (var pair in userData.EnumerateObject())
{ {
return; var claimValue = pair.Value.ToString();
}
foreach (var pair in userData)
{
var claimValue = userData.TryGetValue(pair.Key, out var value) ? value.ToString() : null;
// Avoid adding a claim if there's a duplicate name and value. This often happens in OIDC when claims are // Avoid adding a claim if there's a duplicate name and value. This often happens in OIDC when claims are
// retrieved both from the id_token and from the user-info endpoint. // retrieved both from the id_token and from the user-info endpoint.
var duplicate = identity.FindFirst(c => string.Equals(c.Type, pair.Key, StringComparison.OrdinalIgnoreCase) var duplicate = identity.FindFirst(c => string.Equals(c.Type, pair.Name, StringComparison.OrdinalIgnoreCase)
&& string.Equals(c.Value, claimValue, StringComparison.Ordinal)) != null; && string.Equals(c.Value, claimValue, StringComparison.Ordinal)) != null;
if (!duplicate) if (!duplicate)
{ {
identity.AddClaim(new Claim(pair.Key, claimValue, ClaimValueTypes.String, issuer)); identity.AddClaim(new Claim(pair.Name, claimValue, ClaimValueTypes.String, issuer));
} }
} }
} }

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>ASP.NET Core middleware that enables an application to support any standard OAuth 2.0 authentication workflow.</Description> <Description>ASP.NET Core middleware that enables an application to support any standard OAuth 2.0 authentication workflow.</Description>
@ -10,7 +10,6 @@
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.AspNetCore.Authentication" /> <Reference Include="Microsoft.AspNetCore.Authentication" />
<Reference Include="Newtonsoft.Json" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -9,13 +9,13 @@ using System.Net.Http.Headers;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.OAuth namespace Microsoft.AspNetCore.Authentication.OAuth
{ {
@ -99,62 +99,63 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
return HandleRequestResult.Fail("Code was not found.", properties); return HandleRequestResult.Fail("Code was not found.", properties);
} }
var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath)); using (var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath)))
if (tokens.Error != null)
{ {
return HandleRequestResult.Fail(tokens.Error, properties); if (tokens.Error != null)
}
if (string.IsNullOrEmpty(tokens.AccessToken))
{
return HandleRequestResult.Fail("Failed to retrieve access token.", properties);
}
var identity = new ClaimsIdentity(ClaimsIssuer);
if (Options.SaveTokens)
{
var authTokens = new List<AuthenticationToken>();
authTokens.Add(new AuthenticationToken { Name = "access_token", Value = tokens.AccessToken });
if (!string.IsNullOrEmpty(tokens.RefreshToken))
{ {
authTokens.Add(new AuthenticationToken { Name = "refresh_token", Value = tokens.RefreshToken }); return HandleRequestResult.Fail(tokens.Error, properties);
} }
if (!string.IsNullOrEmpty(tokens.TokenType)) if (string.IsNullOrEmpty(tokens.AccessToken))
{ {
authTokens.Add(new AuthenticationToken { Name = "token_type", Value = tokens.TokenType }); return HandleRequestResult.Fail("Failed to retrieve access token.", properties);
} }
if (!string.IsNullOrEmpty(tokens.ExpiresIn)) var identity = new ClaimsIdentity(ClaimsIssuer);
if (Options.SaveTokens)
{ {
int value; var authTokens = new List<AuthenticationToken>();
if (int.TryParse(tokens.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
authTokens.Add(new AuthenticationToken { Name = "access_token", Value = tokens.AccessToken });
if (!string.IsNullOrEmpty(tokens.RefreshToken))
{ {
// https://www.w3.org/TR/xmlschema-2/#dateTime authTokens.Add(new AuthenticationToken { Name = "refresh_token", Value = tokens.RefreshToken });
// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx
var expiresAt = Clock.UtcNow + TimeSpan.FromSeconds(value);
authTokens.Add(new AuthenticationToken
{
Name = "expires_at",
Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
});
} }
if (!string.IsNullOrEmpty(tokens.TokenType))
{
authTokens.Add(new AuthenticationToken { Name = "token_type", Value = tokens.TokenType });
}
if (!string.IsNullOrEmpty(tokens.ExpiresIn))
{
int value;
if (int.TryParse(tokens.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
{
// https://www.w3.org/TR/xmlschema-2/#dateTime
// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx
var expiresAt = Clock.UtcNow + TimeSpan.FromSeconds(value);
authTokens.Add(new AuthenticationToken
{
Name = "expires_at",
Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
});
}
}
properties.StoreTokens(authTokens);
} }
properties.StoreTokens(authTokens); var ticket = await CreateTicketAsync(identity, properties, tokens);
} if (ticket != null)
{
var ticket = await CreateTicketAsync(identity, properties, tokens); return HandleRequestResult.Success(ticket);
if (ticket != null) }
{ else
return HandleRequestResult.Success(ticket); {
} return HandleRequestResult.Fail("Failed to retrieve user information from remote server.", properties);
else }
{
return HandleRequestResult.Fail("Failed to retrieve user information from remote server.", properties);
} }
} }
@ -177,7 +178,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted); var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted);
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
return OAuthTokenResponse.Success(payload); return OAuthTokenResponse.Success(payload);
} }
else else
@ -198,9 +199,12 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
protected virtual async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) protected virtual async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
{ {
var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens); using (var user = JsonDocument.Parse("{}"))
await Events.CreatingTicket(context); {
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, user.RootElement);
await Events.CreatingTicket(context);
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
}
} }
protected override async Task HandleChallengeAsync(AuthenticationProperties properties) protected override async Task HandleChallengeAsync(AuthenticationProperties properties)

View File

@ -2,19 +2,20 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using Newtonsoft.Json.Linq; using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication.OAuth namespace Microsoft.AspNetCore.Authentication.OAuth
{ {
public class OAuthTokenResponse public class OAuthTokenResponse : IDisposable
{ {
private OAuthTokenResponse(JObject response) private OAuthTokenResponse(JsonDocument response)
{ {
Response = response; Response = response;
AccessToken = response.Value<string>("access_token"); var root = response.RootElement;
TokenType = response.Value<string>("token_type"); AccessToken = root.GetString("access_token");
RefreshToken = response.Value<string>("refresh_token"); TokenType = root.GetString("token_type");
ExpiresIn = response.Value<string>("expires_in"); RefreshToken = root.GetString("refresh_token");
ExpiresIn = root.GetString("expires_in");
} }
private OAuthTokenResponse(Exception error) private OAuthTokenResponse(Exception error)
@ -22,7 +23,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
Error = error; Error = error;
} }
public static OAuthTokenResponse Success(JObject response) public static OAuthTokenResponse Success(JsonDocument response)
{ {
return new OAuthTokenResponse(response); return new OAuthTokenResponse(response);
} }
@ -32,11 +33,16 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
return new OAuthTokenResponse(error); return new OAuthTokenResponse(error);
} }
public JObject Response { get; set; } public void Dispose()
{
Response?.Dispose();
}
public JsonDocument Response { get; set; }
public string AccessToken { get; set; } public string AccessToken { get; set; }
public string TokenType { get; set; } public string TokenType { get; set; }
public string RefreshToken { get; set; } public string RefreshToken { get; set; }
public string ExpiresIn { get; set; } public string ExpiresIn { get; set; }
public Exception Error { get; set; } public Exception Error { get; set; }
} }
} }

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:44318/",
"sslPort": 44318
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"SocialSample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:44318/"
}
}
}

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:44318/",
"sslPort": 44318
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"SocialSample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:44318/"
}
}
}

View File

@ -5,6 +5,7 @@ using System.IdentityModel.Tokens.Jwt;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
@ -16,7 +17,6 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Newtonsoft.Json.Linq;
namespace OpenIdConnectSample namespace OpenIdConnectSample
{ {
@ -208,30 +208,31 @@ namespace OpenIdConnectSample
var tokenResponse = await options.Backchannel.PostAsync(metadata.TokenEndpoint, content, context.RequestAborted); var tokenResponse = await options.Backchannel.PostAsync(metadata.TokenEndpoint, content, context.RequestAborted);
tokenResponse.EnsureSuccessStatusCode(); tokenResponse.EnsureSuccessStatusCode();
var payload = JObject.Parse(await tokenResponse.Content.ReadAsStringAsync()); using (var payload = JsonDocument.Parse(await tokenResponse.Content.ReadAsStringAsync()))
// Persist the new acess token
props.UpdateTokenValue("access_token", payload.Value<string>("access_token"));
props.UpdateTokenValue("refresh_token", payload.Value<string>("refresh_token"));
if (int.TryParse(payload.Value<string>("expires_in"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var seconds))
{ {
var expiresAt = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(seconds); // Persist the new acess token
props.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture)); props.UpdateTokenValue("access_token", payload.RootElement.GetString("access_token"));
props.UpdateTokenValue("refresh_token", payload.RootElement.GetString("refresh_token"));
if (payload.RootElement.TryGetProperty("expires_in", out var property) && property.TryGetInt32(out var seconds))
{
var expiresAt = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(seconds);
props.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture));
}
await context.SignInAsync(user, props);
await WriteHtmlAsync(response, async res =>
{
await res.WriteAsync($"<h1>Refreshed.</h1>");
await res.WriteAsync("<a class=\"btn btn-default\" href=\"/refresh\">Refresh tokens</a>");
await res.WriteAsync("<a class=\"btn btn-default\" href=\"/\">Home</a>");
await res.WriteAsync("<h2>Tokens:</h2>");
await WriteTableHeader(res, new string[] { "Token Type", "Value" }, props.GetTokens().Select(token => new string[] { token.Name, token.Value }));
await res.WriteAsync("<h2>Payload:</h2>");
await res.WriteAsync(HtmlEncoder.Default.Encode(payload.ToString()).Replace(",", ",<br>") + "<br>");
});
} }
await context.SignInAsync(user, props);
await WriteHtmlAsync(response, async res =>
{
await res.WriteAsync($"<h1>Refreshed.</h1>");
await res.WriteAsync("<a class=\"btn btn-default\" href=\"/refresh\">Refresh tokens</a>");
await res.WriteAsync("<a class=\"btn btn-default\" href=\"/\">Home</a>");
await res.WriteAsync("<h2>Tokens:</h2>");
await WriteTableHeader(res, new string[] { "Token Type", "Value" }, props.GetTokens().Select(token => new string[] { token.Name, token.Value }));
await res.WriteAsync("<h2>Payload:</h2>");
await res.WriteAsync(HtmlEncoder.Default.Encode(payload.ToString()).Replace(",", ",<br>") + "<br>");
});
return; return;
} }

View File

@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
@ -16,6 +16,6 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
public OpenIdConnectMessage ProtocolMessage { get; set; } public OpenIdConnectMessage ProtocolMessage { get; set; }
public JObject User { get; set; } public JsonDocument User { get; set; }
} }
} }

View File

@ -12,6 +12,7 @@ using System.Security.Claims;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -20,7 +21,6 @@ using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
@ -712,10 +712,13 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
} }
else else
{ {
var identity = (ClaimsIdentity)user.Identity; using (var payload = JsonDocument.Parse("{}"))
foreach (var action in Options.ClaimActions)
{ {
action.Run(null, identity, ClaimsIssuer); var identity = (ClaimsIdentity)user.Identity;
foreach (var action in Options.ClaimActions)
{
action.Run(payload.RootElement, identity, ClaimsIssuer);
}
} }
} }
@ -853,42 +856,46 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
responseMessage.EnsureSuccessStatusCode(); responseMessage.EnsureSuccessStatusCode();
var userInfoResponse = await responseMessage.Content.ReadAsStringAsync(); var userInfoResponse = await responseMessage.Content.ReadAsStringAsync();
JObject user; JsonDocument user;
var contentType = responseMessage.Content.Headers.ContentType; var contentType = responseMessage.Content.Headers.ContentType;
if (contentType.MediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase)) if (contentType.MediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase))
{ {
user = JObject.Parse(userInfoResponse); user = JsonDocument.Parse(userInfoResponse);
} }
else if (contentType.MediaType.Equals("application/jwt", StringComparison.OrdinalIgnoreCase)) else if (contentType.MediaType.Equals("application/jwt", StringComparison.OrdinalIgnoreCase))
{ {
var userInfoEndpointJwt = new JwtSecurityToken(userInfoResponse); var userInfoEndpointJwt = new JwtSecurityToken(userInfoResponse);
user = JObject.FromObject(userInfoEndpointJwt.Payload); user = JsonDocument.Parse(userInfoEndpointJwt.Payload.SerializeToJson());
} }
else else
{ {
return HandleRequestResult.Fail("Unknown response type: " + contentType.MediaType, properties); return HandleRequestResult.Fail("Unknown response type: " + contentType.MediaType, properties);
} }
var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(principal, properties, message, user); using (user)
if (userInformationReceivedContext.Result != null)
{ {
return userInformationReceivedContext.Result; var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(principal, properties, message, user);
} if (userInformationReceivedContext.Result != null)
principal = userInformationReceivedContext.Principal; {
properties = userInformationReceivedContext.Properties; return userInformationReceivedContext.Result;
user = userInformationReceivedContext.User; }
principal = userInformationReceivedContext.Principal;
properties = userInformationReceivedContext.Properties;
using (var updatedUser = userInformationReceivedContext.User)
{
Options.ProtocolValidator.ValidateUserInfoResponse(new OpenIdConnectProtocolValidationContext()
{
UserInfoEndpointResponse = userInfoResponse,
ValidatedIdToken = jwt,
});
Options.ProtocolValidator.ValidateUserInfoResponse(new OpenIdConnectProtocolValidationContext() var identity = (ClaimsIdentity)principal.Identity;
{
UserInfoEndpointResponse = userInfoResponse,
ValidatedIdToken = jwt,
});
var identity = (ClaimsIdentity)principal.Identity; foreach (var action in Options.ClaimActions)
{
foreach (var action in Options.ClaimActions) action.Run(user.RootElement, identity, ClaimsIssuer);
{ }
action.Run(user, identity, ClaimsIssuer); }
} }
return HandleRequestResult.Success(new AuthenticationTicket(principal, properties, Scheme.Name)); return HandleRequestResult.Success(new AuthenticationTicket(principal, properties, Scheme.Name));
@ -1144,7 +1151,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
return context; return context;
} }
private async Task<UserInformationReceivedContext> RunUserInformationReceivedEventAsync(ClaimsPrincipal principal, AuthenticationProperties properties, OpenIdConnectMessage message, JObject user) private async Task<UserInformationReceivedContext> RunUserInformationReceivedEventAsync(ClaimsPrincipal principal, AuthenticationProperties properties, OpenIdConnectMessage message, JsonDocument user)
{ {
Logger.UserInformationReceived(user.ToString()); Logger.UserInformationReceived(user.ToString());

View File

@ -1,10 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Authentication.OAuth.Claims;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect.Claims namespace Microsoft.AspNetCore.Authentication.OpenIdConnect.Claims
{ {
@ -27,9 +27,9 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect.Claims
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Run(JObject userData, ClaimsIdentity identity, string issuer) public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer)
{ {
var value = userData?.Value<string>(JsonKey); var value = userData.GetString(JsonKey);
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{ {
// Not found // Not found

View File

@ -1,10 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.Twitter namespace Microsoft.AspNetCore.Authentication.Twitter
{ {
@ -36,14 +35,14 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
string screenName, string screenName,
string accessToken, string accessToken,
string accessTokenSecret, string accessTokenSecret,
JObject user) JsonElement user)
: base(context, scheme, options) : base(context, scheme, options)
{ {
UserId = userId; UserId = userId;
ScreenName = screenName; ScreenName = screenName;
AccessToken = accessToken; AccessToken = accessToken;
AccessTokenSecret = accessTokenSecret; AccessTokenSecret = accessTokenSecret;
User = user ?? new JObject(); User = user;
Principal = principal; Principal = principal;
Properties = properties; Properties = properties;
} }
@ -70,8 +69,8 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
/// <summary> /// <summary>
/// Gets the JSON-serialized user or an empty /// Gets the JSON-serialized user or an empty
/// <see cref="JObject"/> if it is not available. /// <see cref="JsonElement"/> if it is not available.
/// </summary> /// </summary>
public JObject User { get; } public JsonElement User { get; }
} }
} }

View File

@ -9,13 +9,13 @@ using System.Security.Claims;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.Twitter namespace Microsoft.AspNetCore.Authentication.Twitter
{ {
@ -99,25 +99,33 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
}, },
ClaimsIssuer); ClaimsIssuer);
JObject user = null; JsonDocument user;
if (Options.RetrieveUserDetails) if (Options.RetrieveUserDetails)
{ {
user = await RetrieveUserDetailsAsync(accessToken, identity); user = await RetrieveUserDetailsAsync(accessToken, identity);
} }
else
if (Options.SaveTokens)
{ {
properties.StoreTokens(new [] { user = JsonDocument.Parse("{}");
new AuthenticationToken { Name = "access_token", Value = accessToken.Token },
new AuthenticationToken { Name = "access_token_secret", Value = accessToken.TokenSecret }
});
} }
return HandleRequestResult.Success(await CreateTicketAsync(identity, properties, accessToken, user)); using (user)
{
if (Options.SaveTokens)
{
properties.StoreTokens(new[] {
new AuthenticationToken { Name = "access_token", Value = accessToken.Token },
new AuthenticationToken { Name = "access_token_secret", Value = accessToken.TokenSecret }
});
}
var ticket = await CreateTicketAsync(identity, properties, accessToken, user.RootElement);
return HandleRequestResult.Success(ticket);
}
} }
protected virtual async Task<AuthenticationTicket> CreateTicketAsync( protected virtual async Task<AuthenticationTicket> CreateTicketAsync(
ClaimsIdentity identity, AuthenticationProperties properties, AccessToken token, JObject user) ClaimsIdentity identity, AuthenticationProperties properties, AccessToken token, JsonElement user)
{ {
foreach (var action in Options.ClaimActions) foreach (var action in Options.ClaimActions)
{ {
@ -275,7 +283,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
} }
// https://dev.twitter.com/rest/reference/get/account/verify_credentials // https://dev.twitter.com/rest/reference/get/account/verify_credentials
private async Task<JObject> RetrieveUserDetailsAsync(AccessToken accessToken, ClaimsIdentity identity) private async Task<JsonDocument> RetrieveUserDetailsAsync(AccessToken accessToken, ClaimsIdentity identity)
{ {
Logger.RetrieveUserDetails(); Logger.RetrieveUserDetails();
@ -288,7 +296,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
} }
var responseText = await response.Content.ReadAsStringAsync(); var responseText = await response.Content.ReadAsStringAsync();
var result = JObject.Parse(responseText); var result = JsonDocument.Parse(responseText);
return result; return result;
} }

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:44318/",
"sslPort": 44318
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"SocialSample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:44318/"
}
}
}

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:44318/",
"sslPort": 44318
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"SocialSample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:44318/"
}
}
}

View File

@ -6,6 +6,7 @@ using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
@ -21,7 +22,6 @@ using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
namespace SocialSample namespace SocialSample
{ {
@ -207,9 +207,10 @@ namespace SocialSample
var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted); var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var user = JObject.Parse(await response.Content.ReadAsStringAsync()); using (var user = JsonDocument.Parse(await response.Content.ReadAsStringAsync()))
{
context.RunClaimActions(user); context.RunClaimActions(user.RootElement);
}
} }
}; };
}); });
@ -332,24 +333,25 @@ namespace SocialSample
var refreshResponse = await options.Backchannel.PostAsync(options.TokenEndpoint, content, context.RequestAborted); var refreshResponse = await options.Backchannel.PostAsync(options.TokenEndpoint, content, context.RequestAborted);
refreshResponse.EnsureSuccessStatusCode(); refreshResponse.EnsureSuccessStatusCode();
var payload = JObject.Parse(await refreshResponse.Content.ReadAsStringAsync()); using (var payload = JsonDocument.Parse(await refreshResponse.Content.ReadAsStringAsync()))
// Persist the new acess token
authProperties.UpdateTokenValue("access_token", payload.Value<string>("access_token"));
refreshToken = payload.Value<string>("refresh_token");
if (!string.IsNullOrEmpty(refreshToken))
{ {
authProperties.UpdateTokenValue("refresh_token", refreshToken);
}
if (int.TryParse(payload.Value<string>("expires_in"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var seconds))
{
var expiresAt = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(seconds);
authProperties.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture));
}
await context.SignInAsync(user, authProperties);
await PrintRefreshedTokensAsync(response, payload, authProperties); // Persist the new acess token
authProperties.UpdateTokenValue("access_token", payload.RootElement.GetString("access_token"));
refreshToken = payload.RootElement.GetString("refresh_token");
if (!string.IsNullOrEmpty(refreshToken))
{
authProperties.UpdateTokenValue("refresh_token", refreshToken);
}
if (payload.RootElement.TryGetProperty("expires_in", out var property) && property.TryGetInt32(out var seconds))
{
var expiresAt = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(seconds);
authProperties.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture));
}
await context.SignInAsync(user, authProperties);
await PrintRefreshedTokensAsync(response, payload, authProperties);
}
return; return;
} }
// https://developers.facebook.com/docs/facebook-login/access-tokens/expiration-and-extension // https://developers.facebook.com/docs/facebook-login/access-tokens/expiration-and-extension
@ -368,18 +370,18 @@ namespace SocialSample
}.ToQueryString(); }.ToQueryString();
var refreshResponse = await options.Backchannel.GetStringAsync(options.TokenEndpoint + query); var refreshResponse = await options.Backchannel.GetStringAsync(options.TokenEndpoint + query);
var payload = JObject.Parse(refreshResponse); using (var payload = JsonDocument.Parse(refreshResponse))
authProperties.UpdateTokenValue("access_token", payload.Value<string>("access_token"));
if (int.TryParse(payload.Value<string>("expires_in"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var seconds))
{ {
var expiresAt = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(seconds); authProperties.UpdateTokenValue("access_token", payload.RootElement.GetString("access_token"));
authProperties.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture)); if (payload.RootElement.TryGetProperty("expires_in", out var property) && property.TryGetInt32(out var seconds))
{
var expiresAt = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(seconds);
authProperties.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture));
}
await context.SignInAsync(user, authProperties);
await PrintRefreshedTokensAsync(response, payload, authProperties);
} }
await context.SignInAsync(user, authProperties);
await PrintRefreshedTokensAsync(response, payload, authProperties);
return; return;
} }
@ -485,12 +487,12 @@ namespace SocialSample
throw new NotImplementedException(currentAuthType); throw new NotImplementedException(currentAuthType);
} }
private async Task PrintRefreshedTokensAsync(HttpResponse response, JObject payload, AuthenticationProperties authProperties) private async Task PrintRefreshedTokensAsync(HttpResponse response, JsonDocument payload, AuthenticationProperties authProperties)
{ {
response.ContentType = "text/html"; response.ContentType = "text/html";
await response.WriteAsync("<html><body>"); await response.WriteAsync("<html><body>");
await response.WriteAsync("Refreshed.<br>"); await response.WriteAsync("Refreshed.<br>");
await response.WriteAsync(HtmlEncoder.Default.Encode(payload.ToString()).Replace(",", ",<br>") + "<br>"); await response.WriteAsync(HtmlEncoder.Default.Encode(payload.RootElement.ToString()).Replace(",", ",<br>") + "<br>");
await response.WriteAsync("<br>Tokens:<br>"); await response.WriteAsync("<br>Tokens:<br>");

View File

@ -4,6 +4,8 @@
<handlers> <handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" /> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers> </handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" /> <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false">
<environmentVariables />
</aspNetCore>
</system.webServer> </system.webServer>
</configuration> </configuration>

View File

@ -1,14 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Testing.xunit; using System.Text.Json;
using Xunit;
using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Authentication.OAuth.Claims;
using Newtonsoft.Json.Linq; using Xunit;
namespace Microsoft.AspNetCore.Authentication namespace Microsoft.AspNetCore.Authentication
{ {
@ -17,15 +14,12 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public void CanMapSingleValueUserDataToClaim() public void CanMapSingleValueUserDataToClaim()
{ {
var userData = new JObject var userData = JsonDocument.Parse("{ \"name\": \"test\" }");
{
["name"] = "test"
};
var identity = new ClaimsIdentity(); var identity = new ClaimsIdentity();
var action = new JsonKeyClaimAction("name", "name", "name"); var action = new JsonKeyClaimAction("name", "name", "name");
action.Run(userData, identity, "iss"); action.Run(userData.RootElement, identity, "iss");
Assert.Equal("name", identity.FindFirst("name").Type); Assert.Equal("name", identity.FindFirst("name").Type);
Assert.Equal("test", identity.FindFirst("name").Value); Assert.Equal("test", identity.FindFirst("name").Value);
@ -34,15 +28,12 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public void CanMapArrayValueUserDataToClaims() public void CanMapArrayValueUserDataToClaims()
{ {
var userData = new JObject var userData = JsonDocument.Parse("{ \"role\": [ \"role1\", null, \"role2\" ] }");
{
["role"] = new JArray { "role1", "role2" }
};
var identity = new ClaimsIdentity(); var identity = new ClaimsIdentity();
var action = new JsonKeyClaimAction("role", "role", "role"); var action = new JsonKeyClaimAction("role", "role", "role");
action.Run(userData, identity, "iss"); action.Run(userData.RootElement, identity, "iss");
var roleClaims = identity.FindAll("role").ToList(); var roleClaims = identity.FindAll("role").ToList();
Assert.Equal(2, roleClaims.Count); Assert.Equal(2, roleClaims.Count);
@ -55,15 +46,11 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public void MapAllSucceeds() public void MapAllSucceeds()
{ {
var userData = new JObject var userData = JsonDocument.Parse("{ \"name0\": \"value0\", \"name1\": \"value1\" }");
{
["name0"] = "value0",
["name1"] = "value1",
};
var identity = new ClaimsIdentity(); var identity = new ClaimsIdentity();
var action = new MapAllClaimsAction(); var action = new MapAllClaimsAction();
action.Run(userData, identity, "iss"); action.Run(userData.RootElement, identity, "iss");
Assert.Equal("name0", identity.FindFirst("name0").Type); Assert.Equal("name0", identity.FindFirst("name0").Type);
Assert.Equal("value0", identity.FindFirst("name0").Value); Assert.Equal("value0", identity.FindFirst("name0").Value);
@ -74,17 +61,13 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public void MapAllAllowesDulicateKeysWithUniqueValues() public void MapAllAllowesDulicateKeysWithUniqueValues()
{ {
var userData = new JObject var userData = JsonDocument.Parse("{ \"name0\": \"value0\", \"name1\": \"value1\" }");
{
["name0"] = "value0",
["name1"] = "value1",
};
var identity = new ClaimsIdentity(); var identity = new ClaimsIdentity();
identity.AddClaim(new Claim("name0", "value2")); identity.AddClaim(new Claim("name0", "value2"));
identity.AddClaim(new Claim("name1", "value3")); identity.AddClaim(new Claim("name1", "value3"));
var action = new MapAllClaimsAction(); var action = new MapAllClaimsAction();
action.Run(userData, identity, "iss"); action.Run(userData.RootElement, identity, "iss");
Assert.Equal(2, identity.FindAll("name0").Count()); Assert.Equal(2, identity.FindAll("name0").Count());
Assert.Equal(2, identity.FindAll("name1").Count()); Assert.Equal(2, identity.FindAll("name1").Count());
@ -93,17 +76,13 @@ namespace Microsoft.AspNetCore.Authentication
[Fact] [Fact]
public void MapAllSkipsDuplicateValues() public void MapAllSkipsDuplicateValues()
{ {
var userData = new JObject var userData = JsonDocument.Parse("{ \"name0\": \"value0\", \"name1\": \"value1\" }");
{
["name0"] = "value0",
["name1"] = "value1",
};
var identity = new ClaimsIdentity(); var identity = new ClaimsIdentity();
identity.AddClaim(new Claim("name0", "value0")); identity.AddClaim(new Claim("name0", "value0"));
identity.AddClaim(new Claim("name1", "value1")); identity.AddClaim(new Claim("name1", "value1"));
var action = new MapAllClaimsAction(); var action = new MapAllClaimsAction();
action.Run(userData, identity, "iss"); action.Run(userData.RootElement, identity, "iss");
Assert.Single(identity.FindAll("name0")); Assert.Single(identity.FindAll("name0"));
Assert.Single(identity.FindAll("name1")); Assert.Single(identity.FindAll("name1"));

View File

@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using System; using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -325,10 +324,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
if (req.RequestUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped) == FacebookDefaults.TokenEndpoint) if (req.RequestUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped) == FacebookDefaults.TokenEndpoint)
{ {
var res = new HttpResponseMessage(HttpStatusCode.OK); var res = new HttpResponseMessage(HttpStatusCode.OK);
var graphResponse = JsonConvert.SerializeObject(new var graphResponse = "{ \"access_token\": \"TestAuthToken\" }";
{
access_token = "TestAuthToken"
});
res.Content = new StringContent(graphResponse, Encoding.UTF8); res.Content = new StringContent(graphResponse, Encoding.UTF8);
return res; return res;
} }
@ -337,11 +333,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
{ {
finalUserInfoEndpoint = req.RequestUri.ToString(); finalUserInfoEndpoint = req.RequestUri.ToString();
var res = new HttpResponseMessage(HttpStatusCode.OK); var res = new HttpResponseMessage(HttpStatusCode.OK);
var graphResponse = JsonConvert.SerializeObject(new var graphResponse = "{ \"id\": \"TestProfileId\", \"name\": \"TestName\" }";
{
id = "TestProfileId",
name = "TestName"
});
res.Content = new StringContent(graphResponse, Encoding.UTF8); res.Content = new StringContent(graphResponse, Encoding.UTF8);
return res; return res;
} }

View File

@ -9,7 +9,6 @@ using Microsoft.AspNetCore.TestHost;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -704,7 +703,6 @@ namespace Microsoft.AspNetCore.Authentication.Google
{ {
OnCreatingTicket = context => OnCreatingTicket = context =>
{ {
Assert.NotNull(context.User);
Assert.Equal("Test Access Token", context.AccessToken); Assert.Equal("Test Access Token", context.AccessToken);
Assert.Equal("Test Refresh Token", context.RefreshToken); Assert.Equal("Test Refresh Token", context.RefreshToken);
Assert.Equal(TimeSpan.FromSeconds(3600), context.ExpiresIn); Assert.Equal(TimeSpan.FromSeconds(3600), context.ExpiresIn);
@ -972,7 +970,7 @@ namespace Microsoft.AspNetCore.Authentication.Google
private static HttpResponseMessage ReturnJsonResponse(object content, HttpStatusCode code = HttpStatusCode.OK) private static HttpResponseMessage ReturnJsonResponse(object content, HttpStatusCode code = HttpStatusCode.OK)
{ {
var res = new HttpResponseMessage(code); var res = new HttpResponseMessage(code);
var text = JsonConvert.SerializeObject(content); var text = Newtonsoft.Json.JsonConvert.SerializeObject(content);
res.Content = new StringContent(text, Encoding.UTF8, "application/json"); res.Content = new StringContent(text, Encoding.UTF8, "application/json");
return res; return res;
} }

View File

@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using System; using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -302,7 +301,7 @@ namespace Microsoft.AspNetCore.Authentication.Tests.MicrosoftAccount
private static HttpResponseMessage ReturnJsonResponse(object content) private static HttpResponseMessage ReturnJsonResponse(object content)
{ {
var res = new HttpResponseMessage(HttpStatusCode.OK); var res = new HttpResponseMessage(HttpStatusCode.OK);
var text = JsonConvert.SerializeObject(content); var text = Newtonsoft.Json.JsonConvert.SerializeObject(content);
res.Content = new StringContent(text, Encoding.UTF8, "application/json"); res.Content = new StringContent(text, Encoding.UTF8, "application/json");
return res; return res;
} }