In part one I created a Persona Controller, which provided OWIN Cookie Authentication (as a Web API controller), for supporting MVC logins. Going further down OWIN authentication rabbit hole, I needed to figure out how to get the OWIN bearer token support working, as the new templates default to using that to secure Web API 2 controllers themselves. Turns out this was easier than I expected (most of the previous code copy and pasted over), but felt a lot more complicated in terms of finding useful example documentation. (Not that part one was a cake walk in that department either...)
Here is the ApplicationOAuthProvider class that replaces the bulk of the PersonaController:
using System;
using System.Threading.Tasks;
using Microsoft.Owin.Security.OAuth;
using System.Net.Http;
using System.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Security.Claims;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security;
using System.Collections.Generic;
namespace StingerCheck.Providers
{
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public async override Task GrantCustomExtension(OAuthGrantCustomExtensionContext context)
{
if (context.GrantType.Equals("persona", StringComparison.OrdinalIgnoreCase))
{
var http = new HttpClient() { BaseAddress = new Uri(ConfigurationManager.AppSettings["PersonaVerificationBaseUrl"]), };
var body = await JsonConvert.SerializeObjectAsync(new
{
assertion = (string)context.Parameters["assertion"],
audience = ConfigurationManager.AppSettings["PersonaAudienceUrl"],
});
var result = await http.PostAsync("verify", new StringContent(body, System.Text.Encoding.UTF8, "application/json"));
var response = JObject.Parse(await result.Content.ReadAsStringAsync());
var status = (string)response["status"];
if (result.IsSuccessStatusCode && string.Equals(status, "okay", StringComparison.OrdinalIgnoreCase))
{
var email = (string)response["email"];
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, email),
new Claim(ClaimTypes.Email, email),
new Claim("persona-expires", (string)response["expires"]),
new Claim("persona-audience", (string)response["audience"]),
new Claim("persona-issuer", (string)response["issuer"]),
new Claim(ClaimTypes.AuthenticationMethod, "Persona"),
};
var oauthIdentity = new ClaimsIdentity(claims, context.Options.AuthenticationType);
var ticket = new AuthenticationTicket(oauthIdentity, new AuthenticationProperties(new Dictionary<string, string> {
{"email", email},
}));
context.Validated(ticket);
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationType);
context.OwinContext.Authentication.SignIn(identity);
return;
}
context.SetError("invalid_grant", (string)response.SelectToken("reason"));
return;
}
await base.GrantCustomExtension(context);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
else if (context.ClientId == "web")
{
var expectedUri = new Uri(context.Request.Uri, "/");
context.Validated(expectedUri.AbsoluteUri);
}
}
return Task.FromResult<object>(null);
}
}
}