+ support for response_mode=form_post and

new Hook in OAuthAuthorizationServerProvider: OnAuthorizationEndpointResponse
This commit is contained in:
manfredsteyer 2014-04-21 17:19:24 +02:00 коммит произвёл Chris Ross
Родитель d0fbf8fc45
Коммит d9ed49eb5c
11 изменённых файлов: 1768 добавлений и 1421 удалений

Просмотреть файл

@ -35,6 +35,12 @@ namespace Microsoft.Owin.Security.OAuth.Messages
/// </summary>
public string ResponseType { get; set; }
/// <summary>
/// The "response_mode" query string parameter of the Authorize request. Known values are "query", "fragment" and "form_post"
/// See also, http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
/// </summary>
public string ResponseMode { get; set; }
/// <summary>
/// The "client_id" query string parameter of the Authorize request.
/// </summary>
@ -58,13 +64,32 @@ namespace Microsoft.Owin.Security.OAuth.Messages
/// </summary>
public string State { get; set; }
/// <summary>
/// True if the "response_type" query string contains the passed responseType.
/// See also, http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
/// </summary>
/// <param name="responseType">The responseType that is expected within the "response_type" query string</param>
/// <returns>True if the "response_type" query string contains the passed responseType.</returns>
public bool ContainsGrantType(string responseType)
{
var parts = ResponseType.Split(' ');
foreach (var part in parts)
{
if (string.Equals(part, responseType, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
/// <summary>
/// True if the "response_type" query string parameter is "code".
/// See also, http://tools.ietf.org/html/rfc6749#section-4.1.1
/// </summary>
public bool IsAuthorizationCodeGrantType
{
get { return string.Equals(ResponseType, Constants.ResponseTypes.Code, StringComparison.Ordinal); }
get { return ContainsGrantType(Constants.ResponseTypes.Code); }
}
/// <summary>
@ -73,7 +98,12 @@ namespace Microsoft.Owin.Security.OAuth.Messages
/// </summary>
public bool IsImplicitGrantType
{
get { return string.Equals(ResponseType, Constants.ResponseTypes.Token, StringComparison.Ordinal); }
get { return ContainsGrantType(Constants.ResponseTypes.Token); }
}
public bool IsFormPostResponseMode
{
get { return string.Equals(ResponseMode, Constants.ResponseModes.FormPost, StringComparison.Ordinal); }
}
private void AddParameter(string name, string value)
@ -98,6 +128,10 @@ namespace Microsoft.Owin.Security.OAuth.Messages
{
State = value;
}
else if (string.Equals(name, Constants.Parameters.ResponseMode, StringComparison.Ordinal))
{
ResponseMode = value;
}
}
}
}

Просмотреть файл

@ -70,11 +70,13 @@
<Compile Include="OAuthBearerAuthenticationOptions.cs" />
<Compile Include="OAuthConstants.cs" />
<Compile Include="OAuthDefaults.cs" />
<Compile Include="Provider\OAuthTokenEndpointResponseContext.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Provider\BaseValidatingClientContext.cs" />
<Compile Include="Provider\BaseValidatingContext.cs" />
<Compile Include="Provider\BaseValidatingTicketContext.cs" />
<Compile Include="Provider\DefaultBehavior.cs" />
<Compile Include="Provider\OAuthAuthorizeEndpointResponseContext.cs" />
<Compile Include="Provider\OAuthAuthorizeEndpointContext.cs" />
<Compile Include="Provider\IOAuthAuthorizationServerProvider.cs" />
<Compile Include="Provider\OAuthAuthorizationServerProvider.cs" />

Просмотреть файл

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
@ -12,6 +13,7 @@ using Microsoft.Owin.Security.Infrastructure;
using Microsoft.Owin.Security.OAuth.Messages;
using Newtonsoft.Json;
namespace Microsoft.Owin.Security.OAuth
{
internal class OAuthAuthorizationServerHandler : AuthenticationHandler<OAuthAuthorizationServerOptions>
@ -172,7 +174,7 @@ namespace Microsoft.Owin.Security.OAuth
return;
}
string location = _clientContext.RedirectUri;
var returnParameter = new Dictionary<string, string>();
if (_authorizeEndpointRequest.IsAuthorizationCodeGrantType)
{
@ -204,15 +206,51 @@ namespace Microsoft.Owin.Security.OAuth
await SendErrorRedirectAsync(_clientContext, errorContext);
}
location = WebUtilities.AddQueryString(location, Constants.Parameters.Code, code);
var authResponseContext = new OAuthAuthorizationEndpointResponseContext(
Context,
Options,
new AuthenticationTicket(signin.Identity, signin.Properties),
_authorizeEndpointRequest,
null,
code);
await Options.Provider.AuthorizationEndpointResponse(authResponseContext);
foreach (var parameter in authResponseContext.AdditionalResponseParameters)
{
returnParameter[parameter.Key] = parameter.Value.ToString();
}
returnParameter[Constants.Parameters.Code] = code;
if (!String.IsNullOrEmpty(_authorizeEndpointRequest.State))
{
location = WebUtilities.AddQueryString(location, Constants.Parameters.State, _authorizeEndpointRequest.State);
returnParameter[Constants.Parameters.State] = _authorizeEndpointRequest.State;
}
string location = "";
if (_authorizeEndpointRequest.IsFormPostResponseMode)
{
location = Options.FormPostEndpoint.ToString();
returnParameter[Constants.Parameters.RedirectUri] = _clientContext.RedirectUri;
}
else
{
location = _clientContext.RedirectUri;
}
foreach (var key in returnParameter.Keys)
{
location = WebUtilities.AddQueryString(location, key, returnParameter[key]);
}
Response.Redirect(location);
}
else if (_authorizeEndpointRequest.IsImplicitGrantType)
{
string location = _clientContext.RedirectUri;
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
signin.Properties.IssuedUtc = currentUtc;
signin.Properties.ExpiresUtc = currentUtc.Add(Options.AccessTokenExpireTimeSpan);
@ -249,10 +287,28 @@ namespace Microsoft.Owin.Security.OAuth
{
appender.Append(Constants.Parameters.State, _authorizeEndpointRequest.State);
}
var authResponseContext = new OAuthAuthorizationEndpointResponseContext(
Context,
Options,
new AuthenticationTicket(signin.Identity, signin.Properties),
_authorizeEndpointRequest,
accessToken,
null);
await Options.Provider.AuthorizationEndpointResponse(authResponseContext);
foreach (var parameter in authResponseContext.AdditionalResponseParameters)
{
appender.Append(parameter.Key, parameter.Value.ToString());
}
Response.Redirect(appender.ToString());
}
}
private async Task InvokeTokenEndpointAsync()
{
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
@ -375,6 +431,16 @@ namespace Microsoft.Owin.Security.OAuth
await Options.RefreshTokenProvider.CreateAsync(refreshTokenCreateContext);
string refreshToken = refreshTokenCreateContext.Token;
var tokenEndpointResponseContext = new OAuthTokenEndpointResponseContext(
Context,
Options,
ticket,
tokenEndpointRequest,
accessToken,
tokenEndpointContext.AdditionalResponseParameters);
await Options.Provider.TokenEndpointResponse(tokenEndpointResponseContext);
var memory = new MemoryStream();
byte[] body;
using (var writer = new JsonTextWriter(new StreamWriter(memory)))
@ -399,7 +465,7 @@ namespace Microsoft.Owin.Security.OAuth
writer.WritePropertyName(Constants.Parameters.RefreshToken);
writer.WriteValue(refreshToken);
}
foreach (var additionalResponseParameter in tokenEndpointContext.AdditionalResponseParameters)
foreach (var additionalResponseParameter in tokenEndpointResponseContext.AdditionalResponseParameters)
{
writer.WritePropertyName(additionalResponseParameter.Key);
writer.WriteValue(additionalResponseParameter.Value);

Просмотреть файл

@ -120,5 +120,12 @@ namespace Microsoft.Owin.Security.OAuth
/// redirect_uri authorize request parameter to have http URI addresses.
/// </summary>
public bool AllowInsecureHttp { get; set; }
/// <summary>
/// Endpoint responsible for Form Post Response Mode
/// See also, http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
/// </summary>
public PathString FormPostEndpoint { get; set; }
}
}

