Add docs for Negotiate, OAuth, OpenIdConnect (#26610)

* Add docs for Negotiate, OAuth, OpenIdConnect

Contributes to #26397
This commit is contained in:
Pranav K 2020-10-06 08:41:47 -07:00 committed by GitHub
parent fda7d1d660
commit 8b741bd9c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 416 additions and 61 deletions

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>ASP.NET Core middleware that enables an application to support the Microsoft Account authentication workflow.</Description>

View File

@ -13,9 +13,7 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
/// <summary>
/// Creates a new <see cref="AuthenticatedContext"/>.
/// </summary>
/// <param name="context"></param>
/// <param name="scheme"></param>
/// <param name="options"></param>
/// <inheritdoc />
public AuthenticatedContext(
HttpContext context,
AuthenticationScheme scheme,

View File

@ -14,9 +14,7 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
/// <summary>
/// Creates a <see cref="AuthenticationFailedContext"/>.
/// </summary>
/// <param name="context"></param>
/// <param name="scheme"></param>
/// <param name="options"></param>
/// <inheritdoc />
public AuthenticationFailedContext(
HttpContext context,
AuthenticationScheme scheme,

View File

@ -14,10 +14,7 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
/// <summary>
/// Creates a new <see cref="ChallengeContext"/>.
/// </summary>
/// <param name="context"></param>
/// <param name="scheme"></param>
/// <param name="options"></param>
/// <param name="properties"></param>
/// <inheritdoc />
public ChallengeContext(
HttpContext context,
AuthenticationScheme scheme,
@ -26,7 +23,8 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
: base(context, scheme, options, properties) { }
/// <summary>
/// If true, will skip any default logic for this challenge.
/// Gets a value that determines if this challenge was handled.
/// If <see langword="true"/>, will skip any default logic for this challenge.
/// </summary>
public bool Handled { get; private set; }

View File

@ -13,10 +13,7 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
/// <summary>
/// Creates a new <see cref="LdapContext"/>.
/// </summary>
/// <param name="context"></param>
/// <param name="scheme"></param>
/// <param name="options"></param>
/// <param name="settings"></param>
/// <inheritdoc />
public LdapContext(
HttpContext context,
AuthenticationScheme scheme,

View File

@ -76,6 +76,9 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
internal MemoryCache ClaimsCache { get; set; }
/// <summary>
/// Validates the <see cref="LdapSettings"/>.
/// </summary>
public void Validate()
{
if (EnableLdapClaimResolution)

View File

@ -3,6 +3,7 @@
<PropertyGroup>
<Description>ASP.NET Core authentication handler used to authenticate requests using Negotiate, Kerberos, or NTLM.</Description>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;authentication;security</PackageTags>
</PropertyGroup>

View File

@ -17,7 +17,11 @@ namespace Microsoft.Extensions.DependencyInjection
public static class NegotiateExtensions
{
/// <summary>
/// Adds Negotiate authentication.
/// Configures the <see cref="AuthenticationBuilder"/> to use Negotiate (also known as Windows, Kerberos, or NTLM) authentication
/// using the default scheme from <see cref="NegotiateDefaults.AuthenticationScheme"/>.
/// <para>
/// This authentication handler supports Kerberos on Windows and Linux servers.
/// </para>
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <returns>The original builder.</returns>
@ -25,7 +29,11 @@ namespace Microsoft.Extensions.DependencyInjection
=> builder.AddNegotiate(NegotiateDefaults.AuthenticationScheme, _ => { });
/// <summary>
/// Adds and configures Negotiate authentication.
/// Configures the <see cref="AuthenticationBuilder"/> to use Negotiate (also known as Windows, Kerberos, or NTLM) authentication
/// using the default scheme. The default scheme is specified by <see cref="NegotiateDefaults.AuthenticationScheme"/>.
/// <para>
/// This authentication handler supports Kerberos on Windows and Linux servers.
/// </para>
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="configureOptions">Allows for configuring the authentication handler.</param>
@ -34,7 +42,11 @@ namespace Microsoft.Extensions.DependencyInjection
=> builder.AddNegotiate(NegotiateDefaults.AuthenticationScheme, configureOptions);
/// <summary>
/// Adds and configures Negotiate authentication.
/// Configures the <see cref="AuthenticationBuilder"/> to use Negotiate (also known as Windows, Kerberos, or NTLM) authentication
/// using the specified authentication scheme.
/// <para>
/// This authentication handler supports Kerberos on Windows and Linux servers.
/// </para>
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="authenticationScheme">The scheme name used to identify the authentication handler internally.</param>
@ -44,7 +56,11 @@ namespace Microsoft.Extensions.DependencyInjection
=> builder.AddNegotiate(authenticationScheme, displayName: null, configureOptions: configureOptions);
/// <summary>
/// Adds and configures Negotiate authentication.
/// Configures the <see cref="AuthenticationBuilder"/> to use Negotiate (also known as Windows, Kerberos, or NTLM) authentication
/// using the specified authentication scheme.
/// <para>
/// This authentication handler supports Kerberos on Windows and Linux servers.
/// </para>
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="authenticationScheme">The scheme name used to identify the authentication handler internally.</param>

View File

@ -34,10 +34,7 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
/// <summary>
/// Creates a new <see cref="NegotiateHandler"/>
/// </summary>
/// <param name="options"></param>
/// <param name="logger"></param>
/// <param name="encoder"></param>
/// <param name="clock"></param>
/// <inheritdoc />
public NegotiateHandler(IOptionsMonitor<NegotiateOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{ }
@ -63,7 +60,7 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
/// <summary>
/// Intercepts incomplete Negotiate authentication handshakes and continues or completes them.
/// </summary>
/// <returns>True if a response was generated, false otherwise.</returns>
/// <returns><see langword="true" /> if a response was generated, otherwise <see langword="false"/>.</returns>
public async Task<bool> HandleRequestAsync()
{
AuthPersistence persistence = null;

View File

@ -24,15 +24,15 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
/// <summary>
/// Indicates if Kerberos credentials should be persisted and re-used for subsquent anonymous requests.
/// This option must not be used if connections may be shared by requests from different users.
/// The default is false.
/// </summary>
/// <value>Defaults to <see langword="false"/>.</value>
public bool PersistKerberosCredentials { get; set; } = false;
/// <summary>
/// Indicates if NTLM credentials should be persisted and re-used for subsquent anonymous requests.
/// This option must not be used if connections may be shared by requests from different users.
/// The default is true.
/// </summary>
/// <value>Defaults to <see langword="true"/>.</value>
public bool PersistNtlmCredentials { get; set; } = true;
/// <summary>

View File

@ -23,11 +23,13 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
}
/// <summary>
/// The value to use for Claim.Type when creating a Claim.
/// Gets the value to use for <see cref="Claim.Value"/>when creating a Claim.
/// </summary>
public string ClaimType { get; }
// The value to use for Claim.ValueType when creating a Claim.
/// <summary>
/// Gets the value to use for <see cref="Claim.ValueType"/> when creating a Claim.
/// </summary>
public string ValueType { get; }
/// <summary>

View File

@ -1,4 +1,4 @@
// 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.
using System;
@ -39,6 +39,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
Actions.Add(action);
}
/// <inheritdoc />
public IEnumerator<ClaimAction> GetEnumerator()
{
return Actions.GetEnumerator();
@ -49,4 +50,4 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
return Actions.GetEnumerator();
}
}
}
}

View File

@ -8,13 +8,16 @@ using Microsoft.AspNetCore.Authentication.OAuth.Claims;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Extension methods for <see cref="ClaimActionCollection"/>.
/// </summary>
public static class ClaimActionCollectionMapExtensions
{
/// <summary>
/// Select a top level value from the json user data with the given key name and add it as a Claim.
/// This no-ops if the key is not found or the value is empty.
/// </summary>
/// <param name="collection"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</param>
/// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param>
/// <param name="jsonKey">The top level key to look for in the json user data.</param>
public static void MapJsonKey(this ClaimActionCollection collection, string claimType, string jsonKey)
@ -26,7 +29,7 @@ namespace Microsoft.AspNetCore.Authentication
/// Select a top level value from the json user data with the given key name and add it as a Claim.
/// This no-ops if the key is not found or the value is empty.
/// </summary>
/// <param name="collection"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</param>
/// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param>
/// <param name="jsonKey">The top level key to look for in the json user data.</param>
/// <param name="valueType">The value to use for Claim.ValueType when creating a Claim.</param>
@ -39,7 +42,7 @@ namespace Microsoft.AspNetCore.Authentication
/// Select a second level value from the json user data with the given top level key name and second level sub key name and add it as a Claim.
/// This no-ops if the keys are not found or the value is empty.
/// </summary>
/// <param name="collection"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</param>
/// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param>
/// <param name="jsonKey">The top level key to look for in the json user data.</param>
/// <param name="subKey">The second level key to look for in the json user data.</param>
@ -52,7 +55,7 @@ namespace Microsoft.AspNetCore.Authentication
/// Select a second level value from the json user data with the given top level key name and second level sub key name and add it as a Claim.
/// This no-ops if the keys are not found or the value is empty.
/// </summary>
/// <param name="collection"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</param>
/// <param name="claimType">The value to use for Claim.Type when creating a Claim.</param>
/// <param name="jsonKey">The top level key to look for in the json user data.</param>
/// <param name="subKey">The second level key to look for in the json user data.</param>
@ -66,7 +69,7 @@ namespace Microsoft.AspNetCore.Authentication
/// Run the given resolver to select a value from the json user data to add as a claim.
/// This no-ops if the returned value is empty.
/// </summary>
/// <param name="collection"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</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>
public static void MapCustomJson(this ClaimActionCollection collection, string claimType, Func<JsonElement, string> resolver)
@ -78,7 +81,7 @@ namespace Microsoft.AspNetCore.Authentication
/// Run the given resolver to select a value from the json user data to add as a claim.
/// This no-ops if the returned value is empty.
/// </summary>
/// <param name="collection"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</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="resolver">The Func that will be called to select value from the given json user data.</param>
@ -90,7 +93,7 @@ namespace Microsoft.AspNetCore.Authentication
/// <summary>
/// Clears any current ClaimsActions and maps all values from the json user data as claims, excluding duplicates.
/// </summary>
/// <param name="collection"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</param>
public static void MapAll(this ClaimActionCollection collection)
{
collection.Clear();
@ -100,8 +103,8 @@ namespace Microsoft.AspNetCore.Authentication
/// <summary>
/// Clears any current ClaimsActions and maps all values from the json user data as claims, excluding the specified types.
/// </summary>
/// <param name="collection"></param>
/// <param name="exclusions"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</param>
/// <param name="exclusions">The types to exclude.</param>
public static void MapAllExcept(this ClaimActionCollection collection, params string[] exclusions)
{
collection.MapAll();
@ -111,8 +114,8 @@ namespace Microsoft.AspNetCore.Authentication
/// <summary>
/// Delete all claims from the given ClaimsIdentity with the given ClaimType.
/// </summary>
/// <param name="collection"></param>
/// <param name="claimType"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</param>
/// <param name="claimType">The claim type to delete</param>
public static void DeleteClaim(this ClaimActionCollection collection, string claimType)
{
collection.Add(new DeleteClaimAction(claimType));
@ -121,8 +124,8 @@ namespace Microsoft.AspNetCore.Authentication
/// <summary>
/// Delete all claims from the ClaimsIdentity with the given claimTypes.
/// </summary>
/// <param name="collection"></param>
/// <param name="claimTypes"></param>
/// <param name="collection">The <see cref="ClaimActionCollection"/>.</param>
/// <param name="claimTypes">The claim types to delete.</param>
public static void DeleteClaims(this ClaimActionCollection collection, params string[] claimTypes)
{
if (claimTypes == null)

View File

@ -108,8 +108,16 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// </summary>
public ClaimsIdentity Identity => Principal?.Identity as ClaimsIdentity;
/// <summary>
/// Examines <see cref="User"/>, determine if the requisite data is present, and optionally add it
/// to <see cref="Identity"/>.
/// </summary>
public void RunClaimActions() => RunClaimActions(User);
/// <summary>
/// Examines the specified <paramref name="userData"/>, determine if the requisite data is present, and optionally add it
/// to <see cref="Identity"/>.
/// </summary>
public void RunClaimActions(JsonElement userData)
{
foreach (var action in Options.ClaimActions)

View File

@ -13,10 +13,14 @@ namespace Microsoft.AspNetCore.Authentication.OAuth.Claims
/// </summary>
public class MapAllClaimsAction : ClaimAction
{
/// <summary>
/// Initializes a new instance of <see cref="MapAllClaimsAction"/>.
/// </summary>
public MapAllClaimsAction() : base("All", ClaimValueTypes.String)
{
}
/// <inheritdoc />
public override void Run(JsonElement userData, ClaimsIdentity identity, string issuer)
{
foreach (var pair in userData.EnumerateObject())

View File

@ -4,7 +4,7 @@
<Description>ASP.NET Core middleware that enables an application to support any standard OAuth 2.0 authentication workflow.</Description>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<IsAspNetCoreApp>true</IsAspNetCoreApp>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;authentication;security</PackageTags>
<IsPackable>false</IsPackable>

View File

@ -1,7 +1,10 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Authentication.OAuth
{
/// <summary>
/// <see cref="AuthenticationProperties"/> for an OAuth challenge.
/// </summary>
public class OAuthChallengeProperties : AuthenticationProperties
{
/// <summary>
@ -9,13 +12,24 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// </summary>
public static readonly string ScopeKey = "scope";
/// <summary>
/// Initializes a new instance of <see cref="OAuthChallengeProperties"/>.
/// </summary>
public OAuthChallengeProperties()
{ }
/// <summary>
/// Initializes a new instance of <see cref="OAuthChallengeProperties" />.
/// </summary>
/// <inheritdoc />
public OAuthChallengeProperties(IDictionary<string, string> items)
: base(items)
{ }
/// <summary>
/// Initializes a new instance of <see cref="OAuthChallengeProperties" />.
/// </summary>
/// <inheritdoc />
public OAuthChallengeProperties(IDictionary<string, string> items, IDictionary<string, object> parameters)
: base(items, parameters)
{ }

View File

@ -1,10 +1,16 @@
// 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.
namespace Microsoft.AspNetCore.Authentication.OAuth
{
/// <summary>
/// Defaults for OAuth authentication.
/// </summary>
public static class OAuthDefaults
{
/// <summary>
/// The default display name for OAuth authentication.
/// </summary>
public static readonly string DisplayName = "OAuth";
}
}

View File

@ -10,19 +10,52 @@ using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Extension methods to add OAuth authentication.
/// </summary>
public static class OAuthExtensions
{
/// <summary>
/// Adds OAuth 2.0 based authentication to <see cref="AuthenticationBuilder"/> using the specified authentication scheme.
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="authenticationScheme">The authentication scheme.</param>
/// <param name="configureOptions">A delegate to configure <see cref="OAuthOptions"/>.</param>
/// <returns>A reference to <paramref name="builder"/> after the operation has completed.</returns>
public static AuthenticationBuilder AddOAuth(this AuthenticationBuilder builder, string authenticationScheme, Action<OAuthOptions> configureOptions)
=> builder.AddOAuth<OAuthOptions, OAuthHandler<OAuthOptions>>(authenticationScheme, configureOptions);
/// <summary>
/// Adds OAuth 2.0 based authentication to <see cref="AuthenticationBuilder"/> using the specified authentication scheme.
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="authenticationScheme">The authentication scheme.</param>
/// <param name="displayName">A display name for the authentication handler.</param>
/// <param name="configureOptions">A delegate to configure <see cref="OAuthOptions"/>.</param>
/// <returns>A reference to <paramref name="builder"/> after the operation has completed.</returns>
public static AuthenticationBuilder AddOAuth(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<OAuthOptions> configureOptions)
=> builder.AddOAuth<OAuthOptions, OAuthHandler<OAuthOptions>>(authenticationScheme, displayName, configureOptions);
/// <summary>
/// Adds OAuth 2.0 based authentication to <see cref="AuthenticationBuilder"/> using the specified authentication scheme.
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="authenticationScheme">The authentication scheme.</param>
/// <param name="configureOptions">A delegate to configure the handler specific options.</param>
/// <returns>A reference to <paramref name="builder"/> after the operation has completed.</returns>
public static AuthenticationBuilder AddOAuth<TOptions, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]THandler>(this AuthenticationBuilder builder, string authenticationScheme, Action<TOptions> configureOptions)
where TOptions : OAuthOptions, new()
where THandler : OAuthHandler<TOptions>
=> builder.AddOAuth<TOptions, THandler>(authenticationScheme, OAuthDefaults.DisplayName, configureOptions);
/// <summary>
/// Adds OAuth 2.0 based authentication to <see cref="AuthenticationBuilder"/> using the specified authentication scheme.
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="authenticationScheme">The authentication scheme.</param>
/// <param name="displayName">A display name for the authentication handler.</param>
/// <param name="configureOptions">A delegate to configure the handler specific options.</param>
/// <returns>A reference to <paramref name="builder"/> after the operation has completed.</returns>
public static AuthenticationBuilder AddOAuth<TOptions, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]THandler>(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<TOptions> configureOptions)
where TOptions : OAuthOptions, new()
where THandler : OAuthHandler<TOptions>

View File

@ -20,8 +20,15 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Authentication.OAuth
{
/// <summary>
/// An authentication handler that supports OAuth.
/// </summary>
/// <typeparam name="TOptions">The type of options.</typeparam>
public class OAuthHandler<TOptions> : RemoteAuthenticationHandler<TOptions> where TOptions : OAuthOptions, new()
{
/// <summary>
/// Gets the <see cref="HttpClient"/> instance used to communicate with the remote authentication provider.
/// </summary>
protected HttpClient Backchannel => Options.Backchannel;
/// <summary>
@ -34,6 +41,10 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
set { base.Events = value; }
}
/// <summary>
/// Initializes a new instance of <see cref="OAuthHandler{TOptions}"/>.
/// </summary>
/// <inheritdoc />
public OAuthHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{ }
@ -44,6 +55,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// <returns>A new instance of the events instance.</returns>
protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new OAuthEvents());
/// <inheritdoc />
protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
{
var query = Request.Query;
@ -173,6 +185,11 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
}
}
/// <summary>
/// Exchanges the authorization code for a authorization token from the remote provider.
/// </summary>
/// <param name="context">The <see cref="OAuthCodeExchangeContext"/>.</param>
/// <returns>The response <see cref="OAuthTokenResponse"/>.</returns>
protected virtual async Task<OAuthTokenResponse> ExchangeCodeAsync(OAuthCodeExchangeContext context)
{
var tokenRequestParameters = new Dictionary<string, string>()
@ -219,6 +236,13 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
return output.ToString();
}
/// <summary>
/// Creates an <see cref="AuthenticationTicket"/> from the specified <paramref name="tokens"/>.
/// </summary>
/// <param name="identity">The <see cref="ClaimsIdentity"/>.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="tokens">The <see cref="OAuthTokenResponse"/>.</param>
/// <returns>The <see cref="AuthenticationTicket"/>.</returns>
protected virtual async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
{
using (var user = JsonDocument.Parse("{}"))
@ -229,6 +253,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
}
}
/// <inheritdoc />
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
if (string.IsNullOrEmpty(properties.RedirectUri))
@ -258,6 +283,12 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
Logger.HandleChallenge(location, cookie);
}
/// <summary>
/// Constructs the OAuth challenge url.
/// </summary>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="redirectUri">The url to redirect to once the challenge is completed.</param>
/// <returns>The challenge url.</returns>
protected virtual string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
{
var scopeParameter = properties.GetParameter<ICollection<string>>(OAuthChallengeProperties.ScopeKey);

View File

@ -14,13 +14,16 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// </summary>
public class OAuthOptions : RemoteAuthenticationOptions
{
/// <summary>
/// Initializes a new instance of <see cref="OAuthOptions"/>.
/// </summary>
public OAuthOptions()
{
Events = new OAuthEvents();
}
/// <summary>
/// Check that the options are valid. Should throw an exception if things are not ok.
/// Check that the options are valid. Should throw an exception if things are not ok.
/// </summary>
public override void Validate()
{
@ -75,7 +78,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// <summary>
/// Gets or sets the URI the middleware will access to obtain the user information.
/// This value is not used in the default implementation, it is for use in custom implementations of
/// IOAuthAuthenticationEvents.Authenticated or OAuthAuthenticationHandler.CreateTicketAsync.
/// <see cref="OAuthEvents.OnCreatingTicket" />.
/// </summary>
public string UserInformationEndpoint { get; set; }

View File

@ -18,11 +18,16 @@ namespace Microsoft.Extensions.DependencyInjection
{
private readonly IDataProtectionProvider _dp;
/// <summary>
/// Initializes the <see cref="OAuthPostConfigureOptions{TOptions, THandler}"/>.
/// </summary>
/// <param name="dataProtection">The <see cref="IDataProtectionProvider"/>.</param>
public OAuthPostConfigureOptions(IDataProtectionProvider dataProtection)
{
_dp = dataProtection;
}
/// <inheritdoc />
public void PostConfigure(string name, TOptions options)
{
options.DataProtectionProvider = options.DataProtectionProvider ?? _dp;

View File

@ -6,8 +6,15 @@ using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication.OAuth
{
/// <summary>
/// Response from an provider for an OAuth token request.
/// </summary>
public class OAuthTokenResponse : IDisposable
{
/// <summary>
/// Initializes a new isntance <see cref="OAuthTokenResponse"/>.
/// </summary>
/// <param name="response">The received JSON payload.</param>
private OAuthTokenResponse(JsonDocument response)
{
Response = response;
@ -23,26 +30,63 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
Error = error;
}
/// <summary>
/// Creates a successful <see cref="OAuthTokenResponse"/>.
/// </summary>
/// <param name="response">The received JSON payload.</param>
/// <returns>A <see cref="OAuthTokenResponse"/> instance.</returns>
public static OAuthTokenResponse Success(JsonDocument response)
{
return new OAuthTokenResponse(response);
}
/// <summary>
/// Creates a failed <see cref="OAuthTokenResponse"/>.
/// </summary>
/// <param name="error">The error associated with the failure.</param>
/// <returns>A <see cref="OAuthTokenResponse"/> instance.</returns>
public static OAuthTokenResponse Failed(Exception error)
{
return new OAuthTokenResponse(error);
}
/// <inheritdoc />
public void Dispose()
{
Response?.Dispose();
}
/// <summary>
/// Gets or sets the received JSON payload.
/// </summary>
public JsonDocument Response { get; set; }
/// <summary>
/// Gets or sets the access token issued by the OAuth provider.
/// </summary>
public string AccessToken { get; set; }
/// <summary>
/// Gets or sets the token type.
/// </summary>
/// <remarks>
/// Typically the string “bearer”.
/// </remarks>
public string TokenType { get; set; }
/// <summary>
/// Gets or sets a refresh token that applications can use to obtain another access token if tokens can expire.
/// </summary>
public string RefreshToken { get; set; }
/// <summary>
/// Gets or sets the validatity lifetime of the token in seconds.
/// </summary>
public string ExpiresIn { get; set; }
/// <summary>
/// The exception in the event the response was a failure.
/// </summary>
public Exception Error { get; set; }
}
}

View File

@ -1,4 +1,4 @@
// 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.
using System.Security.Claims;
@ -7,6 +7,9 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect.Claims;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Extension methods for <see cref="ClaimActionCollection"/>.
/// </summary>
public static class ClaimActionCollectionUniqueExtensions
{
/// <summary>

View File

@ -7,14 +7,27 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
/// <summary>
/// A conext for <see cref="OpenIdConnectEvents.AuthenticationFailed"/>.
/// </summary>
public class AuthenticationFailedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{
/// <summary>
/// Initializes a new instance of <see cref="AuthenticationFailedContext"/>.
/// </summary>
/// <inheritdoc />
public AuthenticationFailedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options)
: base(context, scheme, options, new AuthenticationProperties())
{ }
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary>
/// Gets or sets the exception associated with the failure.
/// </summary>
public Exception Exception { get; set; }
}
}
}

View File

@ -23,6 +23,9 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
AuthenticationProperties properties)
: base(context, scheme, options, properties) { }
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary>
@ -90,4 +93,4 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
TokenEndpointResponse = tokenEndpointResponse;
}
}
}
}

View File

@ -6,8 +6,15 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
/// <summary>
/// A context for <see cref="OpenIdConnectEvents.OnMessageReceived"/>.
/// </summary>
public class MessageReceivedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{
/// <summary>
/// Initializes a new instance of <see cref="MessageReceivedContext"/>.
/// </summary>
/// <inheritdoc />
public MessageReceivedContext(
HttpContext context,
AuthenticationScheme scheme,
@ -15,6 +22,9 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
AuthenticationProperties properties)
: base(context, scheme, options, properties) { }
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary>
@ -22,4 +32,4 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// </summary>
public string Token { get; set; }
}
}
}

View File

@ -63,24 +63,56 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// </summary>
public Func<UserInformationReceivedContext, Task> OnUserInformationReceived { get; set; } = context => Task.CompletedTask;
/// <summary>
/// Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed.
/// </summary>
public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context);
/// <summary>
/// Invoked if an authorization code is present in the protocol message.
/// </summary>
public virtual Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context) => OnAuthorizationCodeReceived(context);
/// <summary>
/// Invoked when a protocol message is first received.
/// </summary>
public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context);
/// <summary>
/// Invoked before redirecting to the identity provider to authenticate. This can be used to set ProtocolMessage.State
/// that will be persisted through the authentication process. The ProtocolMessage can also be used to add or customize
/// parameters sent to the identity provider.
/// </summary>
public virtual Task RedirectToIdentityProvider(RedirectContext context) => OnRedirectToIdentityProvider(context);
/// <summary>
/// Invoked before redirecting to the identity provider to sign out.
/// </summary>
public virtual Task RedirectToIdentityProviderForSignOut(RedirectContext context) => OnRedirectToIdentityProviderForSignOut(context);
/// <summary>
/// Invoked before redirecting to the <see cref="OpenIdConnectOptions.SignedOutRedirectUri"/> at the end of a remote sign-out flow.
/// </summary>
public virtual Task SignedOutCallbackRedirect(RemoteSignOutContext context) => OnSignedOutCallbackRedirect(context);
/// <summary>
/// Invoked when a request is received on the RemoteSignOutPath.
/// </summary>
public virtual Task RemoteSignOut(RemoteSignOutContext context) => OnRemoteSignOut(context);
/// <summary>
/// Invoked after an authorization code is redeemed for tokens at the token endpoint.
/// </summary>
public virtual Task TokenResponseReceived(TokenResponseReceivedContext context) => OnTokenResponseReceived(context);
/// <summary>
/// Invoked when an IdToken has been validated and produced an AuthenticationTicket.
/// </summary>
public virtual Task TokenValidated(TokenValidatedContext context) => OnTokenValidated(context);
/// <summary>
/// Invoked when user information is retrieved from the UserInfoEndpoint.
/// </summary>
public virtual Task UserInformationReceived(UserInformationReceivedContext context) => OnUserInformationReceived(context);
}
}
}

