public ActionResult BadgeDescription(int achievementID)
        {
            string achievementTitle, achievementDescription, imageUri;

            using (UnitOfWork work = new UnitOfWork())
            {
                achievement_template template = work.AchievementRepository.GetTemplateById(achievementID);
                if (template == null)
                {
                    return(new HttpStatusCodeResult(HttpStatusCode.NotFound));
                }

                achievementTitle       = template.title;
                achievementDescription = template.description;
                imageUri = JppUriInfo.GetAbsoluteUri(Request, template.icon);
            }

            var badgeDescription = new
            {
                // TODO: truncate data if too large; see https://wiki.mozilla.org/Badges/Onboarding-Issuer#E._Metadata_Spec
                name        = achievementTitle,
                description = achievementDescription,
                image       = imageUri,
                criteria    = JppUriInfo.GetCurrentDomain(Request) + Url.RouteUrl("AchievementsPlayersRoute", new { id = achievementID }),
                issuer      = JppUriInfo.GetCurrentDomain(Request) + Url.RouteUrl("OpenBadgesIssuerRoute"),
                // TODO: tags (optional)
            };

            return(Json(badgeDescription, JsonRequestBehavior.AllowGet));
        }
        public ActionResult Assertion(int userID, int achievementID)
        {
            string   userEmail, achievementImageURL;
            DateTime achievementDate;

            using (UnitOfWork work = new UnitOfWork())
            {
                // Verify user actually has achievement
                if (!work.AchievementRepository.DoesUserHaveAchievement(userID, achievementID))
                {
                    return(new HttpStatusCodeResult(HttpStatusCode.NotFound));
                }

                // If so, get data and generate assertion
                userEmail = work.UserRepository.GetUser(userID).email;

                achievement_template achievementTemplate = work.AchievementRepository.GetTemplateById(achievementID);
                achievementImageURL = JppUriInfo.GetAbsoluteUri(Request, achievementTemplate.icon);

                achievement_instance achievementInstance = work.AchievementRepository.GetUserAchievementInstance(userID, achievementID);
                achievementDate = achievementInstance.achieved_date;
            }
            string salt = "CoeA8DQf"; // As we are exposing the salt anyway, using a constant isn't an issue, and it saves us from having to store every randomly-generated salt in the db
            string hashedEmail;

            hashedEmail = Sha256Helper.HashStringWithSalt(userEmail, salt);

            var badgeAssertion = new
            {
                uid       = GenerateUniqueId(userID, achievementID),
                recipient = new
                {
                    identity = "sha256$" + hashedEmail,
                    type     = "email",
                    hashed   = true,
                    salt     = salt
                },
                image  = achievementImageURL,
                badge  = JppUriInfo.GetCurrentDomain(Request) + Url.RouteUrl("OpenBadgeDescriptionRoute", new { Action = "BadgeDescription", achievementID = achievementID }),
                verify = new
                {
                    type = "hosted",
                    url  = JppUriInfo.GetCurrentDomain(Request) + Url.RouteUrl("OpenBadgeRoute", new { Action = "Assertion", userID = userID, achievementID = achievementID }),
                },
                issuedOn = achievementDate.ToString("s", System.Globalization.CultureInfo.InvariantCulture),
                evidence = JppUriInfo.GetCurrentDomain(Request) + Url.RouteUrl("AchievementsPlayersRoute", new { id = achievementID, playerID = userID }),
            };

            return(Json(badgeAssertion, JsonRequestBehavior.AllowGet));
        }