Просмотреть файл

@ -23,6 +23,8 @@ namespace Microsoft.Owin.Security.OAuth
public const string ExpiresIn = "expires_in";
public const string AccessToken = "access_token";
public const string TokenType = "token_type";
public const string ResponseMode = "response_mode";
}
public static class ResponseTypes
@ -59,5 +61,10 @@ namespace Microsoft.Owin.Security.OAuth
public const string ClientId = "client_id";
public const string RedirectUri = "redirect_uri";
}
public static class ResponseModes
{
public const string FormPost = "form_post";
}
}
}

Просмотреть файл

@ -150,5 +150,23 @@ namespace Microsoft.Owin.Security.OAuth
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task TokenEndpoint(OAuthTokenEndpointContext context);
/// <summary>
/// Called before the AuthorizationEndpoint redirects its response to the caller. The response could be the
/// token, when using implicit flow or the AuthorizationEndpoint when using authorization code flow.
/// An application may implement this call in order to do any final modification of the claims being used
/// to issue access or refresh tokens. This call may also be used in order to add additional
/// response parameters to the authorization endpoint's response.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context);
/// <summary>
/// Called before the TokenEndpoint redirects its response to the caller.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context);
}
}

Просмотреть файл

@ -33,6 +33,10 @@ namespace Microsoft.Owin.Security.OAuth
OnAuthorizeEndpoint = context => Task.FromResult<object>(null);
OnTokenEndpoint = context => Task.FromResult<object>(null);
OnAuthorizationEndpointResponse = context => Task.FromResult<object>(null);
OnTokenEndpointResponse = context => Task.FromResult<object>(null);
}
/// <summary>
@ -152,6 +156,24 @@ namespace Microsoft.Owin.Security.OAuth
/// </summary>
public Func<OAuthTokenEndpointContext, Task> OnTokenEndpoint { get; set; }
/// <summary>
/// Called before the AuthorizationEndpoint redirects its response to the caller. The response could be the
/// token, when using implicit flow or the AuthorizationEndpoint when using authorization code flow.
/// An application may implement this call in order to do any final modification of the claims being used
/// to issue access or refresh tokens. This call may also be used in order to add additional
/// response parameters to the authorization endpoint's response.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public Func<OAuthAuthorizationEndpointResponseContext, Task> OnAuthorizationEndpointResponse { get; set; }
/// <summary>
/// Called before the TokenEndpoint redirects its response to the caller.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public Func<OAuthTokenEndpointResponseContext, Task> OnTokenEndpointResponse { get; set; }
/// <summary>
/// Called to determine if an incoming request is treated as an Authorize or Token
/// endpoint. If Options.AuthorizeEndpointPath or Options.TokenEndpointPath
@ -328,5 +350,29 @@ namespace Microsoft.Owin.Security.OAuth
{
return OnTokenEndpoint.Invoke(context);
}
/// <summary>
/// Called before the AuthorizationEndpoint redirects its response to the caller. The response could be the
/// token, when using implicit flow or the AuthorizationEndpoint when using authorization code flow.
/// An application may implement this call in order to do any final modification of the claims being used
/// to issue access or refresh tokens. This call may also be used in order to add additional
/// response parameters to the authorization endpoint's response.
/// </summary>
/// <param name="context">The context of the event carries information in and results out.</param>
/// <returns>Task to enable asynchronous execution</returns>
public virtual Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context)
{
return OnAuthorizationEndpointResponse.Invoke(context);
}
/// <summary>
/// Called before the TokenEndpoint redirects its response to the caller.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
{
return OnTokenEndpointResponse.Invoke(context); ;
}
}
}

