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

Auth 2.0 Iteration 2 #1113

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d86162b
First cut of Authentication.Abstractions
HaoK Feb 3, 2017
2edb279
Add impl package
HaoK Feb 3, 2017
5b11d51
Cleanup
HaoK Feb 3, 2017
d193389
Cleanup scheme to be data only
HaoK Feb 3, 2017
6f24590
Switch cookies over to new stack
HaoK Feb 9, 2017
bbb055f
Migrate twitter to new stack
HaoK Feb 9, 2017
5a99f5b
Port jwtbearer
HaoK Feb 9, 2017
0732cc4
Port OIDC
HaoK Feb 10, 2017
479085c
Port OAuth
HaoK Feb 10, 2017
f3e9e70
Add facebook
HaoK Feb 10, 2017
a2a25f1
Fix test
HaoK Feb 10, 2017
051a0ea
Add google
HaoK Feb 13, 2017
40b542f
Add feature to preserve old map resolve url behavior
HaoK Feb 13, 2017
7e256bc
PR feedback
HaoK Feb 13, 2017
bf607e1
PR tweaks
HaoK Feb 13, 2017
84f11e8
Switch to ReadOnlyDictionary
HaoK Feb 13, 2017
0e89cea
Cleanup cookies finish response
HaoK Feb 14, 2017
9228486
Add fb test
HaoK Feb 14, 2017
4d7ed14
Add test for fb AppId/Secret options
HaoK Feb 14, 2017
2429651
Fix issue with cookies renew + signout/signin
HaoK Feb 14, 2017
a9fe998
Add microsoft, cleanup test dirs
HaoK Feb 14, 2017
11564e2
Reenable two skipped tests
HaoK Feb 15, 2017
b940578
Remove using
HaoK Feb 15, 2017
d92d923
Support dynamic add/remove of schemes
HaoK Feb 22, 2017
2aafea9
Update samples to work with new stack
HaoK Feb 24, 2017
9a8d8ef
Update cookie interop tests
HaoK Feb 24, 2017
42ca776
Fix cookiepolicy test
HaoK Feb 24, 2017
8b8c549
Opt in for schemes to handle requests
HaoK Feb 24, 2017
06b32c6
Opt out of request handling for JwtBearer
HaoK Feb 24, 2017
b02da9e
HandleRequest back to bool
HaoK Feb 27, 2017
7700e08
Schemes must register callbackPaths
HaoK Feb 27, 2017
96c2c8b
Add GetAllSchemes
HaoK Feb 27, 2017
a5d6d5a
Rename Skip => Stop/None
HaoK Mar 6, 2017
d80b0d3
Cleanup
HaoK Mar 6, 2017
caf7ac7
Bring back Skip
HaoK Mar 6, 2017
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
134 changes: 133 additions & 1 deletion Security.sln

Large diffs are not rendered by default.

10 changes: 4 additions & 6 deletions samples/CookieSample/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Linq;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
Expand All @@ -13,24 +14,21 @@ public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
services.AddCookieAuthentication();
}

public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
{
loggerfactory.AddConsole(LogLevel.Information);

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true
});
app.UseAuthentication();

app.Run(async context =>
{
if (!context.User.Identities.Any(identity => identity.IsAuthenticated))
{
var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "bob") }, CookieAuthenticationDefaults.AuthenticationScheme));
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user);
await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user);

context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Hello First timer");
Expand Down
11 changes: 4 additions & 7 deletions samples/CookieSessionSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
Expand All @@ -14,18 +15,14 @@ public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
services.AddCookieAuthentication(o => o.SessionStore = new MemoryCacheTicketStore());
}

public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
{
loggerfactory.AddConsole(LogLevel.Information);

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
SessionStore = new MemoryCacheTicketStore()
});
app.UseAuthentication();

app.Run(async context =>
{
Expand All @@ -39,7 +36,7 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
claims.Add(new Claim(ClaimTypes.Role, "SomeRandomGroup" + i, ClaimValueTypes.String, "IssuedByBob", "OriginalIssuerJoe"));
}

await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
Copy link
Member

Choose a reason for hiding this comment

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

So AuthenticationManager is completely obsolete and you're rooting all of the extensions on HttpContext?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah we should deprecate the Authentication versions as part of this

new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme)));

