Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asp Mvc Core support #1109

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion NuGet.Config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="globalPackagesFolder" value="./Packages" />
Expand All @@ -8,4 +8,9 @@
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="LocalSupport" value="./NuPkgs/Support" />
</packageSources>
<disabledPackageSources>
<add key="LocalSupport" value="true" />
<add key="telerik local" value="true" />
<add key="telerik local" value="true" />
</disabledPackageSources>
</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.4" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Google.Apis.Auth\Google.Apis.Auth.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright 2013 Google Inc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Extensions;
using Google.Apis.Auth.OAuth2.Web;
using Microsoft.AspNetCore.Mvc;

namespace Google.Apis.Auth.OAuth2.AspMvcCore
{
/// <summary>
/// Thread-safe OAuth 2.0 authorization code flow for a MVC web application that persists end-user credentials.
/// </summary>
public class AuthorizationCodeMvcApp : AuthorizationCodeWebApp
{
// TODO(peleyal): we should also follow the MVC framework Authorize attribute

private readonly Controller controller;
private readonly FlowMetadata flowData;

/// <summary>Gets the controller which is the owner of this authorization code MVC app instance.</summary>
public Controller Controller { get { return controller; } }

/// <summary>Gets the <see cref="Google.Apis.Auth.OAuth2.Mvc.FlowMetadata"/> object.</summary>
public FlowMetadata FlowData { get { return flowData; } }

/// <summary>Constructs a new authorization code MVC app using the given controller and flow data.</summary>
public AuthorizationCodeMvcApp(Controller controller, FlowMetadata flowData)
: base(
flowData.Flow,
new Uri($"{controller.Request.Scheme}://{controller.Request.Host}{flowData.AuthCallback}").ToString(),
controller.Request.GetDisplayUrl())
{
this.controller = controller;
this.flowData = flowData;
}

/// <summary>
/// Asynchronously authorizes the installed application to access user's protected data. It gets the user
/// identifier by calling to <see cref="Google.Apis.Auth.OAuth2.Mvc.FlowMetadata.GetUserId"/> and then calls to
/// <see cref="Google.Apis.Auth.OAuth2.Web.AuthorizationCodeWebApp.AuthorizeAsync"/>.
/// </summary>
/// <param name="taskCancellationToken">Cancellation token to cancel an operation</param>
/// <returns>
/// Auth result object which contains the user's credential or redirect URI for the authorization server
/// </returns>
public Task<AuthResult> AuthorizeAsync(CancellationToken taskCancellationToken)
{
return base.AuthorizeAsync(FlowData.GetUserId(Controller), taskCancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
Copyright 2013 Google Inc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Apis.Auth.OAuth2.Web;
using Google.Apis.Logging;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http.Extensions;
using Google.Apis.Auth.OAuth2.AspMvcCore.Filters;

namespace Google.Apis.Auth.OAuth2.AspMvcCore.Controllers
{
/// <summary>
/// Auth callback to process the authorization code or error response from the authorization redirect page.
/// </summary>
[AuthorizationCodeActionFilter]
public abstract class AuthCallbackController : Controller
{
/// <summary>Logger for this class.</summary>
protected static readonly ILogger Logger = ApplicationContext.Logger.ForType<AuthCallbackController>();

/// <summary>Gets the authorization code flow.</summary>
protected IAuthorizationCodeFlow Flow { get { return FlowData.Flow; } }

/// <summary>
/// Gets the user identifier. Potential logic is to use session variables to retrieve that information.
/// </summary>
protected string UserId { get { return FlowData.GetUserId(this); } }

#region Abstract and Virtual Members

/// <summary>Gets the flow data which contains </summary>
protected abstract FlowMetadata FlowData { get; }

/// <summary>
/// A callback which gets the error when this controller didn't receive an authorization code. The default
/// implementation throws a <see cref="Google.Apis.Auth.OAuth2.Responses.TokenResponseException"/>.
/// </summary>
protected virtual ActionResult OnTokenError(TokenErrorResponse errorResponse)
{
throw new TokenResponseException(errorResponse);
}

/// <summary>
/// The authorization callback which receives an authorization code which contains an error or a code.
/// If a code is available the method exchange the coed with an access token and redirect back to the original
/// page which initialized the auth process (using the state parameter).
/// </summary>
/// <param name="authorizationCode">Authorization code response which contains the code or an error.</param>
/// <param name="taskCancellationToken">Cancellation token to cancel operation.</param>
/// <returns>
/// Redirect action to the state parameter or <see cref="OnTokenError"/> in case of an error.
/// </returns>
public async virtual Task<ActionResult> IndexAsync(AuthorizationCodeResponseUrl authorizationCode,
CancellationToken taskCancellationToken)
{
if (string.IsNullOrEmpty(authorizationCode.Code))
{
var errorResponse = new TokenErrorResponse(authorizationCode);
Logger.Info("Received an error. The response is: {0}", errorResponse);

return OnTokenError(errorResponse);
}

Logger.Debug("Received \"{0}\" code", authorizationCode.Code);

var returnUrl = Request.GetDisplayUrl();
returnUrl = returnUrl.Substring(0, returnUrl.IndexOf("?"));

var token = await Flow.ExchangeCodeForTokenAsync(UserId, authorizationCode.Code, returnUrl,
taskCancellationToken).ConfigureAwait(false);

// Extract the right state.
var oauthState = await AuthWebUtility.ExtracRedirectFromState(Flow.DataStore, UserId,
authorizationCode.State).ConfigureAwait(false);

return new RedirectResult(oauthState);
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
Copyright 2013 Google Inc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using Google.Apis.Auth.OAuth2.Responses;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.WebUtilities;

namespace Google.Apis.Auth.OAuth2.AspMvcCore.Filters
{
/// <summary>
/// An action filter which parses the query parameters into
/// <see cref="Google.Apis.Auth.OAuth2.Responses.AuthorizationCodeResponseUrl"/>.
/// </summary>
public class AuthorizationCodeActionFilter : ActionFilterAttribute, IActionFilter
{
/// <summary>
/// Parses the request into <see cref="Google.Apis.Auth.OAuth2.Responses.AuthorizationCodeResponseUrl"/>.
/// </summary>
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
(actionContext.ActionArguments["authorizationCode"] as AuthorizationCodeResponseUrl)
.ParseRequest(actionContext.HttpContext.Request);

base.OnActionExecuting(actionContext);
}
}

/// <summary>Auth extensions methods.</summary>
public static class AuthExtensions
{
/// <summary>Parses the HTTP request query parameters into the Authorization code response.</summary>
internal static void ParseRequest(this AuthorizationCodeResponseUrl authorizationCode, HttpRequest request)
{
var queryDic = QueryHelpers.ParseQuery(request.QueryString.ToString());
authorizationCode.Code = queryDic["code"];
authorizationCode.State = queryDic["state"];

// if there is no error these are not present in the query string, check first
if (queryDic.ContainsKey("error")) authorizationCode.Error = queryDic["error"];
if (queryDic.ContainsKey("error_description")) authorizationCode.Error = queryDic["error_description"];
if (queryDic.ContainsKey("error_uri")) authorizationCode.Error = queryDic["error_uri"];
}
}
}
48 changes: 48 additions & 0 deletions Src/Support/Google.Apis.Auth.AspMvcCore/OAuth2/Mvc/FlowMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
Copyright 2013 Google Inc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using Google.Apis.Auth.OAuth2.Flows;
using Microsoft.AspNetCore.Mvc;

namespace Google.Apis.Auth.OAuth2.AspMvcCore
{
/// <summary>
/// Flow metadata abstract class which contains the reference to the
/// <see cref="Google.Apis.Auth.OAuth2.Flows.IAuthorizationCodeFlow"/> and
/// method to get the user identifier.
/// </summary>
public abstract class FlowMetadata
{
/// <summary>
/// Gets the user identifier.
/// </summary>
/// <param name="controller">The controller</param>
/// <returns>User identifier</returns>
public abstract string GetUserId(Controller controller);

/// <summary>Gets the authorization code flow.</summary>
public abstract IAuthorizationCodeFlow Flow { get; }

/// <summary>
/// Gets the authorization application's call back. That relative URL will handle the authorization code
/// response.
/// </summary>
public virtual string AuthCallback
{
get { return @"/AuthCallback/IndexAsync"; }
}
}
}
27 changes: 18 additions & 9 deletions Src/Support/GoogleApisClient.sln
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26403.7
VisualStudioVersion = 15.0.26730.16
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Apis.Core", "Google.Apis.Core\Google.Apis.Core.csproj", "{556BEB88-1F3B-4EFA-B912-828C90AC3BEB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Apis.Core", "Google.Apis.Core\Google.Apis.Core.csproj", "{556BEB88-1F3B-4EFA-B912-828C90AC3BEB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Apis", "Google.Apis\Google.Apis.csproj", "{A0E0F5AA-6A5F-44A1-8DC7-0443F01C72E2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Apis", "Google.Apis\Google.Apis.csproj", "{A0E0F5AA-6A5F-44A1-8DC7-0443F01C72E2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Apis.Tests", "Google.Apis.Tests\Google.Apis.Tests.csproj", "{B8D261F0-4C17-4887-BBC3-CBBFDF0D5328}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Apis.Tests", "Google.Apis.Tests\Google.Apis.Tests.csproj", "{B8D261F0-4C17-4887-BBC3-CBBFDF0D5328}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Apis.PlatformServices", "Google.Apis.PlatformServices\Google.Apis.PlatformServices.csproj", "{44D57EC1-3859-483C-8904-4E6D14DE6115}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Apis.PlatformServices", "Google.Apis.PlatformServices\Google.Apis.PlatformServices.csproj", "{44D57EC1-3859-483C-8904-4E6D14DE6115}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Apis.Auth", "Google.Apis.Auth\Google.Apis.Auth.csproj", "{4CBC15CC-B05F-4219-8FB3-8923D552673E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Apis.Auth", "Google.Apis.Auth\Google.Apis.Auth.csproj", "{4CBC15CC-B05F-4219-8FB3-8923D552673E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Apis.Auth.PlatformServices", "Google.Apis.Auth.PlatformServices\Google.Apis.Auth.PlatformServices.csproj", "{11BB1D61-8D13-4443-9B5B-76613302BC0D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Apis.Auth.PlatformServices", "Google.Apis.Auth.PlatformServices\Google.Apis.Auth.PlatformServices.csproj", "{11BB1D61-8D13-4443-9B5B-76613302BC0D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Apis.Auth.Tests", "Google.Apis.Auth.Tests\Google.Apis.Auth.Tests.csproj", "{F106E24E-49F8-4D86-8CBF-9616178CA08C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Apis.Auth.Tests", "Google.Apis.Auth.Tests\Google.Apis.Auth.Tests.csproj", "{F106E24E-49F8-4D86-8CBF-9616178CA08C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "IntegrationTests\IntegrationTests.csproj", "{AB7DA6CF-0100-4941-BD4B-BEE1AA80C6AE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "IntegrationTests\IntegrationTests.csproj", "{AB7DA6CF-0100-4941-BD4B-BEE1AA80C6AE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Apis.Auth.Mvc", "Google.Apis.Auth.Mvc\Google.Apis.Auth.Mvc.csproj", "{22ED8B87-9A8E-4330-8011-318A85F545DD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Apis.Auth.AspMvcCore", "Google.Apis.Auth.AspMvcCore\Google.Apis.Auth.AspMvcCore.csproj", "{CDAF4410-9BF2-43CF-9038-C2EB97D2DDDC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -63,8 +65,15 @@ Global
{22ED8B87-9A8E-4330-8011-318A85F545DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22ED8B87-9A8E-4330-8011-318A85F545DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22ED8B87-9A8E-4330-8011-318A85F545DD}.Release|Any CPU.Build.0 = Release|Any CPU
{CDAF4410-9BF2-43CF-9038-C2EB97D2DDDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CDAF4410-9BF2-43CF-9038-C2EB97D2DDDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CDAF4410-9BF2-43CF-9038-C2EB97D2DDDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CDAF4410-9BF2-43CF-9038-C2EB97D2DDDC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E7FD8CDE-C78E-4254-837E-2FB9BD458BC4}
EndGlobalSection
EndGlobal