Просмотреть файл

@ -0,0 +1,78 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.Owin.Security.OAuth.Messages;
using Microsoft.Owin.Security.Provider;
namespace Microsoft.Owin.Security.OAuth
{
/// <summary>
/// Provides context information when processing an Authorization Response
/// </summary>
public class OAuthAuthorizationEndpointResponseContext: EndpointContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthAuthorizationEndpointResponseContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="ticket"></param>
/// <param name="tokenEndpointRequest"></param>
public OAuthAuthorizationEndpointResponseContext(
IOwinContext context,
OAuthAuthorizationServerOptions options,
AuthenticationTicket ticket,
AuthorizeEndpointRequest authorizeEndpointRequest,
string accessToken,
string authorizationCode)
: base(context, options)
{
if (ticket == null)
{
throw new ArgumentNullException("ticket");
}
Identity = ticket.Identity;
Properties = ticket.Properties;
AuthorizeEndpointRequest = authorizeEndpointRequest;
AdditionalResponseParameters = new Dictionary<string, object>(StringComparer.Ordinal);
AccessToken = accessToken;
AuthorizationCode = authorizationCode;
}
/// <summary>
/// Gets the identity of the resource owner.
/// </summary>
public ClaimsIdentity Identity { get; private set; }
/// <summary>
/// Dictionary containing the state of the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
/// <summary>
/// Gets information about the authorize endpoint request.
/// </summary>
public AuthorizeEndpointRequest AuthorizeEndpointRequest { get; private set; }
/// <summary>
/// Enables additional values to be appended to the token response.
/// </summary>
public IDictionary<string, object> AdditionalResponseParameters { get; private set; }
/// <summary>
/// The serialized Access-Token. Depending on the flow, it can be null.
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// The created Authorization-Code. Depending on the flow, it can be null.
/// </summary>
public string AuthorizationCode { get; private set; }
}
}