View File

@ -8,10 +8,15 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
/// <summary>
/// When a user configures the <see cref="OpenIdConnectHandler"/> to be notified prior to redirecting to an IdentityProvider
/// an instance of <see cref="RedirectContext"/> is passed to the 'RedirectToAuthenticationEndpoint' or 'RedirectToEndSessionEndpoint' events.
/// an instance of <see cref="RedirectContext"/> is passed to the <see cref="OpenIdConnectEvents.RedirectToIdentityProvider(RedirectContext)"/>
/// and <see cref="OpenIdConnectEvents.RedirectToIdentityProviderForSignOut(RedirectContext)"/>.
/// </summary>
public class RedirectContext : PropertiesContext<OpenIdConnectOptions>
{
/// <summary>
/// Initializes a new instance of <see cref="RedirectContext"/>.
/// </summary>
/// <inheritdoc />
public RedirectContext(
HttpContext context,
AuthenticationScheme scheme,
@ -19,6 +24,9 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
AuthenticationProperties properties)
: base(context, scheme, options, properties) { }
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary>
@ -31,4 +39,4 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// </summary>
public void HandleResponse() => Handled = true;
}
}
}

View File

@ -6,12 +6,22 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
/// <summary>
/// A context for <see cref="OpenIdConnectEvents.RemoteSignOut(RemoteSignOutContext)"/> event.
/// </summary>
public class RemoteSignOutContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{
/// <summary>
/// Initializes a new instance of <see cref="RemoteSignOutContext"/>.
/// </summary>
/// <inheritdoc />
public RemoteSignOutContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options, OpenIdConnectMessage message)
: base(context, scheme, options, new AuthenticationProperties())
=> ProtocolMessage = message;
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }
}
}
}

