Browse Source

Move the userinfo endpoint to a separate controller and update Startup to use app.UseWhen()

pull/322/head
Kévin Chalet 9 years ago
parent
commit
1229e225d6
  1. 29
      samples/Mvc.Client/Controllers/HomeController.cs
  2. 3
      samples/Mvc.Client/Startup.cs
  3. 42
      samples/Mvc.Server/Controllers/AccountController.cs
  4. 58
      samples/Mvc.Server/Controllers/UserinfoController.cs
  5. 40
      samples/Mvc.Server/Extensions/AppBuilderExtensions.cs
  6. 59
      samples/Mvc.Server/Startup.cs

29
samples/Mvc.Client/Controllers/HomeController.cs

@ -6,9 +6,16 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Mvc.Client.Controllers {
public class HomeController : Controller {
private readonly HttpClient _client;
public HomeController(HttpClient client) {
_client = client;
}
[HttpGet("~/")]
public ActionResult Index() {
return View("Home");
@ -16,21 +23,19 @@ namespace Mvc.Client.Controllers {
[Authorize, HttpPost("~/")]
public async Task<ActionResult> Index(CancellationToken cancellationToken) {
using (var client = new HttpClient()) {
var token = await HttpContext.Authentication.GetTokenAsync("access_token");
if (string.IsNullOrEmpty(token)) {
throw new InvalidOperationException("The access token cannot be found in the authentication ticket. " +
"Make sure that SaveTokens is set to true in the OIDC options.");
}
var token = await HttpContext.Authentication.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
if (string.IsNullOrEmpty(token)) {
throw new InvalidOperationException("The access token cannot be found in the authentication ticket. " +
"Make sure that SaveTokens is set to true in the OIDC options.");
}
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:54540/api/message");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:54540/api/message");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
var response = await _client.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
return View("Home", model: await response.Content.ReadAsStringAsync());
}
return View("Home", model: await response.Content.ReadAsStringAsync());
}
}
}

3
samples/Mvc.Client/Startup.cs

@ -1,3 +1,4 @@
using System.Net.Http;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
@ -13,6 +14,8 @@ namespace Mvc.Client {
});
services.AddMvc();
services.AddSingleton<HttpClient>();
}
public void Configure(IApplicationBuilder app) {

42
samples/Mvc.Server/Controllers/AccountController.cs

@ -1,8 +1,6 @@
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OAuth.Validation;
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
@ -10,8 +8,6 @@ using Microsoft.AspNetCore.Mvc.Rendering;
using Mvc.Server.Models;
using Mvc.Server.Services;
using Mvc.Server.ViewModels.Account;
using Newtonsoft.Json.Linq;
using OpenIddict.Core;
namespace Mvc.Server.Controllers {
[Authorize]
@ -36,44 +32,6 @@ namespace Mvc.Server.Controllers {
_applicationDbContext = applicationDbContext;
}
//
// GET: /Account/Userinfo
[Authorize(ActiveAuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)]
[HttpGet, Produces("application/json")]
public async Task<IActionResult> Userinfo() {
var user = await _userManager.GetUserAsync(User);
if (user == null) {
return BadRequest(new OpenIdConnectResponse {
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The user profile is no longer available."
});
}
var claims = new JObject();
// Note: the "sub" claim is a mandatory claim and must be included in the JSON response.
claims[OpenIdConnectConstants.Claims.Subject] = user.Id.ToString();
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Email)) {
claims[OpenIdConnectConstants.Claims.Email] = user.Email;
claims[OpenIdConnectConstants.Claims.EmailVerified] = user.EmailConfirmed;
}
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Phone)) {
claims[OpenIdConnectConstants.Claims.PhoneNumber] = user.PhoneNumber;
claims[OpenIdConnectConstants.Claims.PhoneNumberVerified] = user.PhoneNumberConfirmed;
}
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIddictConstants.Scopes.Roles)) {
claims[OpenIddictConstants.Claims.Roles] = JArray.FromObject(await _userManager.GetRolesAsync(user));
}
// Note: the complete list of standard claims supported by the OpenID Connect specification
// can be found here: http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
return Json(claims);
}
//
// GET: /Account/Login
[HttpGet]

58
samples/Mvc.Server/Controllers/UserinfoController.cs