context.Response.ContentType = "text/plain";
Expand Down
54 changes: 27 additions & 27 deletions samples/JwtBearerSample/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
Expand Down Expand Up @@ -42,7 +43,28 @@ public Startup(IHostingEnvironment env)
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
services.AddJwtBearerAuthentication(o =>
{
// You also need to update /wwwroot/app/scripts/app.js
o.Authority = Configuration["jwt:authority"];
o.Audience = Configuration["jwt:audience"];
o.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = c =>
{
c.HandleResponse();

c.Response.StatusCode = 500;
c.Response.ContentType = "text/plain";
if (Environment.IsDevelopment())
{
// Debug only, in production do not share exceptions with the remote host.
return c.Response.WriteAsync(c.Exception.ToString());
}
return c.Response.WriteAsync("An error occurred processing your authentication.");
}
};
});
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -69,44 +91,22 @@ public void Configure(IApplicationBuilder app)
app.UseDefaultFiles();
app.UseStaticFiles();

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
// You also need to update /wwwroot/app/scripts/app.js
Authority = Configuration["jwt:authority"],
Audience = Configuration["jwt:audience"],
Events = new JwtBearerEvents()
{
OnAuthenticationFailed = c =>
{
c.HandleResponse();

c.Response.StatusCode = 500;
c.Response.ContentType = "text/plain";
if (Environment.IsDevelopment())
{
// Debug only, in production do not share exceptions with the remote host.
return c.Response.WriteAsync(c.Exception.ToString());
}
return c.Response.WriteAsync("An error occurred processing your authentication.");
}
}
});
app.UseAuthentication();

// [Authorize] would usually handle this
app.Use(async (context, next) =>
{
// Use this if options.AutomaticAuthenticate = false
// Use this if there are multiple authentication schemes
// var user = await context.Authentication.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);

var user = context.User; // We can do this because of options.AutomaticAuthenticate = true;
var user = context.User; // We can do this because of there's only a single authentication scheme
if (user?.Identity?.IsAuthenticated ?? false)
{
await next();
}
else
{
// We can do this because of options.AutomaticChallenge = true;
await context.Authentication.ChallengeAsync();
await context.ChallengeAsync(JwtBearerDefaults.AuthenticationScheme);
Copy link
Member

Choose a reason for hiding this comment

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

No more generic Challenge? It should work if there's only one or its the default.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can preserve it, it would line up with naked Authorize with no DefaultPolicy then.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well if we add an overload for Challenge that omits scheme, we should probably do the same for authenticate and SignIn, basically null scheme will mean use default (since the overloads are implemented as extension methods)

}
});

Expand Down
12 changes: 5 additions & 7 deletions samples/OpenIdConnect.AzureAdSample/AuthPropertiesTokenCache.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

namespace OpenIdConnect.AzureAdSample
Expand Down Expand Up @@ -58,10 +57,9 @@ private void BeforeAccessNotificationWithProperties(TokenCacheNotificationArgs a
private void BeforeAccessNotificationWithContext(TokenCacheNotificationArgs args)
{
// Retrieve the auth session with the cached tokens
var authenticateContext = new AuthenticateContext(_signInScheme);
_httpContext.Authentication.AuthenticateAsync(authenticateContext).Wait();
_authProperties = new AuthenticationProperties(authenticateContext.Properties);
_principal = authenticateContext.Principal;
var result = _httpContext.AuthenticateAsync(_signInScheme).Result;
_authProperties = result.Ticket.Properties;
_principal = result.Ticket.Principal;

BeforeAccessNotificationWithProperties(args);
}
Expand All @@ -87,7 +85,7 @@ private void AfterAccessNotificationWithContext(TokenCacheNotificationArgs args)
var cachedTokens = Serialize();
var cachedTokensText = Convert.ToBase64String(cachedTokens);
_authProperties.Items[TokenCacheKey] = cachedTokensText;
_httpContext.Authentication.SignInAsync(_signInScheme, _principal, _authProperties).Wait();
_httpContext.SignInAsync(_signInScheme, _principal, _authProperties).Wait();
}
}