View File

@ -19,6 +19,9 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
: base(context, scheme, options, properties)
=> Principal = user;
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary>

View File

@ -8,21 +8,37 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
/// <summary>
/// A context for <see cref="OpenIdConnectEvents.TokenValidated(TokenValidatedContext)"/>.
/// </summary>
public class TokenValidatedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{
/// <summary>
/// Creates a <see cref="TokenValidatedContext"/>
/// </summary>
/// <inheritdoc />
public TokenValidatedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options, ClaimsPrincipal principal, AuthenticationProperties properties)
: base(context, scheme, options, properties)
=> Principal = principal;
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary>
/// Gets or sets the validated security token.
/// </summary>
public JwtSecurityToken SecurityToken { get; set; }
/// <summary>
/// Gets or sets the token endpoint response.
/// </summary>
public OpenIdConnectMessage TokenEndpointResponse { get; set; }
/// <summary>
/// Gets or sets the protocol nonce.
/// </summary>
public string Nonce { get; set; }
}
}
}

View File

@ -8,14 +8,27 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
/// <summary>
/// A context for <see cref="OpenIdConnectEvents.UserInformationReceived(UserInformationReceivedContext)"/>.
/// </summary>
public class UserInformationReceivedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{
/// <summary>
/// Initializes a new instance of <see cref="UserInformationReceivedContext"/>.
/// </summary>
/// <inheritdoc />
public UserInformationReceivedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options, ClaimsPrincipal principal, AuthenticationProperties properties)
: base(context, scheme, options, properties)
=> Principal = principal;
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary>
/// Gets or sets the user information payload.
/// </summary>
public JsonDocument User { get; set; }
}
}

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>ASP.NET Core middleware that enables an application to support the OpenID Connect authentication workflow.</Description>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;authentication;security</PackageTags>
</PropertyGroup>

