Skip to content

Commit

Permalink
Add response_mode=query support for OpenID Connect (#297)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-w authored and Tratcher committed Aug 12, 2019
1 parent 40de801 commit 569f1c8
Show file tree
Hide file tree
Showing 11 changed files with 581 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
</Compile>
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="Notifications\AuthorizationCodeReceivedNotification.cs" />
<Compile Include="Notifications\TokenResponseReceivedNotification.cs" />
<Compile Include="OpenIdConnectAuthenticationDefaults.cs" />
<Compile Include="OpenIdConnectAuthenticationExtensions.cs" />
<Compile Include="OpenidConnectAuthenticationHandler.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ public AuthorizationCodeReceivedNotification(IOwinContext context, OpenIdConnect
/// </summary>
public JwtSecurityToken JwtSecurityToken { get; set; }

/// <summary>
/// The request that will be sent to the token endpoint and is available for customization.
/// </summary>
public OpenIdConnectMessage TokenEndpointRequest { get; set; }

/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
Expand All @@ -47,5 +52,57 @@ public AuthorizationCodeReceivedNotification(IOwinContext context, OpenIdConnect
/// <remarks>This is the redirect_uri that was sent in the id_token + code OpenIdConnectRequest.</remarks>
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "user controlled, not necessarily a URI")]
public string RedirectUri { get; set; }

/// <summary>
/// If the developer chooses to redeem the code themselves then they can provide the resulting tokens here. This is the
/// same as calling HandleCodeRedemption. If set then the handler will not attempt to redeem the code. An IdToken
/// is required if one had not been previously received in the authorization response.
/// </summary>
public OpenIdConnectMessage TokenEndpointResponse { get; set; }

/// <summary>
/// Indicates if the developer choose to handle (or skip) the code redemption. If true then the handler will not attempt
/// to redeem the code. See HandleCodeRedemption and TokenEndpointResponse.
/// </summary>
public bool HandledCodeRedemption
{
get
{
return TokenEndpointResponse != null;
}
}

/// <summary>
/// Tells the handler to skip the code redemption process. The developer may have redeemed the code themselves, or
/// decided that the redemption was not required. If tokens were retrieved that are needed for further processing then
/// call one of the overloads that allows providing tokens. An IdToken is required if one had not been previously received
/// in the authorization response. Calling this is the same as setting TokenEndpointResponse.
/// </summary>
public void HandleCodeRedemption()
{
TokenEndpointResponse = new OpenIdConnectMessage();
}

/// <summary>
/// Tells the handler to skip the code redemption process. The developer may have redeemed the code themselves, or
/// decided that the redemption was not required. If tokens were retrieved that are needed for further processing then
/// call one of the overloads that allows providing tokens. An IdToken is required if one had not been previously received
/// in the authorization response. Calling this is the same as setting TokenEndpointResponse.
/// </summary>
public void HandleCodeRedemption(string accessToken, string idToken)
{
TokenEndpointResponse = new OpenIdConnectMessage() { AccessToken = accessToken, IdToken = idToken };
}

