#10 Remove the oboslete Google OpenId auth components.
This commit is contained in:
Родитель
43a9bb509c
Коммит
6ee9653219
|
@ -1,56 +1,16 @@
|
|||
// 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.
|
||||
|
||||
// OpenID is obsolete
|
||||
#pragma warning disable 618
|
||||
|
||||
using System;
|
||||
using Microsoft.Owin.Security;
|
||||
using Microsoft.Owin.Security.Google;
|
||||
|
||||
namespace Owin
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for using <see cref="GoogleAuthenticationMiddleware"/>
|
||||
/// Extension methods for using <see cref="GoogleOAuth2AuthenticationMiddleware"/>
|
||||
/// </summary>
|
||||
public static class GoogleAuthenticationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Authenticate users using Google OpenId
|
||||
/// </summary>
|
||||
/// <param name="app">The <see cref="IAppBuilder"/> passed to the configuration method</param>
|
||||
/// <param name="options">Middleware configuration options</param>
|
||||
/// <returns>The updated <see cref="IAppBuilder"/></returns>
|
||||
[Obsolete("Google is discontinuing support for the OpenId. Use OAuth2 instead.", error: false)]
|
||||
public static IAppBuilder UseGoogleAuthentication(this IAppBuilder app, GoogleAuthenticationOptions options)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException("app");
|
||||
}
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException("options");
|
||||
}
|
||||
|
||||
app.Use(typeof(GoogleAuthenticationMiddleware), app, options);
|
||||
return app;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate users using Google OpenId
|
||||
/// </summary>
|
||||
/// <param name="app">The <see cref="IAppBuilder"/> passed to the configuration method</param>
|
||||
/// <returns>The updated <see cref="IAppBuilder"/></returns>
|
||||
[Obsolete("Google is discontinuing support for the OpenId. Use OAuth2 instead.", error: false)]
|
||||
public static IAppBuilder UseGoogleAuthentication(
|
||||
this IAppBuilder app)
|
||||
{
|
||||
return UseGoogleAuthentication(
|
||||
app,
|
||||
new GoogleAuthenticationOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate users using Google OAuth 2.0
|
||||
/// </summary>
|
||||
|
@ -97,5 +57,4 @@ namespace Owin
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 618
|
||||
}
|
|
@ -1,360 +0,0 @@
|
|||
// 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.
|
||||
|
||||
// OpenID is obsolete
|
||||
#pragma warning disable 618
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Owin.Infrastructure;
|
||||
using Microsoft.Owin.Logging;
|
||||
using Microsoft.Owin.Security.Google.Infrastructure;
|
||||
using Microsoft.Owin.Security.Infrastructure;
|
||||
|
||||
namespace Microsoft.Owin.Security.Google
|
||||
{
|
||||
internal class GoogleAuthenticationHandler : AuthenticationHandler<GoogleAuthenticationOptions>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public GoogleAuthenticationHandler(HttpClient httpClient, ILogger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task<bool> InvokeAsync()
|
||||
{
|
||||
if (Options.CallbackPath.HasValue && Options.CallbackPath == Request.Path)
|
||||
{
|
||||
return await InvokeReturnPathAsync();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
{
|
||||
AuthenticationProperties properties = null;
|
||||
|
||||
try
|
||||
{
|
||||
IReadableStringCollection query = Request.Query;
|
||||
|
||||
properties = UnpackStateParameter(query);
|
||||
if (properties == null)
|
||||
{
|
||||
_logger.WriteWarning("Invalid return state");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Anti-CSRF
|
||||
if (!ValidateCorrelationId(properties, _logger))
|
||||
{
|
||||
return new AuthenticationTicket(null, properties);
|
||||
}
|
||||
|
||||
Message message = await ParseRequestMessageAsync(query);
|
||||
|
||||
bool messageValidated = false;
|
||||
|
||||
Property mode;
|
||||
if (!message.Properties.TryGetValue("mode.http://specs.openid.net/auth/2.0", out mode))
|
||||
{
|
||||
_logger.WriteWarning("Missing mode parameter");
|
||||
return new AuthenticationTicket(null, properties);
|
||||
}
|
||||
|
||||
if (string.Equals("cancel", mode.Value, StringComparison.Ordinal))
|
||||
{
|
||||
_logger.WriteWarning("User cancelled signin request");
|
||||
return new AuthenticationTicket(null, properties);
|
||||
}
|
||||
|
||||
if (string.Equals("id_res", mode.Value, StringComparison.Ordinal))
|
||||
{
|
||||
mode.Value = "check_authentication";
|
||||
|
||||
var requestBody = new FormUrlEncodedContent(message.ToFormValues());
|
||||
HttpResponseMessage response = await _httpClient.PostAsync("https://www.google.com/accounts/o8/ud", requestBody, Request.CallCancelled);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var verifyBody = new Dictionary<string, string[]>();
|
||||
foreach (var line in responseBody.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
int delimiter = line.IndexOf(':');
|
||||
if (delimiter != -1)
|
||||
{
|
||||
verifyBody.Add("openid." + line.Substring(0, delimiter), new[] { line.Substring(delimiter + 1) });
|
||||
}
|
||||
}
|
||||
var verifyMessage = new Message(new ReadableStringCollection(verifyBody), strict: false);
|
||||
Property isValid;
|
||||
if (verifyMessage.Properties.TryGetValue("is_valid.http://specs.openid.net/auth/2.0", out isValid))
|
||||
{
|
||||
if (string.Equals("true", isValid.Value, StringComparison.Ordinal))
|
||||
{
|
||||
messageValidated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
messageValidated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// http://openid.net/specs/openid-authentication-2_0.html#verify_return_to
|
||||
// To verify that the "openid.return_to" URL matches the URL that is processing this assertion:
|
||||
// * The URL scheme, authority, and path MUST be the same between the two URLs.
|
||||
// * Any query parameters that are present in the "openid.return_to" URL MUST also
|
||||
// be present with the same values in the URL of the HTTP request the RP received.
|
||||
if (messageValidated)
|
||||
{
|
||||
// locate the required return_to parameter
|
||||
string actualReturnTo;
|
||||
if (!message.TryGetValue("return_to.http://specs.openid.net/auth/2.0", out actualReturnTo))
|
||||
{
|
||||
_logger.WriteWarning("openid.return_to parameter missing at return address");
|
||||
messageValidated = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create the expected return_to parameter based on the URL that is processing
|
||||
// the assertion, plus exactly and only the the query string parameter (state)
|
||||
// that this RP must have received
|
||||
string expectedReturnTo = BuildReturnTo(GetStateParameter(query));
|
||||
|
||||
if (!string.Equals(actualReturnTo, expectedReturnTo, StringComparison.Ordinal))
|
||||
{
|
||||
_logger.WriteWarning("openid.return_to parameter not equal to expected value based on return address");
|
||||
messageValidated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messageValidated)
|
||||
{
|
||||
IDictionary<string, string> attributeExchangeProperties = new Dictionary<string, string>();
|
||||
foreach (var typeProperty in message.Properties.Values)
|
||||
{
|
||||
if (typeProperty.Namespace == "http://openid.net/srv/ax/1.0" &&
|
||||
typeProperty.Name.StartsWith("type."))
|
||||
{
|
||||
string qname = "value." + typeProperty.Name.Substring("type.".Length) + "http://openid.net/srv/ax/1.0";
|
||||
Property valueProperty;
|
||||
if (message.Properties.TryGetValue(qname, out valueProperty))
|
||||
{
|
||||
attributeExchangeProperties.Add(typeProperty.Value, valueProperty.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var responseNamespaces = new object[]
|
||||
{
|
||||
new XAttribute(XNamespace.Xmlns + "openid", "http://specs.openid.net/auth/2.0"),
|
||||
new XAttribute(XNamespace.Xmlns + "openid.ax", "http://openid.net/srv/ax/1.0")
|
||||
};
|
||||
|
||||
IEnumerable<object> responseProperties = message.Properties
|
||||
.Where(p => p.Value.Namespace != null)
|
||||
.Select(p => (object)new XElement(XName.Get(p.Value.Name.Substring(0, p.Value.Name.Length - 1), p.Value.Namespace), p.Value.Value));
|
||||
|
||||
var responseMessage = new XElement("response", responseNamespaces.Concat(responseProperties).ToArray());
|
||||
|
||||
var identity = new ClaimsIdentity(Options.AuthenticationType);
|
||||
XElement claimedId = responseMessage.Element(XName.Get("claimed_id", "http://specs.openid.net/auth/2.0"));
|
||||
if (claimedId != null)
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, claimedId.Value, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
|
||||
}
|
||||
|
||||
string firstValue;
|
||||
if (attributeExchangeProperties.TryGetValue("http://axschema.org/namePerson/first", out firstValue))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.GivenName, firstValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
|
||||
}
|
||||
string lastValue;
|
||||
if (attributeExchangeProperties.TryGetValue("http://axschema.org/namePerson/last", out lastValue))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Surname, lastValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
|
||||
}
|
||||
string nameValue;
|
||||
if (attributeExchangeProperties.TryGetValue("http://axschema.org/namePerson", out nameValue))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, nameValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(firstValue) && !string.IsNullOrEmpty(lastValue))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, firstValue + " " + lastValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(firstValue))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, firstValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(lastValue))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, lastValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
|
||||
}
|
||||
string emailValue;
|
||||
if (attributeExchangeProperties.TryGetValue("http://axschema.org/contact/email", out emailValue))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, emailValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
|
||||
}
|
||||
|
||||
var context = new GoogleAuthenticatedContext(
|
||||
Context,
|
||||
identity,
|
||||
properties,
|
||||
responseMessage,
|
||||
attributeExchangeProperties);
|
||||
|
||||
await Options.Provider.Authenticated(context);
|
||||
|
||||
return new AuthenticationTicket(context.Identity, context.Properties);
|
||||
}
|
||||
|
||||
return new AuthenticationTicket(null, properties);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.WriteError("Authentication failed", ex);
|
||||
return new AuthenticationTicket(null, properties);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetStateParameter(IReadableStringCollection query)
|
||||
{
|
||||
IList<string> values = query.GetValues("state");
|
||||
if (values != null && values.Count == 1)
|
||||
{
|
||||
return values[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private AuthenticationProperties UnpackStateParameter(IReadableStringCollection query)
|
||||
{
|
||||
string state = GetStateParameter(query);
|
||||
if (state != null)
|
||||
{
|
||||
return Options.StateDataFormat.Unprotect(state);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string BuildReturnTo(string state)
|
||||
{
|
||||
return Request.Scheme + "://" + Request.Host +
|
||||
RequestPathBase + Options.CallbackPath +
|
||||
"?state=" + Uri.EscapeDataString(state);
|
||||
}
|
||||
|
||||
private async Task<Message> ParseRequestMessageAsync(IReadableStringCollection query)
|
||||
{
|
||||
if (Request.Method == "POST")
|
||||
{
|
||||
IFormCollection form = await Request.ReadFormAsync();
|
||||
return new Message(form, strict: true);
|
||||
}
|
||||
return new Message(query, strict: true);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "MemoryStream.Dispose is idempotent")]
|
||||
protected override Task ApplyResponseChallengeAsync()
|
||||
{
|
||||
if (Response.StatusCode != 401)
|
||||
{
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
|
||||
|
||||
if (challenge != null)
|
||||
{
|
||||
string requestPrefix = Request.Scheme + Uri.SchemeDelimiter + Request.Host;
|
||||
|
||||
var state = challenge.Properties;
|
||||
if (String.IsNullOrEmpty(state.RedirectUri))
|
||||
{
|
||||
state.RedirectUri = requestPrefix + Request.PathBase + Request.Path + Request.QueryString;
|
||||
}
|
||||
|
||||
// Anti-CSRF
|
||||
GenerateCorrelationId(state);
|
||||
|
||||
string returnTo = BuildReturnTo(Options.StateDataFormat.Protect(state));
|
||||
|
||||
string authorizationEndpoint =
|
||||
"https://www.google.com/accounts/o8/ud" +
|
||||
"?openid.ns=" + Uri.EscapeDataString("http://specs.openid.net/auth/2.0") +
|
||||
"&openid.ns.ax=" + Uri.EscapeDataString("http://openid.net/srv/ax/1.0") +
|
||||
"&openid.mode=" + Uri.EscapeDataString("checkid_setup") +
|
||||
"&openid.claimed_id=" + Uri.EscapeDataString("http://specs.openid.net/auth/2.0/identifier_select") +
|
||||
"&openid.identity=" + Uri.EscapeDataString("http://specs.openid.net/auth/2.0/identifier_select") +
|
||||
"&openid.return_to=" + Uri.EscapeDataString(returnTo) +
|
||||
"&openid.realm=" + Uri.EscapeDataString(requestPrefix) +
|
||||
"&openid.ax.mode=" + Uri.EscapeDataString("fetch_request") +
|
||||
"&openid.ax.type.email=" + Uri.EscapeDataString("http://axschema.org/contact/email") +
|
||||
"&openid.ax.type.name=" + Uri.EscapeDataString("http://axschema.org/namePerson") +
|
||||
"&openid.ax.type.first=" + Uri.EscapeDataString("http://axschema.org/namePerson/first") +
|
||||
"&openid.ax.type.last=" + Uri.EscapeDataString("http://axschema.org/namePerson/last") +
|
||||
"&openid.ax.required=" + Uri.EscapeDataString("email,name,first,last");
|
||||
|
||||
var redirectContext = new GoogleApplyRedirectContext(
|
||||
Context, Options,
|
||||
state, authorizationEndpoint);
|
||||
Options.Provider.ApplyRedirect(redirectContext);
|
||||
}
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
public async Task<bool> InvokeReturnPathAsync()
|
||||
{
|
||||
AuthenticationTicket model = await AuthenticateAsync();
|
||||
if (model == null)
|
||||
{
|
||||
_logger.WriteWarning("Invalid return state, unable to redirect.");
|
||||
Response.StatusCode = 500;
|
||||
return true;
|
||||
}
|
||||
|
||||
var context = new GoogleReturnEndpointContext(Context, model);
|
||||
context.SignInAsAuthenticationType = Options.SignInAsAuthenticationType;
|
||||
context.RedirectUri = model.Properties.RedirectUri;
|
||||
model.Properties.RedirectUri = null;
|
||||
|
||||
await Options.Provider.ReturnEndpoint(context);
|
||||
|
||||
if (context.SignInAsAuthenticationType != null && context.Identity != null)
|
||||
{
|
||||
ClaimsIdentity signInIdentity = context.Identity;
|
||||
if (!string.Equals(signInIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal))
|
||||
{
|
||||
signInIdentity = new ClaimsIdentity(signInIdentity.Claims, context.SignInAsAuthenticationType, signInIdentity.NameClaimType, signInIdentity.RoleClaimType);
|
||||
}
|
||||
Context.Authentication.SignIn(context.Properties, signInIdentity);
|
||||
}
|
||||
|
||||
if (!context.IsRequestCompleted && context.RedirectUri != null)
|
||||
{
|
||||
if (context.Identity == null)
|
||||
{
|
||||
// add a redirect hint that sign-in failed in some way
|
||||
context.RedirectUri = WebUtilities.AddQueryString(context.RedirectUri, "error", "access_denied");
|
||||
}
|
||||
Response.Redirect(context.RedirectUri);
|
||||
context.RequestCompleted();
|
||||
}
|
||||
|
||||
return context.IsRequestCompleted;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 618
|
|
@ -1,92 +0,0 @@
|
|||
// 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.
|
||||
|
||||
// OpenID is obsolete
|
||||
#pragma warning disable 618
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Owin.Logging;
|
||||
using Microsoft.Owin.Security.DataHandler;
|
||||
using Microsoft.Owin.Security.DataProtection;
|
||||
using Microsoft.Owin.Security.Infrastructure;
|
||||
using Owin;
|
||||
|
||||
namespace Microsoft.Owin.Security.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// OWIN middleware for authenticating users using Google OpenID
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Middleware are not disposable.")]
|
||||
public class GoogleAuthenticationMiddleware : AuthenticationMiddleware<GoogleAuthenticationOptions>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="GoogleAuthenticationMiddleware"/>
|
||||
/// </summary>
|
||||
/// <param name="next">The next middleware in the OWIN pipeline to invoke</param>
|
||||
/// <param name="app">The OWIN application</param>
|
||||
/// <param name="options">Configuration options for the middleware</param>
|
||||
public GoogleAuthenticationMiddleware(
|
||||
OwinMiddleware next,
|
||||
IAppBuilder app,
|
||||
GoogleAuthenticationOptions options)
|
||||
: base(next, options)
|
||||
{
|
||||
_logger = app.CreateLogger<GoogleAuthenticationMiddleware>();
|
||||
|
||||
if (Options.Provider == null)
|
||||
{
|
||||
Options.Provider = new GoogleAuthenticationProvider();
|
||||
}
|
||||
if (Options.StateDataFormat == null)
|
||||
{
|
||||
IDataProtector dataProtecter = app.CreateDataProtector(
|
||||
typeof(GoogleAuthenticationMiddleware).FullName,
|
||||
Options.AuthenticationType, "v1");
|
||||
Options.StateDataFormat = new PropertiesDataFormat(dataProtecter);
|
||||
}
|
||||
if (String.IsNullOrEmpty(Options.SignInAsAuthenticationType))
|
||||
{
|
||||
Options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType();
|
||||
}
|
||||
|
||||
_httpClient = new HttpClient(ResolveHttpMessageHandler(Options));
|
||||
_httpClient.Timeout = Options.BackchannelTimeout;
|
||||
_httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the <see cref="AuthenticationHandler"/> object for processing authentication-related requests.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="AuthenticationHandler"/> configured with the <see cref="GoogleAuthenticationOptions"/> supplied to the constructor.</returns>
|
||||
protected override AuthenticationHandler<GoogleAuthenticationOptions> CreateHandler()
|
||||
{
|
||||
return new GoogleAuthenticationHandler(_httpClient, _logger);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
|
||||
private static HttpMessageHandler ResolveHttpMessageHandler(GoogleAuthenticationOptions options)
|
||||
{
|
||||
HttpMessageHandler handler = options.BackchannelHttpHandler ?? new WebRequestHandler();
|
||||
|
||||
// If they provided a validator, apply it or fail.
|
||||
if (options.BackchannelCertificateValidator != null)
|
||||
{
|
||||
// Set the cert validate callback
|
||||
var webRequestHandler = handler as WebRequestHandler;
|
||||
if (webRequestHandler == null)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
|
||||
}
|
||||
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 618
|
|
@ -1,87 +0,0 @@
|
|||
// 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;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Microsoft.Owin.Security.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration options for <see cref="GoogleAuthenticationMiddleware"/>
|
||||
/// </summary>
|
||||
[Obsolete("Google is discontinuing support for the OpenId. Use OAuth2 instead.", error: false)]
|
||||
public class GoogleAuthenticationOptions : AuthenticationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="GoogleAuthenticationOptions"/>
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters",
|
||||
MessageId = "Microsoft.Owin.Security.Google.GoogleAuthenticationOptions.set_Caption(System.String)", Justification = "Not localizable")]
|
||||
public GoogleAuthenticationOptions()
|
||||
: base(Constants.DefaultAuthenticationType)
|
||||
{
|
||||
Caption = Constants.DefaultAuthenticationType;
|
||||
CallbackPath = new PathString("/signin-google");
|
||||
AuthenticationMode = AuthenticationMode.Passive;
|
||||
BackchannelTimeout = TimeSpan.FromSeconds(60);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
|
||||
/// in back channel communications belong to Google.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The pinned certificate validator.
|
||||
/// </value>
|
||||
/// <remarks>If this property is null then the default certificate checks are performed,
|
||||
/// validating the subject name and if the signing chain is a trusted party.</remarks>
|
||||
public ICertificateValidator BackchannelCertificateValidator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets timeout value in milliseconds for back channel communications with Google.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The back channel timeout.
|
||||
/// </value>
|
||||
public TimeSpan BackchannelTimeout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The HttpMessageHandler used to communicate with Google.
|
||||
/// This cannot be set at the same time as BackchannelCertificateValidator unless the value
|
||||
/// can be downcast to a WebRequestHandler.
|
||||
/// </summary>
|
||||
public HttpMessageHandler BackchannelHttpHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get or sets the text that the user can display on a sign in user interface.
|
||||
/// </summary>
|
||||
public string Caption
|
||||
{
|
||||
get { return Description.Caption; }
|
||||
set { Description.Caption = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The request path within the application's base path where the user-agent will be returned.
|
||||
/// The middleware will process this request when it arrives.
|
||||
/// Default value is "/signin-google".
|
||||
/// </summary>
|
||||
public PathString CallbackPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of another authentication middleware which will be responsible for actually issuing a user <see cref="System.Security.Claims.ClaimsIdentity"/>.
|
||||
/// </summary>
|
||||
public string SignInAsAuthenticationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IGoogleAuthenticationProvider"/> used to handle authentication events.
|
||||
/// </summary>
|
||||
public IGoogleAuthenticationProvider Provider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type used to secure data handled by the middleware.
|
||||
/// </summary>
|
||||
public ISecureDataFormat<AuthenticationProperties> StateDataFormat { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Owin.Security.Google.Infrastructure
|
||||
{
|
||||
internal class Message
|
||||
{
|
||||
public Message(IReadableStringCollection parameters, bool strict)
|
||||
{
|
||||
Namespaces = new Dictionary<string, Property>(StringComparer.Ordinal);
|
||||
Properties = new Dictionary<string, Property>(parameters.Count(), StringComparer.Ordinal);
|
||||
Add(parameters, strict);
|
||||
}
|
||||
|
||||
public Dictionary<string, Property> Namespaces { get; private set; }
|
||||
public Dictionary<string, Property> Properties { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds the openid parameters from querystring or form body into Namespaces and Properties collections.
|
||||
/// This normalizes the parameter name, by replacing the variable namespace alias with the
|
||||
/// actual namespace in the collection's key, and will optionally skip any parameters that are
|
||||
/// not signed if the strict argument is true.
|
||||
/// </summary>
|
||||
/// <param name="parameters">The keys and values of the incoming querystring or form body</param>
|
||||
/// <param name="strict">True if keys that are not signed should be ignored</param>
|
||||
private void Add(IReadableStringCollection parameters, bool strict)
|
||||
{
|
||||
IEnumerable<KeyValuePair<string, string[]>> addingParameters;
|
||||
|
||||
// strict is true if keys that are not signed should be strict
|
||||
if (strict)
|
||||
{
|
||||
IList<string> signed = parameters.GetValues("openid.signed");
|
||||
if (signed == null ||
|
||||
signed.Count != 1)
|
||||
{
|
||||
// nothing is added if the signed parameter is not present
|
||||
return;
|
||||
}
|
||||
|
||||
// determine the set of keys that are signed, or which may be used without
|
||||
// signing. ns, mode, signed, and sig each may be used without signing.
|
||||
var strictKeys = new HashSet<string>(signed[0]
|
||||
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(value => "openid." + value)
|
||||
.Concat(new[] { "openid.ns", "openid.mode", "openid.signed", "openid.sig" }));
|
||||
|
||||
// the parameters to add are only the parameters what are in this set
|
||||
addingParameters = parameters.Where(kv => strictKeys.Contains(kv.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
// when strict is false all of the incoming parameters are to be added
|
||||
addingParameters = parameters;
|
||||
}
|
||||
|
||||
// convert the incoming parameter strings into Property objects. the
|
||||
// Key is the raw key name. The Name starts of being equal to Key with a
|
||||
// trailing dot appended. The Value is the query or form value, with a comma delimiter
|
||||
// inserted between multiply occuring values.
|
||||
Property[] addingProperties = addingParameters.Select(kv => new Property
|
||||
{
|
||||
Key = kv.Key,
|
||||
Name = kv.Key + ".",
|
||||
Value = string.Join(",", kv.Value)
|
||||
}).ToArray();
|
||||
|
||||
// first, recognize which parameters are namespace declarations
|
||||
|
||||
var namespacePrefixes = new Dictionary<string, Property>(StringComparer.Ordinal);
|
||||
foreach (var item in addingProperties)
|
||||
{
|
||||
// namespaces appear as with "openid.ns" or "openid.ns.alias"
|
||||
if (item.Name.StartsWith("openid.ns.", StringComparison.Ordinal))
|
||||
{
|
||||
// the value of the parameter is the uri of the namespace
|
||||
item.Namespace = item.Value;
|
||||
item.Name = "openid." + item.Name.Substring("openid.ns.".Length);
|
||||
|
||||
// the namespaces collection is keyed by the ns uri
|
||||
Namespaces.Add(item.Namespace, item);
|
||||
|
||||
// and the prefixes collection is keyed by "openid.alias."
|
||||
namespacePrefixes.Add(item.Name, item);
|
||||
}
|
||||
}
|
||||
|
||||
// second, recognize which parameters are property values
|
||||
|
||||
foreach (var item in addingProperties)
|
||||
{
|
||||
// anything with a namespace was already added to Namespaces
|
||||
if (item.Namespace == null)
|
||||
{
|
||||
// look for the namespace match for this property.
|
||||
Property match = null;
|
||||
|
||||
// try finding where openid.alias.arg2 matches openid.ns.alies namespace
|
||||
if (item.Name.StartsWith("openid.", StringComparison.Ordinal))
|
||||
{
|
||||
int dotIndex = item.Name.IndexOf('.', "openid.".Length);
|
||||
if (dotIndex != -1)
|
||||
{
|
||||
string namespacePrefix = item.Name.Substring(0, dotIndex + 1);
|
||||
namespacePrefixes.TryGetValue(namespacePrefix, out match);
|
||||
}
|
||||
}
|
||||
|
||||
// then try finding where openid.arg1 should match openid.ns namespace
|
||||
if (match == null)
|
||||
{
|
||||
namespacePrefixes.TryGetValue("openid.", out match);
|
||||
}
|
||||
|
||||
// when a namespace is found
|
||||
if (match != null)
|
||||
{
|
||||
// the property's namespace is defined, and the namespace's prefix is removed
|
||||
item.Namespace = match.Namespace;
|
||||
item.Name = item.Name.Substring(match.Name.Length);
|
||||
}
|
||||
|
||||
// the resulting property key is keyed by the local name and namespace
|
||||
// so "openid.arg1" becomes "arg1.namespace-uri-of-openid"
|
||||
// and "openid.alias.arg2" becomes "arg2.namespace-uri-of-alias"
|
||||
Properties.Add(item.Name + item.Namespace, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out string value)
|
||||
{
|
||||
Property property;
|
||||
if (Properties.TryGetValue(key, out property))
|
||||
{
|
||||
value = property.Value;
|
||||
return true;
|
||||
}
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<string, string>> ToFormValues()
|
||||
{
|
||||
return Namespaces.Concat(Properties).Select(pair => new KeyValuePair<string, string>(pair.Value.Key, pair.Value.Value));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
// 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.Owin.Security.Google.Infrastructure
|
||||
{
|
||||
internal class Property
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string Namespace { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
|
@ -56,25 +56,15 @@
|
|||
<Link>Properties\CommonAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="GoogleAuthenticationHandler.cs" />
|
||||
<Compile Include="GoogleAuthenticationExtensions.cs" />
|
||||
<Compile Include="GoogleOAuth2AuthenticationMiddleware.cs" />
|
||||
<Compile Include="GoogleAuthenticationMiddleware.cs" />
|
||||
<Compile Include="GoogleAuthenticationOptions.cs" />
|
||||
<Compile Include="GoogleOAuth2AuthenticationHandler.cs" />
|
||||
<Compile Include="GoogleOAuth2AuthenticationOptions.cs" />
|
||||
<Compile Include="Infrastructure\Message.cs" />
|
||||
<Compile Include="Infrastructure\Property.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Provider\GoogleAuthenticatedContext.cs" />
|
||||
<Compile Include="Provider\GoogleAuthenticationProvider.cs" />
|
||||
<Compile Include="Provider\GoogleApplyRedirectContext.cs" />
|
||||
<Compile Include="Provider\GoogleOAuth2ApplyRedirectContext.cs" />
|
||||
<Compile Include="Provider\GoogleOAuth2AuthenticatedContext.cs" />
|
||||
<Compile Include="Provider\GoogleOAuth2AuthenticationProvider.cs" />
|
||||
<Compile Include="Provider\GoogleOAuth2ReturnEndpointContext.cs" />
|
||||
<Compile Include="Provider\GoogleReturnEndpointContext.cs" />
|
||||
<Compile Include="Provider\IGoogleAuthenticationProvider.cs" />
|
||||
<Compile Include="Provider\IGoogleOAuth2AuthenticationProvider.cs" />
|
||||
<Compile Include="Resources.Designer.cs">
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<projectUrl>$projectUrl$</projectUrl>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<title>$title$</title>
|
||||
<description>Contains middlewares to support Google's OpenId and OAuth 2.0 authentication workflows.</description>
|
||||
<description>Contains middlewares to support Google's OAuth 2.0 authentication workflow.</description>
|
||||
<tags>$tags$</tags>
|
||||
<frameworkAssemblies>
|
||||
<frameworkAssembly assemblyName="System.Net.Http" />
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
// 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.
|
||||
|
||||
// OpenID is obsolete
|
||||
#pragma warning disable 618
|
||||
|
||||
using Microsoft.Owin.Security.Provider;
|
||||
|
||||
namespace Microsoft.Owin.Security.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Context passed when a Challenge causes a redirect to authorize endpoint in the Google OpenID middleware
|
||||
/// </summary>
|
||||
public class GoogleApplyRedirectContext : BaseContext<GoogleAuthenticationOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new context object.
|
||||
/// </summary>
|
||||
/// <param name="context">The OWIN request context</param>
|
||||
/// <param name="options">The Google OpenID middleware options</param>
|
||||
/// <param name="properties">The authentication properties of the challenge</param>
|
||||
/// <param name="redirectUri">The initial redirect URI</param>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "3#",
|
||||
Justification = "Represents header value")]
|
||||
public GoogleApplyRedirectContext(IOwinContext context, GoogleAuthenticationOptions options,
|
||||
AuthenticationProperties properties, string redirectUri)
|
||||
: base(context, options)
|
||||
{
|
||||
RedirectUri = redirectUri;
|
||||
Properties = properties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URI used for the redirect operation.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Represents header value")]
|
||||
public string RedirectUri { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authentication properties of the challenge
|
||||
/// </summary>
|
||||
public AuthenticationProperties Properties { get; private set; }
|
||||
}
|
||||
}
|
||||
#pragma warning restore 618
|
|
@ -1,58 +0,0 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Owin.Security.Provider;
|
||||
|
||||
namespace Microsoft.Owin.Security.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
|
||||
/// </summary>
|
||||
public class GoogleAuthenticatedContext : BaseContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="GoogleAuthenticatedContext"/>
|
||||
/// </summary>
|
||||
/// <param name="context">The OWIN environment</param>
|
||||
/// <param name="identity">The <see cref="ClaimsIdentity"/> representing the user</param>
|
||||
/// <param name="properties">A property bag for common authentication properties</param>
|
||||
/// <param name="responseMessage"></param>
|
||||
/// <param name="attributeExchangeProperties"></param>
|
||||
public GoogleAuthenticatedContext(
|
||||
IOwinContext context,
|
||||
ClaimsIdentity identity,
|
||||
AuthenticationProperties properties,
|
||||
XElement responseMessage,
|
||||
IDictionary<string, string> attributeExchangeProperties)
|
||||
: base(context)
|
||||
{
|
||||
Identity = identity;
|
||||
Properties = properties;
|
||||
ResponseMessage = responseMessage;
|
||||
AttributeExchangeProperties = attributeExchangeProperties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ClaimsIdentity"/> representing the user
|
||||
/// </summary>
|
||||
public ClaimsIdentity Identity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a property bag for common authentication properties
|
||||
/// </summary>
|
||||
public AuthenticationProperties Properties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets parsed response message from openid query string
|
||||
/// </summary>
|
||||
public XElement ResponseMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key-value dictinary of message properties
|
||||
/// </summary>
|
||||
public IDictionary<string, string> AttributeExchangeProperties { get; private set; }
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
// 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;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Owin.Security.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Default <see cref="IGoogleAuthenticationProvider"/> implementation.
|
||||
/// </summary>
|
||||
public class GoogleAuthenticationProvider : IGoogleAuthenticationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="GoogleAuthenticationProvider"/>
|
||||
/// </summary>
|
||||
public GoogleAuthenticationProvider()
|
||||
{
|
||||
OnAuthenticated = context => Task.FromResult<object>(null);
|
||||
OnReturnEndpoint = context => Task.FromResult<object>(null);
|
||||
OnApplyRedirect = context =>
|
||||
context.Response.Redirect(context.RedirectUri);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
|
||||
/// </summary>
|
||||
public Func<GoogleAuthenticatedContext, Task> OnAuthenticated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
|
||||
/// </summary>
|
||||
public Func<GoogleReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delegate that is invoked when the ApplyRedirect method is invoked.
|
||||
/// </summary>
|
||||
public Action<GoogleApplyRedirectContext> OnApplyRedirect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whenever Google successfully authenticates a user
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
public virtual Task Authenticated(GoogleAuthenticatedContext context)
|
||||
{
|
||||
return OnAuthenticated(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
public virtual Task ReturnEndpoint(GoogleReturnEndpointContext context)
|
||||
{
|
||||
return OnReturnEndpoint(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a Challenge causes a redirect to authorize endpoint in the Google OpenID middleware
|
||||
/// </summary>
|
||||
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge </param>
|
||||
public virtual void ApplyRedirect(GoogleApplyRedirectContext context)
|
||||
{
|
||||
OnApplyRedirect(context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// 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.Owin.Security.Provider;
|
||||
|
||||
namespace Microsoft.Owin.Security.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides context information to middleware providers.
|
||||
/// </summary>
|
||||
public class GoogleReturnEndpointContext : ReturnEndpointContext
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="context">OWIN environment</param>
|
||||
/// <param name="ticket">The authentication ticket</param>
|
||||
public GoogleReturnEndpointContext(
|
||||
IOwinContext context,
|
||||
AuthenticationTicket ticket)
|
||||
: base(context, ticket)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// 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.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Owin.Security.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies callback methods which the <see cref="GoogleAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
|
||||
/// </summary>
|
||||
public interface IGoogleAuthenticationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked whenever Google succesfully authenticates a user
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
Task Authenticated(GoogleAuthenticatedContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
Task ReturnEndpoint(GoogleReturnEndpointContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a Challenge causes a redirect to authorize endpoint in the Google OpenID middleware
|
||||
/// </summary>
|
||||
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge </param>
|
||||
void ApplyRedirect(GoogleApplyRedirectContext context);
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
// 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.
|
||||
|
||||
// OpenID is obsolete
|
||||
#pragma warning disable 618
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Owin.Security.Cookies;
|
||||
using Microsoft.Owin.Security.Google;
|
||||
using Microsoft.Owin.Testing;
|
||||
using Owin;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Owin.Security.Tests.Google
|
||||
{
|
||||
public class GoogleOpenIDMiddlewareTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ChallengeWillTriggerApplyRedirectEvent()
|
||||
{
|
||||
var options = new GoogleAuthenticationOptions()
|
||||
{
|
||||
Provider = new GoogleAuthenticationProvider
|
||||
{
|
||||
OnApplyRedirect = context =>
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri + "&custom=test");
|
||||
}
|
||||
}
|
||||
};
|
||||
var server = CreateServer(
|
||||
app => app.UseGoogleAuthentication(options),
|
||||
context =>
|
||||
{
|
||||
context.Authentication.Challenge("Google");
|
||||
return true;
|
||||
});
|
||||
var transaction = await SendAsync(server, "http://example.com/challenge");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
var query = transaction.Response.Headers.Location.Query;
|
||||
query.ShouldContain("custom=test");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChallengeWillTriggerRedirection()
|
||||
{
|
||||
var server = CreateServer(
|
||||
app => app.UseGoogleAuthentication(),
|
||||
context =>
|
||||
{
|
||||
context.Authentication.Challenge("Google");
|
||||
return true;
|
||||
});
|
||||
var transaction = await SendAsync(server, "http://example.com/challenge");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
var location = transaction.Response.Headers.Location.AbsoluteUri;
|
||||
location.ShouldContain("https://www.google.com/accounts/o8/ud");
|
||||
location.ShouldContain("?openid.ns=");
|
||||
location.ShouldContain("&openid.ns.ax=");
|
||||
location.ShouldContain("&openid.mode=");
|
||||
location.ShouldContain("&openid.claimed_id=");
|
||||
location.ShouldContain("&openid.identity=");
|
||||
location.ShouldContain("&openid.return_to=");
|
||||
location.ShouldContain("&openid.realm=");
|
||||
location.ShouldContain("&openid.ax.mode=");
|
||||
}
|
||||
|
||||
private static TestServer CreateServer(Action<IAppBuilder> configure, Func<IOwinContext, bool> handler)
|
||||
{
|
||||
return TestServer.Create(app =>
|
||||
{
|
||||
app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests";
|
||||
app.UseCookieAuthentication(new CookieAuthenticationOptions
|
||||
{
|
||||
AuthenticationType = "External"
|
||||
});
|
||||
app.SetDefaultSignInAsAuthenticationType("External");
|
||||
if (configure != null)
|
||||
{
|
||||
configure(app);
|
||||
}
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
if (handler == null || !handler(context))
|
||||
{
|
||||
await next();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static async Task<Transaction> SendAsync(TestServer server, string uri, string cookieHeader = null)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, uri);
|
||||
if (!string.IsNullOrEmpty(cookieHeader))
|
||||
{
|
||||
request.Headers.Add("Cookie", cookieHeader);
|
||||
}
|
||||
var transaction = new Transaction
|
||||
{
|
||||
Request = request,
|
||||
Response = await server.HttpClient.SendAsync(request),
|
||||
};
|
||||
if (transaction.Response.Headers.Contains("Set-Cookie"))
|
||||
{
|
||||
transaction.SetCookie = transaction.Response.Headers.GetValues("Set-Cookie").ToList();
|
||||
}
|
||||
transaction.ResponseText = await transaction.Response.Content.ReadAsStringAsync();
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
private class Transaction
|
||||
{
|
||||
public HttpRequestMessage Request { get; set; }
|
||||
public HttpResponseMessage Response { get; set; }
|
||||
public IList<string> SetCookie { get; set; }
|
||||
public string ResponseText { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 618
|
|
@ -75,7 +75,6 @@
|
|||
<Compile Include="DataHandler\Base64UrlTextEncoderTests.cs" />
|
||||
<Compile Include="Facebook\FacebookMiddlewareTests.cs" />
|
||||
<Compile Include="Google\GoogleOAuth2MiddlewareTests.cs" />
|
||||
<Compile Include="Google\GoogleOpenIDMiddlewareTests.cs" />
|
||||
<Compile Include="MicrosoftAccount\MicrosoftAccountMiddlewareTests.cs" />
|
||||
<Compile Include="OAuth\OAuth2AuthorizationCustomGrantTests.cs" />
|
||||
<Compile Include="OAuth\OAuth2BearerTokenTests.cs" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче