For a website I'm building in ASP.NET MVC 5/Web API 2 I wanted to simply support Mozilla Persona as the one and only login system for the site. After finding the right blog posts (which was harder than I expected), particularly Brock Allen's primer, I found OWIN and ASP.NET Identity made it easier than I expected. I'm posting this in the hopes that it might help the next person attempting this.

using Microsoft.AspNet.Identity;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web.Http;

namespace MyApp.Controllers
{
    [RoutePrefix("api/Persona")]
    public class PersonaController : ApiController
    {
        // From: http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
        public const string PersonaAuthenticationType = DefaultAuthenticationTypes.ApplicationCookie;

        [HttpPost]
        [Route("login")]
        public async Task<IHttpActionResult> Login([FromBody]JObject assertion)
        {
            var http = new HttpClient() { BaseAddress = new Uri(Properties.Settings.Default.PersonaVerificationBaseUrl), };
            var body = await JsonConvert.SerializeObjectAsync(new
            {
                assertion = (string)assertion["assertion"],
                audience = Properties.Settings.Default.PersonaAudienceUrl,
            });
            var result = await http.PostAsync("verify", new StringContent(body, System.Text.Encoding.UTF8, "application/json"));

            dynamic response = JObject.Parse(await result.Content.ReadAsStringAsync());

            var status = (string)response.status;
            if (result.IsSuccessStatusCode && string.Equals(status, "okay", StringComparison.OrdinalIgnoreCase))
            {
                var claims = new Claim[]
                {
                    new Claim(ClaimTypes.Name, (string)response.email),
                    new Claim(ClaimTypes.Email, (string)response.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 identity = new ClaimsIdentity(claims, PersonaAuthenticationType);
                var ctx = Request.GetOwinContext();
                ctx.Authentication.SignIn(identity);
            }
            else
            {
                result.StatusCode = HttpStatusCode.BadRequest;
            }

            return ResponseMessage(result);
        }

        [HttpPost]
        [Route("logout")]
        public IHttpActionResult Logout()
        {
            var ctx = Request.GetOwinContext();
            ctx.Authentication.SignOut(PersonaAuthenticationType);
            return Ok();
        }
    }
}