Просмотреть файл

@ -0,0 +1,89 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.Owin.Security.OAuth.Messages;
using Microsoft.Owin.Security.Provider;
namespace Microsoft.Owin.Security.OAuth
{
/// <summary>
/// Provides context information used at the end of a token-endpoint-request.
/// </summary>
public class OAuthTokenEndpointResponseContext : EndpointContext<OAuthAuthorizationServerOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthTokenEndpointResponseContext"/> class
/// </summary>
/// <param name="context"></param>
/// <param name="options"></param>
/// <param name="ticket"></param>
/// <param name="tokenEndpointRequest"></param>
public OAuthTokenEndpointResponseContext(
IOwinContext context,
OAuthAuthorizationServerOptions options,
AuthenticationTicket ticket,
TokenEndpointRequest tokenEndpointRequest,
string accessToken,
IDictionary<string, object> additionalResponseParameters)
: base(context, options)
{
if (ticket == null)
{
throw new ArgumentNullException("ticket");
}
Identity = ticket.Identity;
Properties = ticket.Properties;
TokenEndpointRequest = tokenEndpointRequest;
AdditionalResponseParameters = new Dictionary<string, object>(StringComparer.Ordinal);
TokenIssued = Identity != null;
AccessToken = accessToken;
AdditionalResponseParameters = additionalResponseParameters;
}
/// <summary>
/// Gets the identity of the resource owner.
/// </summary>
public ClaimsIdentity Identity { get; private set; }
/// <summary>
/// Dictionary containing the state of the authentication session.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
/// <summary>
/// The issued Access-Token
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// Gets information about the token endpoint request.
/// </summary>
public TokenEndpointRequest TokenEndpointRequest { get; set; }
/// <summary>
/// Gets whether or not the token should be issued.
/// </summary>
public bool TokenIssued { get; private set; }
/// <summary>
/// Enables additional values to be appended to the token response.
/// </summary>
public IDictionary<string, object> AdditionalResponseParameters { get; private set; }
/// <summary>
/// Issues the token.
/// </summary>
/// <param name="identity"></param>
/// <param name="properties"></param>
public void Issue(ClaimsIdentity identity, AuthenticationProperties properties)
{
Identity = identity;
Properties = properties;
TokenIssued = true;
}
}
}