Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Additions for OpenIdConnectMiddleware and OAuthBearer Beta1. #112

Closed
wants to merge 12 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NuGet.Config
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
<packageSources>
<add key="AspNetVNext" value="https://www.myget.org/F/aspnetvnext/api/v2" />
<add key="NuGet.org" value="https://nuget.org/api/v2/" />
<add key="AzureADNighty" value="http://www.myget.org/F/azureadwebstacknightly"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if having /api/v2 is required or not necessary. Or may be for consistency with other feeds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that, but am hoping I can get a core50, 50 and 4.5x version together.

</packageSources>
</configuration>
47 changes: 46 additions & 1 deletion Security.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22013.1
VisualStudioVersion = 14.0.22422.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4D2B6A51-2F9F-44F5-8131-EA5CAC053652}"
EndProject
Expand Down Expand Up @@ -36,6 +36,12 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Security.O
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CookieSessionSample", "samples\CookieSessionSample\CookieSessionSample.kproj", "{19711880-46DA-4A26-9E0F-9B2E41D27651}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIdConnectSample", "samples\OpenIdConnectSample\OpenIdConnectSample.kproj", "{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Security.OAuthBearer", "src\Microsoft.AspNet.Security.OAuthBearer\Microsoft.AspNet.Security.OAuthBearer.kproj", "{2755BFE5-7421-4A31-A644-F817DF5CAA98}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Security.OpenIdConnect", "src\Microsoft.AspNet.Security.OpenIdConnect\Microsoft.AspNet.Security.OpenIdConnect.kproj", "{674D128E-83BB-481A-A9D9-6D47872E1FC8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -156,6 +162,42 @@ Global
{19711880-46DA-4A26-9E0F-9B2E41D27651}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{19711880-46DA-4A26-9E0F-9B2E41D27651}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{19711880-46DA-4A26-9E0F-9B2E41D27651}.Release|x86.ActiveCfg = Release|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Debug|x86.ActiveCfg = Debug|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Debug|x86.Build.0 = Debug|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Release|Any CPU.Build.0 = Release|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Release|x86.ActiveCfg = Release|Any CPU
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B}.Release|x86.Build.0 = Release|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Debug|x86.ActiveCfg = Debug|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Debug|x86.Build.0 = Debug|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Release|Any CPU.Build.0 = Release|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Release|x86.ActiveCfg = Release|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Release|x86.Build.0 = Release|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Debug|x86.ActiveCfg = Debug|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Debug|x86.Build.0 = Debug|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Release|Any CPU.Build.0 = Release|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Release|x86.ActiveCfg = Release|Any CPU
{674D128E-83BB-481A-A9D9-6D47872E1FC8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -172,5 +214,8 @@ Global
{1FCF26C2-A3C7-4308-B698-4AFC3560BC0C} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{4A636011-68EE-4CE5-836D-EA8E13CF71E4} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{19711880-46DA-4A26-9E0F-9B2E41D27651} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
{BEF0F5C3-EF4E-4649-9C49-D5E279A3CA2B} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
{2755BFE5-7421-4A31-A644-F817DF5CAA98} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{674D128E-83BB-481A-A9D9-6D47872E1FC8} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
EndGlobalSection
EndGlobal
30 changes: 30 additions & 0 deletions samples/OpenIDConnectSample/OpenIdConnectSample.kproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>bef0f5c3-ef4e-4649-9c49-d5e279a3ca2b</ProjectGuid>
<RootNamespace>OpenIDConnectSample</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AssemblyName>OpenIDConnectSample</AssemblyName>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>42023</DevelopmentServerPort>
<CommandLineArguments />
<DebugTarget>
</DebugTarget>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
<ProjectExtensions>
<VisualStudio>
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
</VisualStudio>
</ProjectExtensions>
</Project>
56 changes: 56 additions & 0 deletions samples/OpenIDConnectSample/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Security.OpenIdConnect;
using Microsoft.AspNet.Http.Security;
using Microsoft.AspNet.Security;

namespace OpenIdConnectSample
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseServices(services =>
{
services.AddDataProtection();
services.Configure<ExternalAuthenticationOptions>(options =>
{
options.SignInAsAuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CookieAuthenticationDefaults.AuthenticationType?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting pinpoint about a simplier, the idea is that it just works if you pass in the authority pinpoint, we reach out for validation coordinates, keys, issues, etc and produce a ClaimsPrincipal. I am pushing for a consistent model for validation. I made a small change where the notification can set the AuthenticationTicket and return. Giving you complete freedom to take it all over.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brentschmaltz @Tratcher all these changes are great and we definitely need them (the OIDC configuration discovery is just amazing). But we also need a basic token validation middleware that behaves exactly like the existing OAuth2 bearer middleware and allows full pluggability without relying on complicated and OIDC-specific things. Here's my suggestion:

  • Rename the existing OAuth2 bearer middleware to Microsoft.AspNet.Security.Tokens.TokenAuthenticationMiddleware: there's definitely nothing OAuth2/OIDC-specific with OAuthBearerAuthenticationMiddleware in its current form (almost identical to Katana 3) and this led to a terrible confusion. In Average Joe's mind, it should be clear that this middleware can be used with tokens issued by non-OAuth2 authorization servers: for instance, you must be able to plug in a ISecureDataFormat that relies on the data protection stuff or that communicates with a remote server to validate an opaque token that cannot be verified locally. That's also the place where I'd create the ISecureDataFormat -> SecurityTokenHandler adapter.
  • Move your new OIDC-stuff to a new package and a new middleware subclassing TokenAuthenticationMiddleware... what about Microsoft.AspNet.Security.Tokens.OpenIdConnect?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To add some clarify, the code written in the tests under: OAuthBearerMiddlewareTests don't use metadata. Inside any three of the notifications (message received, token received, token validated) you can take over.

});

});

app.UseCookieAuthentication(options =>
{
options.AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These auth types are all setup wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that? What are you suggesting they be?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CookieAuthenticationDefaults.AuthenticationType?

Or you can simply omit setting the property.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not blocking, let's think about that later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the sample is working as expected?

If it is, that's really sad: IMO, having multiple authentication handlers configured with the same AuthenticationType is really bad and should trigger an exception somewhere in Microsoft.AspNet.Security as it can't be a valid scenario (here, the AuthenticationType is shared with the OIDC middleware).

IIRC, that was not the case with Katana 3, but an InvalidOperationException thrown by LINQ was thrown when you called AuthenticateAsync with an AuthenticationType shared my multiple handlers.

If @Tratcher agrees with the general concept, I suggest opening a new ticket to track that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We added some validation in the new stack for things like challenging for a missing auth type, but there's not currently any checks for duplicates. Duplicates are not supported and will cause strange side-effects with APIs like SignIn and Challenge.

The correct config is:
ExternalAuthenticationOptions - Use CookieAuthenticationDefaults.AuthenticationType
UseCookieAuthentication - Don't set anything, let it use the default
UseOpenIdConnectAuthentication - Don't set SignInAsAuthenticationType or AuthenticationType, let it use the defaults

});

app.UseOpenIdConnectAuthentication(options =>
{
options.ClientId = "fe78e0b4-6fe7-47e6-812c-fb75cee266a4";
options.Authority = "https://login.windows.net/cyrano.onmicrosoft.com";
options.RedirectUri = "http://localhost:42023";
options.SignInAsAuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CookieAuthenticationDefaults.AuthenticationType?

Or just remove that, given that you already set the cookies middleware as the default authentication middleware via ExternalAuthenticationOptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not entirely sure what to do here. One goal I have is to remove the need to explicitly set this Auth type by adding a property to the AuthenticationTicket. It always seemed add to me that the runtime requires the ClaimsIdentity to have a specific authtype for things to work. I am thinking of ways to re-think this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid redesigning auth types in this PR, it's way out of scope. If we're going to tackle that issue we need to do it as its own work item across the whole stack.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I backed out some of the changes we will need moving forward. So we can get these in, we will deal with the other issues later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's time to have a check in all the providers that use a SignInAsAuthenticationType property to make sure that it can't be that same as the AuthenticationType: an authentication handler shouldn't call itself to delegate the persistence of the authentication ticket. This kind of cyclic call is definitely not a valid scenario.

@Tratcher your thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am actually trying to get rid of SignInAsAuthenticationType. Wouldn't the existence of an authentication ticket be sufficient?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh? Why would you remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conceptually this drives the ClaimsIdentity AuthenticationType, that shouldn't be linked.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really: SignInAsAuthenticationType is the way an authentication handler delegates to another one the persistence of the resulting identity.

An example should be clearer: when the Google middleware creates its own identity using the profile endpoint, it calls the authentication handler corresponding to SignInAsAuthenticationType - a cookie handler in most cases - and offers it the opportunity to store the identity using its own AuthenticationType.

options.AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also omit it.

});

app.Run(async context =>
{
if (context.User == null || !context.User.Identity.IsAuthenticated)
{
context.Response.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);

context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Hello First timer");
return;
}

context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Hello Authenticated User");
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Delete empty lines.


}
}
}
18 changes: 18 additions & 0 deletions samples/OpenIDConnectSample/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"dependencies": {
"Kestrel": "1.0.0-*",
"Microsoft.AspNet.Security.Cookies": "1.0.0-*",
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
"Microsoft.AspNet.Security.OpenIdConnect": "1.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*"
},
"frameworks": {
"aspnet50": { },
"aspnetcore50": { }
},
"commands": {
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:12345",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: All of our other samples use 5001 as port for weblistener. Yeah but does not matter though.

"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5004"
},
"webroot": "wwwroot"
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Globalization;
using System.Net.Http;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Security.DataHandler;
using Microsoft.AspNet.Security.DataProtection;
using Microsoft.AspNet.Security.Infrastructure;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel;
using System;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System goes first.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure found the magic option.

using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net.Http;

namespace Microsoft.AspNet.Security.OAuth
{
Expand Down Expand Up @@ -45,18 +45,22 @@ public OAuthAuthenticationMiddleware(
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "AuthenticationType"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nameof?

}

if (string.IsNullOrWhiteSpace(Options.ClientId))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "ClientId"));
}

if (string.IsNullOrWhiteSpace(Options.ClientSecret))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "ClientSecret"));
}

if (string.IsNullOrWhiteSpace(Options.AuthorizationEndpoint))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "AuthorizationEndpoint"));
}

if (string.IsNullOrWhiteSpace(Options.TokenEndpoint))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "TokenEndpoint"));
Expand Down
Loading