Expand Down
85 changes: 44 additions & 41 deletions samples/OpenIdConnect.AzureAdSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -37,10 +37,42 @@ public Startup(IHostingEnvironment env)

public IConfiguration Configuration { get; set; }

private string ClientId => Configuration["oidc:clientid"];
private string ClientSecret => Configuration["oidc:clientsecret"];
private string Authority => Configuration["oidc:authority"];
private string Resource => "https://graph.windows.net";

public void ConfigureServices(IServiceCollection services)
{
services.AddCookieAuthentication();

services.AddOpenIdConnectAuthentication(o =>
{
o.ClientId = ClientId;
o.ClientSecret = ClientSecret; // for code flow
o.Authority = Authority;
o.ResponseType = OpenIdConnectResponseType.CodeIdToken;
o.PostLogoutRedirectUri = "/signed-out";
// GetClaimsFromUserInfoEndpoint = true,
o.Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeReceived = async context =>
{
var request = context.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(ClientId, ClientSecret);
var authContext = new AuthenticationContext(Authority, AuthPropertiesTokenCache.ForCodeRedemption(context.Properties));

var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
context.ProtocolMessage.Code, new Uri(currentUri), credential, Resource);

context.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
};
});

services.AddAuthentication(sharedOptions =>
sharedOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);
sharedOptions.DefaultAuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme);
}

public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
Expand Down Expand Up @@ -69,36 +101,7 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
}
});

app.UseCookieAuthentication(new CookieAuthenticationOptions());

var clientId = Configuration["oidc:clientid"];
var clientSecret = Configuration["oidc:clientsecret"];
var authority = Configuration["oidc:authority"];
var resource = "https://graph.windows.net";
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = clientId,
ClientSecret = clientSecret, // for code flow
Authority = authority,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
PostLogoutRedirectUri = "/signed-out",
// GetClaimsFromUserInfoEndpoint = true,
Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeReceived = async context =>
{
var request = context.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(clientId, clientSecret);
var authContext = new AuthenticationContext(authority, AuthPropertiesTokenCache.ForCodeRedemption(context.Properties));

var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
context.ProtocolMessage.Code, new Uri(currentUri), credential, resource);

context.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
}
});
app.UseAuthentication();

app.Run(async context =>
{
Expand All @@ -111,13 +114,13 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
return;
}

await context.Authentication.ChallengeAsync(
await context.ChallengeAsync(
OpenIdConnectDefaults.AuthenticationScheme,
new AuthenticationProperties { RedirectUri = "/" });
}
else if (context.Request.Path.Equals("/signout"))
{
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await WriteHtmlAsync(context.Response,
async response =>
{
Expand All @@ -127,8 +130,8 @@ await WriteHtmlAsync(context.Response,
}
else if (context.Request.Path.Equals("/signout-remote"))
{
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await context.Authentication.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await context.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
}
else if (context.Request.Path.Equals("/signed-out"))
{
Expand All @@ -141,7 +144,7 @@ await WriteHtmlAsync(context.Response,
}
else if (context.Request.Path.Equals("/remote-signedout"))
{
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await WriteHtmlAsync(context.Response,
async response =>
{
Expand All @@ -153,7 +156,7 @@ await WriteHtmlAsync(context.Response,
{
if (!context.User.Identities.Any(identity => identity.IsAuthenticated))
{
await context.Authentication.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" });
await context.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" });
return;
}

Expand All @@ -170,10 +173,10 @@ await WriteHtmlAsync(context.Response, async response =>
try
{
// Use ADAL to get the right token
var authContext = new AuthenticationContext(authority, AuthPropertiesTokenCache.ForApiCalls(context, CookieAuthenticationDefaults.AuthenticationScheme));
var credential = new ClientCredential(clientId, clientSecret);
var authContext = new AuthenticationContext(Authority, AuthPropertiesTokenCache.ForApiCalls(context, CookieAuthenticationDefaults.AuthenticationScheme));
var credential = new ClientCredential(ClientId, ClientSecret);
string userObjectID = context.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var result = await authContext.AcquireTokenSilentAsync(resource, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
var result = await authContext.AcquireTokenSilentAsync(Resource, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

await response.WriteAsync($"<h3>access_token</h3><code>{HtmlEncode(result.AccessToken)}</code><br>");
}
Expand Down
Loading