/// <summary>
/// Tells the handler to skip the code redemption process. The developer may have redeemed the code themselves, or
/// decided that the redemption was not required. If tokens were retrieved that are needed for further processing then
/// call one of the overloads that allows providing tokens. An IdToken is required if one had not been previously received
/// in the authorization response. Calling this is the same as setting TokenEndpointResponse.
/// </summary>
public void HandleCodeRedemption(OpenIdConnectMessage tokenEndpointResponse)
{
TokenEndpointResponse = tokenEndpointResponse;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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 Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.Owin.Security.OpenIdConnect;

namespace Microsoft.Owin.Security.Notifications
{
/// <summary>
/// This Notification can be used to be informed when an 'AuthorizationCode' is redeemed for tokens at the token endpoint.
/// </summary>
public class TokenResponseReceivedNotification : BaseNotification<OpenIdConnectAuthenticationOptions>
{
/// <summary>
/// Creates a <see cref="TokenResponseReceivedNotification"/>
/// </summary>
public TokenResponseReceivedNotification(IOwinContext context, OpenIdConnectAuthenticationOptions options)
: base(context, options)
{
}

/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/> that contains the code redeemed for tokens at the token endpoint.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }

/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/> that contains the tokens received after redeeming the code at the token endpoint.
/// </summary>
public OpenIdConnectMessage TokenEndpointResponse { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ public OpenIdConnectAuthenticationMiddleware(OwinMiddleware next, IAppBuilder ap
Options.TokenValidationParameters.ValidAudience = Options.ClientId;
}

if (Options.Backchannel == null)
{
Options.Backchannel = new HttpClient(ResolveHttpMessageHandler(Options));
Options.Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core OpenIdConnect middleware");
Options.Backchannel.Timeout = Options.BackchannelTimeout;
Options.Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
}

if (Options.ConfigurationManager == null)
{
if (Options.Configuration != null)
Expand All @@ -91,13 +99,8 @@ public OpenIdConnectAuthenticationMiddleware(OwinMiddleware next, IAppBuilder ap
throw new InvalidOperationException("The MetadataAddress or Authority must use HTTPS unless disabled for development by setting RequireHttpsMetadata=false.");
}

var backchannel = new HttpClient(ResolveHttpMessageHandler(Options));
backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core OpenIdConnect middleware");
backchannel.Timeout = Options.BackchannelTimeout;
backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB

Options.ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(Options.MetadataAddress, new OpenIdConnectConfigurationRetriever(),
new HttpDocumentRetriever(backchannel) { RequireHttps = Options.RequireHttpsMetadata });
new HttpDocumentRetriever(Options.Backchannel) { RequireHttps = Options.RequireHttpsMetadata });
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public OpenIdConnectAuthenticationNotifications()
SecurityTokenReceived = notification => Task.FromResult(0);
SecurityTokenValidated = notification => Task.FromResult(0);
RedirectToIdentityProvider = notification => Task.FromResult(0);
TokenResponseReceived = notification => Task.FromResult(0);
}

/// <summary>
Expand Down Expand Up @@ -55,5 +56,10 @@ public OpenIdConnectAuthenticationNotifications()
/// Invoked after the security token has passed validation and a ClaimsIdentity has been generated.
/// </summary>
public Func<SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>, Task> SecurityTokenValidated { get; set; }

/// <summary>
/// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
/// </summary>
public Func<TokenResponseReceivedNotification, Task> TokenResponseReceived { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ public OpenIdConnectAuthenticationOptions()
/// <para>Caption: <see cref="OpenIdConnectAuthenticationDefaults.Caption"/>.</para>
/// <para>ProtocolValidator: new <see cref="OpenIdConnectProtocolValidator"/>.</para>
/// <para>RefreshOnIssuerKeyNotFound: true</para>
/// <para>ResponseMode: <see cref="OpenIdConnectResponseMode.FormPost"/></para>
/// <para>ResponseType: <see cref="OpenIdConnectResponseTypes.CodeIdToken"/></para>
/// <para>Scope: <see cref="OpenIdConnectScopes.OpenIdProfile"/>.</para>
/// <para>TokenValidationParameters: new <see cref="TokenValidationParameters"/> with AuthenticationType = authenticationType.</para>
/// <para>UseTokenLifetime: true.</para>
/// <para>RedeemCode: false.</para>
/// </remarks>
/// <param name="authenticationType"> will be used to when creating the <see cref="System.Security.Claims.ClaimsIdentity"/> for the AuthenticationType property.</param>
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthenticationOptions.set_Caption(System.String)", Justification = "Not a LOC field")]
Expand All @@ -60,13 +62,15 @@ public OpenIdConnectAuthenticationOptions(string authenticationType)
NonceLifetime = TimeSpan.FromMinutes(15)
};
RefreshOnIssuerKeyNotFound = true;
ResponseMode = OpenIdConnectResponseMode.FormPost;
ResponseType = OpenIdConnectResponseType.CodeIdToken;
Scope = OpenIdConnectScope.OpenIdProfile;
SecurityTokenValidator = new JwtSecurityTokenHandler();
RequireHttpsMetadata = true;
TokenValidationParameters = new TokenValidationParameters();
UseTokenLifetime = true;
CookieManager = new CookieManager();
RedeemCode = false;
}

/// <summary>
Expand Down Expand Up @@ -122,6 +126,11 @@ public TimeSpan BackchannelTimeout
}
}

/// <summary>
/// Used to communicate with the remote identity provider.
/// </summary>
public HttpClient Backchannel { get; set; }

/// <summary>
/// Get or sets the text that the user can display on a sign in user interface.
/// </summary>
Expand Down Expand Up @@ -216,6 +225,11 @@ public OpenIdConnectProtocolValidator ProtocolValidator
/// </summary>
public string Resource { get; set; }

/// <summary>
/// Gets or sets the 'response_mode'.
/// </summary>
public string ResponseMode { get; set; }

/// <summary>
/// Gets or sets the 'response_type'.
/// </summary>
Expand Down Expand Up @@ -290,9 +304,23 @@ public bool UseTokenLifetime
set;
}

/// <summary>
/// Defines whether access and refresh tokens should be stored in the
/// <see cref="AuthenticationProperties"/> after a successful authorization.
/// This property is set to <c>false</c> by default to reduce
/// the size of the final authentication cookie.
/// </summary>
public bool SaveTokens { get; set; }

/// <summary>
/// An abstraction for reading and setting cookies during the authentication process.
/// </summary>
public ICookieManager CookieManager { get; set; }

/// <summary>
/// When set to <c>true</c> the authorization code will be redeemed for tokens at the token endpoint.
/// This property is set to <c>false</c> by default.
/// </summary>
public bool RedeemCode { get; set; }
}
}

0 comments on commit 569f1c8

Please sign in to comment.