View File

@ -1,10 +1,13 @@
using System;
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
/// <summary>
/// <see cref="AuthenticationProperties"/> for an OpenId Connect challenge.
/// </summary>
public class OpenIdConnectChallengeProperties : OAuthChallengeProperties
{
/// <summary>
@ -17,13 +20,24 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// </summary>
public static readonly string PromptKey = OpenIdConnectParameterNames.Prompt;
/// <summary>
/// Initializes a new instance of <see cref="OpenIdConnectChallengeProperties"/>.
/// </summary>
public OpenIdConnectChallengeProperties()
{ }
/// <summary>
/// Initializes a new instance of <see cref="OpenIdConnectChallengeProperties"/>.
/// </summary>
/// <inheritdoc />
public OpenIdConnectChallengeProperties(IDictionary<string, string> items)
: base(items)
{ }
/// <summary>
/// Initializes a new instance of <see cref="OpenIdConnectChallengeProperties"/>.
/// </summary>
/// <inheritdoc />
public OpenIdConnectChallengeProperties(IDictionary<string, string> items, IDictionary<string, object> parameters)
: base(items, parameters)
{ }

View File

@ -9,17 +9,64 @@ using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Extension methods to configure OpenIdConnect authentication.
/// </summary>
public static class OpenIdConnectExtensions
{
/// <summary>
/// Adds OpenId Connect authentication to <see cref="AuthenticationBuilder"/> using the default scheme.
/// The default scheme is specified by <see cref="OpenIdConnectDefaults.AuthenticationScheme"/>.
/// <para>
/// OpenID Connect is an identity layer on top of the OAuth 2.0 protocol. It allows clients
/// to request and receive information about authenticated sessions and end-users.
/// </para>
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <returns>A reference to <paramref name="builder"/> after the operation has completed.</returns>
public static AuthenticationBuilder AddOpenIdConnect(this AuthenticationBuilder builder)
=> builder.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, _ => { });
/// <summary>
/// Adds OpenId Connect authentication to <see cref="AuthenticationBuilder"/> using the default scheme.
/// The default scheme is specified by <see cref="OpenIdConnectDefaults.AuthenticationScheme"/>.
/// <para>
/// OpenID Connect is an identity layer on top of the OAuth 2.0 protocol. It allows clients
/// to request and receive information about authenticated sessions and end-users.
/// </para>
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="configureOptions">A delegate to configure <see cref="OpenIdConnectOptions"/>.</param>
/// <returns>A reference to <paramref name="builder"/> after the operation has completed.</returns>
public static AuthenticationBuilder AddOpenIdConnect(this AuthenticationBuilder builder, Action<OpenIdConnectOptions> configureOptions)
=> builder.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, configureOptions);
/// <summary>
/// Adds OpenId Connect authentication to <see cref="AuthenticationBuilder"/> using the specified scheme.
/// <para>
/// OpenID Connect is an identity layer on top of the OAuth 2.0 protocol. It allows clients
/// to request and receive information about authenticated sessions and end-users.
/// </para>
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="authenticationScheme">The authentication scheme.</param>
/// <param name="configureOptions">A delegate to configure <see cref="OpenIdConnectOptions"/>.</param>
/// <returns>A reference to <paramref name="builder"/> after the operation has completed.</returns>
public static AuthenticationBuilder AddOpenIdConnect(this AuthenticationBuilder builder, string authenticationScheme, Action<OpenIdConnectOptions> configureOptions)
=> builder.AddOpenIdConnect(authenticationScheme, OpenIdConnectDefaults.DisplayName, configureOptions);
/// <summary>
/// Adds OpenId Connect authentication to <see cref="AuthenticationBuilder"/> using the specified scheme.
/// <para>
/// OpenID Connect is an identity layer on top of the OAuth 2.0 protocol. It allows clients
/// to request and receive information about authenticated sessions and end-users.
/// </para>
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="authenticationScheme">The authentication scheme.</param>
/// <param name="displayName">A display name for the authentication handler.</param>
/// <param name="configureOptions">A delegate to configure <see cref="OpenIdConnectOptions"/>.</param>
/// <returns>A reference to <paramref name="builder"/> after the operation has completed.</returns>
public static AuthenticationBuilder AddOpenIdConnect(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<OpenIdConnectOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<OpenIdConnectOptions>, OpenIdConnectPostConfigureOptions>());