@ -0,0 +1,58 @@
using System.Threading.Tasks;
using AspNet.Security.OAuth.Validation;
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Mvc.Server.Models;
using Newtonsoft.Json.Linq;
using OpenIddict.Core;
namespace Mvc.Server.Controllers {
[Route("api")]
public class UserinfoController : Controller {
private readonly UserManager<ApplicationUser> _userManager;
public UserinfoController(UserManager<ApplicationUser> userManager) {
_userManager = userManager;
}
//
// GET: /api/userinfo
[Authorize(ActiveAuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)]
[HttpGet("userinfo"), Produces("application/json")]
public async Task<IActionResult> Userinfo() {
var user = await _userManager.GetUserAsync(User);
if (user == null) {
return BadRequest(new OpenIdConnectResponse {
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The user profile is no longer available."
});
}
var claims = new JObject();
// Note: the "sub" claim is a mandatory claim and must be included in the JSON response.
claims[OpenIdConnectConstants.Claims.Subject] = await _userManager.GetUserIdAsync(user);
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Email)) {
claims[OpenIdConnectConstants.Claims.Email] = await _userManager.GetEmailAsync(user);
claims[OpenIdConnectConstants.Claims.EmailVerified] = await _userManager.IsEmailConfirmedAsync(user);
}
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Phone)) {
claims[OpenIdConnectConstants.Claims.PhoneNumber] = await _userManager.GetPhoneNumberAsync(user);
claims[OpenIdConnectConstants.Claims.PhoneNumberVerified] = await _userManager.IsPhoneNumberConfirmedAsync(user);
}
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIddictConstants.Scopes.Roles)) {
claims[OpenIddictConstants.Claims.Roles] = JArray.FromObject(await _userManager.GetRolesAsync(user));
}
// Note: the complete list of standard claims supported by the OpenID Connect specification
// can be found here: http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
return Json(claims);
}
}
}

40
samples/Mvc.Server/Extensions/AppBuilderExtensions.cs

@ -0,0 +1,40 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
namespace Mvc.Server.Extensions {
public static class AppBuilderExtensions {
public static IApplicationBuilder UseWhen(this IApplicationBuilder app,
Func<HttpContext, bool> condition, Action<IApplicationBuilder> configuration) {
if (app == null) {
throw new ArgumentNullException(nameof(app));
}
if (condition == null) {
throw new ArgumentNullException(nameof(condition));
}
if (configuration == null) {
throw new ArgumentNullException(nameof(configuration));
}
var builder = app.New();
configuration(builder);
return app.Use(next => {
builder.Run(next);
var branch = builder.Build();
return context => {
if (condition(context)) {
return branch(context);
}
return next(context);
};
});
}
}
}

59
samples/Mvc.Server/Startup.cs

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Mvc.Server.Extensions;
using Mvc.Server.Models;
using Mvc.Server.Services;
using OpenIddict.Core;
@ -50,7 +51,7 @@ namespace Mvc.Server {
.EnableAuthorizationEndpoint("/connect/authorize")
.EnableLogoutEndpoint("/connect/logout")
.EnableTokenEndpoint("/connect/token")
.EnableUserinfoEndpoint("/Account/Userinfo")
.EnableUserinfoEndpoint("/api/userinfo")
// Note: the Mvc.Client sample only uses the code flow and the password flow, but you
// can enable the other flows if you need to support implicit or client credentials.
@ -101,36 +102,40 @@ namespace Mvc.Server {
app.UseStaticFiles();
// Add a middleware used to validate access
// tokens and protect the API endpoints.
app.UseOAuthValidation();
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), branch => {
// Add a middleware used to validate access
// tokens and protect the API endpoints.
branch.UseOAuthValidation();
// Alternatively, you can also use the introspection middleware.
// Using it is recommended if your resource server is in a
// different application/separated from the authorization server.
//
// app.UseOAuthIntrospection(options => {
// options.AutomaticAuthenticate = true;
// options.AutomaticChallenge = true;
// options.Authority = "http://localhost:54540/";
// options.Audience = "resource_server";
// options.ClientId = "resource_server";
// options.ClientSecret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd";
// });
app.UseIdentity();
app.UseGoogleAuthentication(new GoogleOptions {
ClientId = "560027070069-37ldt4kfuohhu3m495hk2j4pjp92d382.apps.googleusercontent.com",
ClientSecret = "n2Q-GEw9RQjzcRbU3qhfTj8f"
// Alternatively, you can also use the introspection middleware.
// Using it is recommended if your resource server is in a
// different application/separated from the authorization server.
//
// branch.UseOAuthIntrospection(options => {
// options.AutomaticAuthenticate = true;
// options.AutomaticChallenge = true;
// options.Authority = "http://localhost:54540/";
// options.Audiences.Add("resource_server");
// options.ClientId = "resource_server";
// options.ClientSecret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd";
// });
});
app.UseTwitterAuthentication(new TwitterOptions {
ConsumerKey = "6XaCTaLbMqfj6ww3zvZ5g",
ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI"
});
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"), branch => {
branch.UseStatusCodePagesWithReExecute("/error");
app.UseStatusCodePagesWithReExecute("/error");
branch.UseIdentity();
branch.UseGoogleAuthentication(new GoogleOptions {
ClientId = "560027070069-37ldt4kfuohhu3m495hk2j4pjp92d382.apps.googleusercontent.com",
ClientSecret = "n2Q-GEw9RQjzcRbU3qhfTj8f"
});
branch.UseTwitterAuthentication(new TwitterOptions {
ConsumerKey = "6XaCTaLbMqfj6ww3zvZ5g",
ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI"
});
});
app.UseOpenIddict();

Loading…
Cancel
Save