View File

@ -36,10 +36,24 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
private OpenIdConnectConfiguration _configuration;
/// <summary>
/// Gets the <see cref="HttpClient"/> used to communicate with the remote identity provider.
/// </summary>
protected HttpClient Backchannel => Options.Backchannel;
/// <summary>
/// Gets the <see cref="System.Text.Encodings.Web.HtmlEncoder"/>.
/// </summary>
protected HtmlEncoder HtmlEncoder { get; }
/// <summary>
/// Initializes a new instance of <see cref="OpenIdConnectHandler"/>.
/// </summary>
/// <param name="options">A monitor to observe changes to <see cref="OpenIdConnectOptions"/>.</param>
/// <param name="logger">The <see cref="ILoggerFactory"/>.</param>
/// <param name="htmlEncoder">The <see cref="System.Text.Encodings.Web.HtmlEncoder"/>.</param>
/// <param name="encoder">The <see cref="UrlEncoder"/>.</param>
/// <param name="clock">The <see cref="ISystemClock"/>.</param>
public OpenIdConnectHandler(IOptionsMonitor<OpenIdConnectOptions> options, ILoggerFactory logger, HtmlEncoder htmlEncoder, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
@ -56,8 +70,10 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
set { base.Events = value; }
}
/// <inheritdoc />
protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new OpenIdConnectEvents());
/// <inheritdoc />
public override Task<bool> HandleRequestAsync()
{
if (Options.RemoteSignOutPath.HasValue && Options.RemoteSignOutPath == Request.Path)
@ -72,6 +88,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
return base.HandleRequestAsync();
}
/// <inheritdoc />
protected virtual async Task<bool> HandleRemoteSignOutAsync()
{
OpenIdConnectMessage message = null;

View File

@ -18,6 +18,10 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
private readonly IDataProtectionProvider _dp;
/// <summary>
/// Initializes a new instance of <see cref="OpenIdConnectPostConfigureOptions"/>.
/// </summary>
/// <param name="dataProtection">The <see cref="IDataProtectionProvider"/>.</param>
public OpenIdConnectPostConfigureOptions(IDataProtectionProvider dataProtection)
{
_dp = dataProtection;